Add Scrollable widget support, implement disclaimer screen, rearrangements of main menu ui code

This commit is contained in:
Imbris 2020-06-05 02:21:23 -04:00
parent 01eb061a83
commit e2bf974ecb
13 changed files with 450 additions and 131 deletions

View File

@ -255,7 +255,7 @@ impl PlayState for MainMenuState {
},
MainMenuEvent::Settings => {}, // TODO
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
/*MainMenuEvent::DisclaimerClosed => {
/*MainMenuEvent::DisclaimerAccepted => {
global_state.settings.show_disclaimer = false
},*/
MainMenuEvent::AuthServerTrust(auth_server, trust) => {

View File

@ -3,12 +3,10 @@ use crate::{
i18n::Localization,
ui::{
fonts::IcedFonts as Fonts,
ice::{component::neat_button, style, widget::image, Element},
ice::{component::neat_button, style, Element},
},
};
use iced::{
button, Align, Color, Column, Container, HorizontalAlignment, Length, Row, Space, Text,
};
use iced::{button, Align, Color, Column, Container, Length, Row, Space, Text, VerticalAlignment};
/// Connecting screen for the main menu
pub struct Screen {
@ -16,10 +14,6 @@ pub struct Screen {
add_button: button::State,
}
// TODO: move to super and unify with identical login consts
const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
impl Screen {
pub fn new() -> Self {
Self {
@ -32,32 +26,22 @@ impl Screen {
&mut self,
fonts: &Fonts,
imgs: &Imgs,
bg_img: image::Handle,
connection_state: &ConnectionState,
version: &str,
time: f32,
i18n: &Localization,
button_style: style::button::Style,
) -> Element<Message> {
let fade_msg = (time * 2.0).sin() * 0.5 + 0.51;
let button_style = style::button::Style::new(imgs.button)
.hover_image(imgs.button_hover)
.press_image(imgs.button_press)
.text_color(TEXT_COLOR)
.disabled_text_color(DISABLED_TEXT_COLOR);
let version = Text::new(version)
.size(fonts.cyri.scale(15)) // move version text size to const
.width(Length::Fill)
.height(if matches!(connection_state, ConnectionState::InProgress {..}){Length::Fill}else{Length::Shrink})
.horizontal_alignment(HorizontalAlignment::Right);
let (middle, bottom) = match connection_state {
let children = match connection_state {
ConnectionState::InProgress { status } => {
let status = Text::new(status)
.size(fonts.alkhemi.scale(80))
.font(fonts.alkhemi.id)
.color(Color::from_rgba(1.0, 1.0, 1.0, fade_msg))
.width(Length::Fill);
.vertical_alignment(VerticalAlignment::Bottom)
.width(Length::Fill)
.height(Length::Fill);
let status = Row::with_children(vec![
Space::new(Length::Units(80), Length::Shrink).into(),
@ -78,7 +62,7 @@ impl Screen {
.center_x()
.padding(3);
(status.into(), cancel.into())
vec![status.into(), cancel.into()]
},
ConnectionState::AuthTrustPrompt { msg, .. } => {
let text = Text::new(msg).size(fonts.cyri.scale(25));
@ -125,22 +109,16 @@ impl Screen {
.center_x()
.center_y();
(
vec![
container.into(),
Space::new(Length::Fill, Length::Units(fonts.cyri.scale(15))).into(),
)
]
},
};
let content = Column::with_children(vec![version.into(), middle, bottom])
Column::with_children(children)
.width(Length::Fill)
.height(Length::Fill)
.padding(3);
// Note: could replace this with styling on iced's container since we aren't
// using fixed aspect ratio
Container::new(content)
.style(style::container::Style::image(bg_img))
.into()
}
}

View File

@ -0,0 +1,78 @@
use super::{IcedImgs as Imgs, Message};
use crate::{
i18n::Localization,
ui::{
fonts::IcedFonts as Fonts,
ice::{component::neat_button, style, Element},
},
};
use iced::{button, scrollable, Column, Container, Length, Scrollable, Space};
use vek::Rgba;
/// Connecting screen for the main menu
pub struct Screen {
accept_button: button::State,
scroll: scrollable::State,
}
impl Screen {
pub fn new() -> Self {
Self {
accept_button: Default::default(),
scroll: Default::default(),
}
}
pub(super) fn view(
&mut self,
fonts: &Fonts,
imgs: &Imgs,
i18n: &Localization,
button_style: style::button::Style,
) -> Element<Message> {
Container::new(
Container::new(
Column::with_children(vec![
iced::Text::new(i18n.get("common.disclaimer"))
.font(fonts.alkhemi.id)
.size(fonts.alkhemi.scale(35))
.into(),
Space::new(Length::Fill, Length::Units(20)).into(),
Scrollable::new(&mut self.scroll)
.push(
iced::Text::new(i18n.get("main.notice"))
.font(fonts.cyri.id)
.size(fonts.cyri.scale(23)),
)
.height(Length::FillPortion(1))
.into(),
Container::new(
Container::new(neat_button(
&mut self.accept_button,
i18n.get("common.accept"),
0.7,
button_style,
Some(Message::AcceptDisclaimer),
))
.height(Length::Units(fonts.cyri.scale(50))),
)
.center_x()
.height(Length::Shrink)
.width(Length::Fill)
.into(),
])
.spacing(5)
.padding(20)
.width(Length::Fill)
.height(Length::Fill),
)
.style(style::container::Style::Color(Rgba::new(22, 19, 17, 255))),
)
.center_x()
.center_y()
.padding(70)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}

View File

@ -1,4 +1,4 @@
use super::{IcedImgs as Imgs, Info, LoginInfo, Message};
use super::{IcedImgs as Imgs, LoginInfo, Message};
use crate::{
i18n::Localization,
ui::{
@ -14,14 +14,11 @@ use crate::{
},
},
};
use iced::{
button, text_input, Align, Column, Container, HorizontalAlignment, Length, Row, Space, Text,
TextInput,
};
use iced::{button, text_input, Align, Column, Container, Length, Row, Space, Text, TextInput};
use vek::*;
const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
const FILL_FRAC_ONE: f32 = 0.77;
const FILL_FRAC_TWO: f32 = 0.53;
const INPUT_WIDTH: u16 = 250;
@ -52,18 +49,11 @@ impl Screen {
fonts: &Fonts,
imgs: &Imgs,
login_info: &LoginInfo,
info: &Info,
error: Option<&str>,
version: &str,
show_servers: bool,
i18n: &Localization,
button_style: style::button::Style,
) -> Element<Message> {
let button_style = style::button::Style::new(imgs.button)
.hover_image(imgs.button_hover)
.press_image(imgs.button_press)
.text_color(TEXT_COLOR)
.disabled_text_color(DISABLED_TEXT_COLOR);
let buttons = Column::with_children(vec![
neat_button(
&mut self.servers_button,
@ -96,32 +86,28 @@ impl Screen {
.height(Length::Fill)
.align_y(Align::End);
let left_column = if matches!(info, Info::Intro) {
let intro_text = i18n.get("main.login_process");
let intro_text = i18n.get("main.login_process");
let info_window = BackgroundContainer::new(
CompoundGraphic::from_graphics(vec![
Graphic::rect(Rgba::new(0, 0, 0, 240), [500, 300], [0, 0]),
// Note: a way to tell it to keep the height of this one piece constant and
// unstreched would be nice, I suppose we could just break this out into a
// column and use Length::Units
Graphic::image(imgs.banner_bottom, [500, 30], [0, 300])
.color(Rgba::new(255, 255, 255, 240)),
])
.height(Length::Shrink),
Text::new(intro_text).size(fonts.cyri.scale(21)),
)
.max_width(450)
.padding(Padding::new().horizontal(20).top(10).bottom(60));
let info_window = BackgroundContainer::new(
CompoundGraphic::from_graphics(vec![
Graphic::rect(Rgba::new(0, 0, 0, 240), [500, 300], [0, 0]),
// Note: a way to tell it to keep the height of this one piece constant and
// unstreched would be nice, I suppose we could just break this out into a
// column and use Length::Units
Graphic::image(imgs.banner_bottom, [500, 30], [0, 300])
.color(Rgba::new(255, 255, 255, 240)),
])
.height(Length::Shrink),
Text::new(intro_text).size(fonts.cyri.scale(21)),
)
.max_width(450)
.padding(Padding::new().horizontal(20).top(10).bottom(60));
Column::with_children(vec![info_window.into(), buttons.into()])
.width(Length::Fill)
.height(Length::Fill)
.padding(27)
.into()
} else {
buttons.into()
};
let left_column = Column::with_children(vec![info_window.into(), buttons.into()])
.width(Length::Fill)
.height(Length::Fill)
.padding(27)
.into();
let banner = self
.banner
@ -133,12 +119,9 @@ impl Screen {
.align_x(Align::Center)
.align_y(Align::Center);
let right_column = Text::new(version)
.size(fonts.cyri.scale(15))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Right);
let right_column = Space::new(Length::Fill, Length::Fill);
let content = Row::with_children(vec![
Row::with_children(vec![
left_column,
central_column.into(),
right_column.into(),
@ -146,11 +129,7 @@ impl Screen {
.width(Length::Fill)
.height(Length::Fill)
.spacing(10)
.padding(3);
Container::new(content)
.style(style::container::Style::image(imgs.bg))
.into()
.into()
}
}

View File

@ -1,4 +1,5 @@
mod connecting;
//mod disclaimer;
mod login;
use crate::{
@ -34,7 +35,8 @@ const COL1: Color = Color::Rgba(0.07, 0.1, 0.1, 0.9);
/*const UI_MAIN: Color = Color::Rgba(0.61, 0.70, 0.70, 1.0); // Greenish Blue
const UI_HIGHLIGHT_0: Color = Color::Rgba(0.79, 1.09, 1.09, 1.0);*/
use iced::text_input;
use iced::{text_input, Column, Container, HorizontalAlignment, Length};
use ui::ice::style;
image_ids_ice! {
struct IcedImgs {
<VoxelGraphic>
@ -94,6 +96,7 @@ pub enum Event {
Quit,
Settings,
//DisclaimerClosed, TODO: remove all traces?
//DisclaimerAccepted,
AuthServerTrust(String, bool),
}
@ -114,11 +117,6 @@ pub struct LoginInfo {
pub server: String,
}
enum Info {
//Disclaimer,
Intro,
}
enum ConnectionState {
InProgress {
status: String,
@ -140,6 +138,9 @@ impl ConnectionState {
}
enum Screen {
/*Disclaimer {
screen: disclaimer::Screen,
},*/
Login {
screen: login::Screen,
// Error to display in a box
@ -162,7 +163,6 @@ struct IcedState {
login_info: LoginInfo,
show_servers: bool,
info: Info,
time: f32,
screen: Screen,
@ -183,7 +183,8 @@ enum Message {
TrustPromptAdd,
TrustPromptCancel,
CloseError,
//CloseDisclaimer,
/*CloseDisclaimer,
*AcceptDisclaimer, */
}
impl IcedState {
@ -200,10 +201,15 @@ impl IcedState {
common::util::GIT_VERSION.to_string()
);
let info = Info::Intro; // if settings.show_disclaimer {
//Info::Disclaimer
//} else {
//Info::Intro
let screen = /* if settings.show_disclaimer {
Screen::Disclaimer {
screen: disclaimer::Screen::new(),
}
} else { */
Screen::Login {
screen: login::Screen::new(),
error: None,
};
//};
Self {
@ -220,30 +226,47 @@ impl IcedState {
},
show_servers: false,
info,
time: 0.0,
screen: Screen::Login {
screen: login::Screen::new(),
error: None,
},
screen,
}
}
fn view(&mut self, dt: f32) -> Element<Message> {
self.time = self.time + dt;
// TODO: make disclaimer it's own screen?
match &mut self.screen {
// TODO: consider setting this as the default in the renderer
let button_style = style::button::Style::new(self.imgs.button)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.text_color(login::TEXT_COLOR)
.disabled_text_color(login::DISABLED_TEXT_COLOR);
let version = iced::Text::new(&self.version)
.size(self.fonts.cyri.scale(15))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Right);
let bg_img = if matches!(&self.screen, Screen::Connecting {..}) {
self.bg_img
} else {
self.imgs.bg
};
// TODO: make any large text blocks scrollable so that if the area is to
// small they can still be read
let content = match &mut self.screen {
/*Screen::Disclaimer { screen } => {
screen.view(&self.fonts, &self.imgs, &self.i18n, button_style)
},*/
Screen::Login { screen, error } => screen.view(
&self.fonts,
&self.imgs,
&self.login_info,
&self.info,
error.as_deref(),
&self.version,
self.show_servers,
&self.i18n,
button_style,
),
Screen::Connecting {
screen,
@ -251,13 +274,22 @@ impl IcedState {
} => screen.view(
&self.fonts,
&self.imgs,
self.bg_img,
&connection_state,
&self.version,
self.time,
&self.i18n,
button_style,
),
}
};
Container::new(
Column::with_children(vec![version.into(), content])
.spacing(3)
.width(Length::Fill)
.height(Length::Fill),
)
.style(style::container::Style::image(bg_img))
.padding(3)
.into()
}
fn update(&mut self, message: Message, events: &mut Vec<Event>) {
@ -330,6 +362,15 @@ impl IcedState {
//Message::CloseDisclaimer => {
// events.push(Event::DisclaimerClosed);
//},
/*Message::AcceptDisclaimer => {
if let Screen::Disclaimer { .. } = &self.screen {
events.push(Event::DisclaimerAccepted);
self.screen = Screen::Login {
screen: login::Screen::new(),
error: None,
};
}
},*/
}
}
@ -816,7 +857,7 @@ impl<'a> MainMenuUi {
.was_clicked()
{
self.show_disclaimer = false;
events.push(Event::DisclaimerClosed);
events.push(Event::DisclaimerAccepted);
}
} else {*/
// TODO: Don't use macros for this?

View File

@ -63,6 +63,8 @@ impl IcedUi {
use iced::window;
match event {
// Intercept resizing events
// TODO: examine if we are handling dpi properly here
// ideally these values should be the logical ones
Event::Window(window::Event::Resized { width, height }) => {
self.window_resized = Some(Vec2::new(width, height));
},

View File

@ -86,7 +86,7 @@ pub struct IcedRenderer {
// Per-frame/update
current_state: State,
mesh: Mesh<UiPipeline>,
glyphs: Vec<(usize, usize, Rgba<f32>)>,
glyphs: Vec<(usize, usize, Rgba<f32>, Vec2<u32>)>,
// Output from glyph_brush in the previous frame
// It can sometimes ask you to redraw with these instead (idk if that is done with
// pre-positioned glyphs)
@ -164,7 +164,7 @@ impl IcedRenderer {
//self.current_scissor = default_scissor(renderer);
self.draw_primitive(primitive, renderer);
self.draw_primitive(primitive, Vec2::zero(), renderer);
// Enter the final command.
self.draw_commands.push(match self.current_state {
@ -240,19 +240,29 @@ impl IcedRenderer {
let glyphs = &self.glyphs;
let mesh = &mut self.mesh;
let p_scale = self.p_scale;
let half_res = self.half_res;
glyphs
.iter()
.flat_map(|(mesh_index, glyph_count, linear_color)| {
.flat_map(|(mesh_index, glyph_count, linear_color, offset)| {
let mesh_index = *mesh_index;
let linear_color = *linear_color;
(0..*glyph_count).map(move |i| (mesh_index + i * 6, linear_color))
// Could potentially pass this in as part of the extras
let offset = offset.map(|e| e as f32 * p_scale) / half_res;
(0..*glyph_count).map(move |i| (mesh_index + i * 6, linear_color, offset))
})
.zip(self.last_glyph_verts.iter())
.for_each(|((mesh_index, linear_color), (uv, rect))| {
.for_each(|((mesh_index, linear_color, offset), (uv, rect))| {
// TODO: add function to vek for this
let rect = Aabr {
min: rect.min + offset,
max: rect.max + offset,
};
mesh.replace_quad(
mesh_index,
create_ui_quad(*rect, *uv, linear_color, UiMode::Text),
create_ui_quad(rect, *uv, linear_color, UiMode::Text),
)
});
},
@ -344,12 +354,12 @@ impl IcedRenderer {
Aabr { min, max }
}
fn draw_primitive(&mut self, primitive: Primitive, renderer: &mut Renderer) {
fn draw_primitive(&mut self, primitive: Primitive, offset: Vec2<u32>, renderer: &mut Renderer) {
match primitive {
Primitive::Group { primitives } => {
primitives
.into_iter()
.for_each(|p| self.draw_primitive(p, renderer));
.for_each(|p| self.draw_primitive(p, offset, renderer));
},
Primitive::Image {
handle,
@ -363,7 +373,11 @@ impl IcedRenderer {
}
let (graphic_id, rotation) = handle;
let gl_aabr = self.gl_aabr(bounds);
let gl_aabr = self.gl_aabr(iced::Rectangle {
x: bounds.x + offset.x as f32,
y: bounds.y + offset.y as f32,
..bounds
});
let graphic_cache = self.cache.graphic_cache_mut();
@ -435,8 +449,14 @@ impl IcedRenderer {
self.switch_state(State::Plain);
let gl_aabr = self.gl_aabr(iced::Rectangle {
x: bounds.x + offset.x as f32,
y: bounds.y + offset.y as f32,
..bounds
});
self.mesh.push_quad(create_ui_quad(
self.gl_aabr(bounds),
gl_aabr,
Aabr {
min: Vec2::zero(),
max: Vec2::zero(),
@ -470,7 +490,7 @@ impl IcedRenderer {
// Since we already passed in `bounds` to position the glyphs some of this
// seems redundant...
// Note: we can't actually use this because dropping glyphs messeses up the
// counting and there is not a convenient method provided to drop out of bounds
// counting and there is not a method provided to drop out of bounds
// glyphs while positioning them
glyph_brush::ab_glyph::Rect {
min: glyph_brush::ab_glyph::point(
@ -489,8 +509,12 @@ impl IcedRenderer {
min: Vec2::broadcast(0.0),
max: Vec2::broadcast(0.0),
};
self.glyphs
.push((self.mesh.vertices().len(), glyph_count, linear_color));
self.glyphs.push((
self.mesh.vertices().len(),
glyph_count,
linear_color,
offset,
));
for _ in 0..glyph_count {
// Push placeholder quad
// Note: moving to some sort of layering / z based system would be an
@ -504,8 +528,13 @@ impl IcedRenderer {
));
}
},
Primitive::Clip { bounds, content } => {
Primitive::Clip {
bounds,
offset: clip_offset,
content,
} => {
let new_scissor = {
// TODO: incorporate current offset for nested Clips
let intersection = Aabr {
min: Vec2 {
x: (bounds.x * self.p_scale) as u16,
@ -545,7 +574,7 @@ impl IcedRenderer {
// TODO: cull primitives outside the current scissor
// Renderer child
self.draw_primitive(*content, renderer);
self.draw_primitive(*content, offset + clip_offset, renderer);
// Reset scissor
self.draw_commands.push(match self.current_state {

View File

@ -25,6 +25,7 @@ pub enum Primitive {
},
Clip {
bounds: iced::Rectangle,
offset: vek::Vec2<u32>,
content: Box<Primitive>,
},
Nothing,

View File

@ -1,2 +1,3 @@
pub mod button;
pub mod container;
pub mod scrollable;

View File

@ -0,0 +1,33 @@
use super::super::super::widget::image;
use vek::Rgba;
#[derive(Clone, Copy)]
pub struct Style {
pub track: Option<Track>,
pub scroller: Scroller,
}
impl Default for Style {
fn default() -> Self {
Self {
track: None,
scroller: Scroller::Color(Rgba::new(128, 128, 128, 255)),
}
}
}
#[derive(Clone, Copy)]
pub enum Track {
Color(Rgba<u8>),
Image(image::Handle, Rgba<u8>),
}
#[derive(Clone, Copy)]
pub enum Scroller {
Color(Rgba<u8>),
Image {
ends: image::Handle,
mid: image::Handle,
color: Rgba<u8>,
},
}

View File

@ -6,6 +6,7 @@ mod compound_graphic;
mod container;
mod image;
mod row;
mod scrollable;
mod space;
mod text;
mod text_input;

View File

@ -0,0 +1,178 @@
use super::super::{super::Rotation, style, IcedRenderer, Primitive};
use common::util::srgba_to_linear;
use iced::{mouse, scrollable, Rectangle};
use style::scrollable::{Scroller, Track};
const SCROLLBAR_WIDTH: u16 = 10;
const SCROLLBAR_MIN_HEIGHT: u16 = 6;
const SCROLLBAR_MARGIN: u16 = 2;
impl scrollable::Renderer for IcedRenderer {
type Style = style::scrollable::Style;
// Interesting that this is here
// I guess we can take advantage of this to keep a constant size despite
// scaling?
fn scrollbar(
&self,
bounds: Rectangle,
content_bounds: Rectangle,
offset: u32,
) -> Option<scrollable::Scrollbar> {
// TODO: might actually want to divide by p_scale here (same in text&ext_input)
// (or just not use it) (or at least only account for dpi but not any
// additional scaling)
let width = (SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN) as f32 * self.p_scale;
if content_bounds.height > bounds.height {
let scrollbar_bounds = Rectangle {
x: bounds.x + bounds.width - width,
width,
..bounds
};
let visible_fraction = bounds.height / content_bounds.height;
let scrollbar_height = (bounds.height * visible_fraction)
.max((2 * SCROLLBAR_MIN_HEIGHT) as f32 * self.p_scale);
let y_offset = offset as f32 * visible_fraction;
let scroller_bounds = Rectangle {
x: scrollbar_bounds.x + SCROLLBAR_MARGIN as f32 * self.p_scale,
// TODO: check this behavior
y: scrollbar_bounds.y + y_offset,
width: scrollbar_bounds.width - (2 * SCROLLBAR_MARGIN) as f32 * self.p_scale,
height: scrollbar_height,
};
Some(scrollable::Scrollbar {
bounds: scrollbar_bounds,
scroller: scrollable::Scroller {
bounds: scroller_bounds,
},
})
} else {
None
}
}
fn draw(
&mut self,
state: &scrollable::State,
bounds: Rectangle,
_content_bounds: Rectangle,
is_mouse_over: bool,
is_mouse_over_scrollbar: bool,
scrollbar: Option<scrollable::Scrollbar>,
offset: u32,
style_sheet: &Self::Style,
(content, mouse_interaction): Self::Output,
) -> Self::Output {
(
if let Some(scrollbar) = scrollbar {
let mut primitives = Vec::with_capacity(5);
// Scrolled content
primitives.push(Primitive::Clip {
bounds,
offset: (0, offset).into(),
content: Box::new(content),
});
let style = style_sheet;
//let style = if state.is_scroller_grabbed() {
// style_sheet.dragging()
//} else if is_mouse_over_scrollbar {
// style_sheet.hovered()
//} else {
// style_sheet.active();
//};
let is_scrollbar_visible = style.track.is_some();
if is_mouse_over || state.is_scroller_grabbed() || is_scrollbar_visible {
let bounds = scrollbar.scroller.bounds;
match style.scroller {
Scroller::Color(color) => primitives.push(Primitive::Rectangle {
bounds,
linear_color: srgba_to_linear(color.map(|e| e as f32 / 255.0)),
}),
Scroller::Image { ends, mid, color } => {
// Calculate sizes of ends pieces based on image aspect ratio
let (img_w, img_h) = self.image_dims(ends);
let end_height = bounds.width * img_h as f32 / img_w as f32;
// Calcutate size of middle piece based on available space
// Note: Might want to scale into real pixels for parts of this
let (end_height, middle_height) =
if end_height * 2.0 + 1.0 <= bounds.height {
(end_height, bounds.height - end_height * 2.0)
} else {
// Take 1 logical pixel for the middle height
let remaining_height = bounds.height - 1.0;
(remaining_height / 2.0, 1.0)
};
// Top
primitives.push(Primitive::Image {
handle: (ends, Rotation::None),
bounds: Rectangle {
height: end_height,
..bounds
},
color,
});
// Middle
primitives.push(Primitive::Image {
handle: (mid, Rotation::None),
bounds: Rectangle {
y: bounds.y + end_height,
height: middle_height,
..bounds
},
color,
});
// Bottom
primitives.push(Primitive::Image {
handle: (ends, Rotation::Cw180),
bounds: Rectangle {
y: bounds.y + end_height + middle_height,
height: end_height,
..bounds
},
color,
});
},
}
}
if let Some(track) = style.track {
let bounds = Rectangle {
x: scrollbar.bounds.x + SCROLLBAR_MARGIN as f32 * self.p_scale,
width: scrollbar.bounds.width
- (2 * SCROLLBAR_MARGIN) as f32 * self.p_scale,
..scrollbar.bounds
};
primitives.push(match track {
Track::Color(color) => Primitive::Rectangle {
bounds,
linear_color: srgba_to_linear(color.map(|e| e as f32 / 255.0)),
},
Track::Image(handle, color) => Primitive::Image {
handle: (handle, Rotation::None),
bounds,
color,
},
});
}
Primitive::Group { primitives }
} else {
content
},
if is_mouse_over_scrollbar || state.is_scroller_grabbed() {
mouse::Interaction::Idle
} else {
mouse_interaction
},
)
}
}

View File

@ -169,7 +169,7 @@ impl text_input::Renderer for IcedRenderer {
(
Primitive::Rectangle {
bounds: Rectangle {
x: text_bounds.x + position - offset - CURSOR_WIDTH / p_scale / 2.0,
x: text_bounds.x + position - CURSOR_WIDTH / p_scale / 2.0,
y: text_bounds.y,
width: CURSOR_WIDTH / p_scale,
height: text_bounds.height,
@ -196,7 +196,7 @@ impl text_input::Renderer for IcedRenderer {
(
Primitive::Rectangle {
bounds: Rectangle {
x: text_bounds.x + left_position - left_offset,
x: text_bounds.x + left_position,
y: text_bounds.y,
width,
height: text_bounds.height,
@ -216,10 +216,9 @@ impl text_input::Renderer for IcedRenderer {
let display_text = text.unwrap_or(if state.is_focused() { "" } else { placeholder });
let section = glyph_brush::Section {
screen_position: (
text_bounds.x * p_scale - scroll_offset,
text_bounds.center_y() * p_scale,
),
// Note: clip offset is an integer so we don't have to worry about not accounting for
// that here where the alignment of the glyphs with pixels affects rasterization
screen_position: (text_bounds.x * p_scale, text_bounds.center_y() * p_scale),
bounds: (
10000.0, /* text_bounds.width * p_scale */
text_bounds.height * p_scale,
@ -279,9 +278,8 @@ impl text_input::Renderer for IcedRenderer {
let primitive = if text_width > text_bounds.width {
Primitive::Clip {
bounds: text_bounds,
offset: (scroll_offset as u32, 0).into(),
content: Box::new(primitive),
/* Note: iced_wgpu uses offset here but we can't do that since we pass the text
* to the glyph_brush here */
}
} else {
primitive