Merge branch 'master' of https://github.com/hecrj/iced into wgpu_outdatedframe
This commit is contained in:
commit
e822f654e4
219 changed files with 6266 additions and 1761 deletions
|
|
@ -82,7 +82,12 @@ impl<'a> Layer<'a> {
|
|||
|
||||
let mut layers = vec![first_layer];
|
||||
|
||||
Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive);
|
||||
Self::process_primitive(
|
||||
&mut layers,
|
||||
Vector::new(0.0, 0.0),
|
||||
primitive,
|
||||
0,
|
||||
);
|
||||
|
||||
layers
|
||||
}
|
||||
|
|
@ -91,13 +96,19 @@ impl<'a> Layer<'a> {
|
|||
layers: &mut Vec<Self>,
|
||||
translation: Vector,
|
||||
primitive: &'a Primitive,
|
||||
current_layer: usize,
|
||||
) {
|
||||
match primitive {
|
||||
Primitive::None => {}
|
||||
Primitive::Group { primitives } => {
|
||||
// TODO: Inspect a bit and regroup (?)
|
||||
for primitive in primitives {
|
||||
Self::process_primitive(layers, translation, primitive)
|
||||
Self::process_primitive(
|
||||
layers,
|
||||
translation,
|
||||
primitive,
|
||||
current_layer,
|
||||
)
|
||||
}
|
||||
}
|
||||
Primitive::Text {
|
||||
|
|
@ -109,7 +120,7 @@ impl<'a> Layer<'a> {
|
|||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
} => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.text.push(Text {
|
||||
content,
|
||||
|
|
@ -128,7 +139,7 @@ impl<'a> Layer<'a> {
|
|||
border_width,
|
||||
border_color,
|
||||
} => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
// TODO: Move some of these computations to the GPU (?)
|
||||
layer.quads.push(Quad {
|
||||
|
|
@ -146,7 +157,7 @@ impl<'a> Layer<'a> {
|
|||
});
|
||||
}
|
||||
Primitive::Mesh2D { buffers, size } => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(translation.x, translation.y),
|
||||
|
|
@ -167,7 +178,7 @@ impl<'a> Layer<'a> {
|
|||
offset,
|
||||
content,
|
||||
} => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
let translated_bounds = *bounds + translation;
|
||||
|
||||
// Only draw visible content
|
||||
|
|
@ -175,16 +186,15 @@ impl<'a> Layer<'a> {
|
|||
layer.bounds.intersection(&translated_bounds)
|
||||
{
|
||||
let clip_layer = Layer::new(clip_bounds);
|
||||
let new_layer = Layer::new(layer.bounds);
|
||||
|
||||
layers.push(clip_layer);
|
||||
|
||||
Self::process_primitive(
|
||||
layers,
|
||||
translation
|
||||
- Vector::new(offset.x as f32, offset.y as f32),
|
||||
content,
|
||||
layers.len() - 1,
|
||||
);
|
||||
layers.push(new_layer);
|
||||
}
|
||||
}
|
||||
Primitive::Translate {
|
||||
|
|
@ -195,13 +205,19 @@ impl<'a> Layer<'a> {
|
|||
layers,
|
||||
translation + *new_translation,
|
||||
&content,
|
||||
current_layer,
|
||||
);
|
||||
}
|
||||
Primitive::Cached { cache } => {
|
||||
Self::process_primitive(layers, translation, &cache);
|
||||
Self::process_primitive(
|
||||
layers,
|
||||
translation,
|
||||
&cache,
|
||||
current_layer,
|
||||
);
|
||||
}
|
||||
Primitive::Image { handle, bounds } => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.images.push(Image::Raster {
|
||||
handle: handle.clone(),
|
||||
|
|
@ -209,7 +225,7 @@ impl<'a> Layer<'a> {
|
|||
});
|
||||
}
|
||||
Primitive::Svg { handle, bounds } => {
|
||||
let layer = layers.last_mut().unwrap();
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.images.push(Image::Vector {
|
||||
handle: handle.clone(),
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::{
|
||||
mouse, overlay, Color, Font, HorizontalAlignment, Point, Rectangle,
|
||||
VerticalAlignment,
|
||||
mouse, overlay, Color, Font, HorizontalAlignment, Padding, Point,
|
||||
Rectangle, VerticalAlignment,
|
||||
};
|
||||
|
||||
pub use iced_style::menu::Style;
|
||||
|
|
@ -45,7 +45,7 @@ where
|
|||
viewport: &Rectangle,
|
||||
options: &[T],
|
||||
hovered_option: Option<usize>,
|
||||
padding: u16,
|
||||
padding: Padding,
|
||||
text_size: u16,
|
||||
font: Font,
|
||||
style: &Style,
|
||||
|
|
@ -53,7 +53,7 @@ where
|
|||
use std::f32;
|
||||
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
let option_height = text_size as usize + padding as usize * 2;
|
||||
let option_height = (text_size + padding.vertical()) as usize;
|
||||
|
||||
let mut primitives = Vec::new();
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ where
|
|||
x: bounds.x,
|
||||
y: bounds.y + (option_height * i) as f32,
|
||||
width: bounds.width,
|
||||
height: f32::from(text_size + padding * 2),
|
||||
height: f32::from(text_size + padding.vertical()),
|
||||
};
|
||||
|
||||
if is_selected {
|
||||
|
|
@ -88,7 +88,7 @@ where
|
|||
primitives.push(Primitive::Text {
|
||||
content: option.to_string(),
|
||||
bounds: Rectangle {
|
||||
x: bounds.x + f32::from(padding),
|
||||
x: bounds.x + padding.left as f32,
|
||||
y: bounds.center_y(),
|
||||
width: f32::INFINITY,
|
||||
..bounds
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Viewport {
|
|||
|
||||
/// Returns the physical width of the [`Viewport`].
|
||||
pub fn physical_width(&self) -> u32 {
|
||||
self.physical_size.height
|
||||
self.physical_size.width
|
||||
}
|
||||
|
||||
/// Returns the physical height of the [`Viewport`].
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ pub mod scrollable;
|
|||
pub mod slider;
|
||||
pub mod svg;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
pub mod tooltip;
|
||||
|
||||
mod column;
|
||||
mod row;
|
||||
|
|
@ -48,6 +50,10 @@ pub use scrollable::Scrollable;
|
|||
pub use slider::Slider;
|
||||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
#[doc(no_inline)]
|
||||
pub use tooltip::Tooltip;
|
||||
|
||||
pub use column::Column;
|
||||
pub use image::Image;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::defaults::{self, Defaults};
|
|||
use crate::{Backend, Primitive, Renderer};
|
||||
use iced_native::mouse;
|
||||
use iced_native::{
|
||||
Background, Color, Element, Layout, Point, Rectangle, Vector,
|
||||
Background, Color, Element, Layout, Padding, Point, Rectangle, Vector,
|
||||
};
|
||||
|
||||
pub use iced_native::button::State;
|
||||
|
|
@ -21,7 +21,7 @@ impl<B> iced_native::button::Renderer for Renderer<B>
|
|||
where
|
||||
B: Backend,
|
||||
{
|
||||
const DEFAULT_PADDING: u16 = 5;
|
||||
const DEFAULT_PADDING: Padding = Padding::new(5);
|
||||
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
|
|
|
|||
|
|
@ -154,9 +154,9 @@ where
|
|||
event: iced_native::Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
messages: &mut Vec<Message>,
|
||||
_renderer: &Renderer<B>,
|
||||
_clipboard: Option<&dyn Clipboard>,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
messages: &mut Vec<Message>,
|
||||
) -> event::Status {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl Frame {
|
|||
self.size.width
|
||||
}
|
||||
|
||||
/// Returns the width of the [`Frame`].
|
||||
/// Returns the height of the [`Frame`].
|
||||
#[inline]
|
||||
pub fn height(&self) -> f32 {
|
||||
self.size.height
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub trait Program<Message> {
|
|||
/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
|
||||
/// [`Cache`].
|
||||
///
|
||||
/// [`Frame`]: crate::widget::canvas::Cache
|
||||
/// [`Frame`]: crate::widget::canvas::Frame
|
||||
/// [`Cache`]: crate::widget::canvas::Cache
|
||||
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
//! Display images in your user interface.
|
||||
pub mod viewer;
|
||||
|
||||
use crate::backend::{self, Backend};
|
||||
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::image;
|
||||
use iced_native::mouse;
|
||||
use iced_native::Layout;
|
||||
|
||||
pub use iced_native::image::{Handle, Image};
|
||||
pub use iced_native::image::{Handle, Image, Viewer};
|
||||
|
||||
impl<B> image::Renderer for Renderer<B>
|
||||
where
|
||||
|
|
|
|||
55
graphics/src/widget/image/viewer.rs
Normal file
55
graphics/src/widget/image/viewer.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//! Zoom and pan on an image.
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
|
||||
use iced_native::image;
|
||||
use iced_native::image::viewer;
|
||||
use iced_native::mouse;
|
||||
use iced_native::{Rectangle, Size, Vector};
|
||||
|
||||
impl<B> viewer::Renderer for Renderer<B>
|
||||
where
|
||||
B: Backend + backend::Image,
|
||||
{
|
||||
fn draw(
|
||||
&mut self,
|
||||
state: &viewer::State,
|
||||
bounds: Rectangle,
|
||||
image_size: Size,
|
||||
translation: Vector,
|
||||
handle: image::Handle,
|
||||
is_mouse_over: bool,
|
||||
) -> Self::Output {
|
||||
(
|
||||
{
|
||||
Primitive::Clip {
|
||||
bounds,
|
||||
content: Box::new(Primitive::Translate {
|
||||
translation,
|
||||
content: Box::new(Primitive::Image {
|
||||
handle,
|
||||
bounds: Rectangle {
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
..Rectangle::with_size(image_size)
|
||||
},
|
||||
}),
|
||||
}),
|
||||
offset: Vector::new(0, 0),
|
||||
}
|
||||
},
|
||||
{
|
||||
if state.is_cursor_grabbed() {
|
||||
mouse::Interaction::Grabbing
|
||||
} else if is_mouse_over
|
||||
&& (image_size.width > bounds.width
|
||||
|| image_size.height > bounds.height)
|
||||
{
|
||||
mouse::Interaction::Grab
|
||||
} else {
|
||||
mouse::Interaction::Idle
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,23 +6,21 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
use crate::backend::{self, Backend};
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
use crate::defaults;
|
||||
use crate::{Primitive, Renderer};
|
||||
use crate::{Backend, Color, Primitive, Renderer};
|
||||
use iced_native::container;
|
||||
use iced_native::mouse;
|
||||
use iced_native::pane_grid;
|
||||
use iced_native::text;
|
||||
use iced_native::{
|
||||
Element, HorizontalAlignment, Layout, Point, Rectangle, Vector,
|
||||
VerticalAlignment,
|
||||
};
|
||||
use iced_native::{Element, Layout, Point, Rectangle, Vector};
|
||||
|
||||
pub use iced_native::pane_grid::{
|
||||
Axis, Configuration, Content, Direction, DragEvent, Pane, ResizeEvent,
|
||||
Split, State, TitleBar,
|
||||
Axis, Configuration, Content, Direction, DragEvent, Node, Pane,
|
||||
ResizeEvent, Split, State, TitleBar,
|
||||
};
|
||||
|
||||
pub use iced_style::pane_grid::{Line, StyleSheet};
|
||||
|
||||
/// A collection of panes distributed using either vertical or horizontal splits
|
||||
/// to completely fill the space available.
|
||||
///
|
||||
|
|
@ -34,16 +32,20 @@ pub type PaneGrid<'a, Message, Backend> =
|
|||
|
||||
impl<B> pane_grid::Renderer for Renderer<B>
|
||||
where
|
||||
B: Backend + backend::Text,
|
||||
B: Backend,
|
||||
{
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
fn draw<Message>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
content: &[(Pane, Content<'_, Message, Self>)],
|
||||
dragging: Option<(Pane, Point)>,
|
||||
resizing: Option<Axis>,
|
||||
resizing: Option<(Axis, Rectangle, bool)>,
|
||||
layout: Layout<'_>,
|
||||
style_sheet: &<Self as pane_grid::Renderer>::Style,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) -> Self::Output {
|
||||
let pane_cursor_position = if dragging.is_some() {
|
||||
// TODO: Remove once cursor availability is encoded in the type
|
||||
|
|
@ -61,8 +63,13 @@ where
|
|||
.zip(layout.children())
|
||||
.enumerate()
|
||||
.map(|(i, ((id, pane), layout))| {
|
||||
let (primitive, new_mouse_interaction) =
|
||||
pane.draw(self, defaults, layout, pane_cursor_position);
|
||||
let (primitive, new_mouse_interaction) = pane.draw(
|
||||
self,
|
||||
defaults,
|
||||
layout,
|
||||
pane_cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
if new_mouse_interaction > mouse_interaction {
|
||||
mouse_interaction = new_mouse_interaction;
|
||||
|
|
@ -78,7 +85,8 @@ where
|
|||
})
|
||||
.collect();
|
||||
|
||||
let primitives = if let Some((index, layout, origin)) = dragged_pane {
|
||||
let mut primitives = if let Some((index, layout, origin)) = dragged_pane
|
||||
{
|
||||
let pane = panes.remove(index);
|
||||
let bounds = layout.bounds();
|
||||
|
||||
|
|
@ -108,15 +116,62 @@ where
|
|||
panes
|
||||
};
|
||||
|
||||
let (primitives, mouse_interaction) =
|
||||
if let Some((axis, split_region, is_picked)) = resizing {
|
||||
let highlight = if is_picked {
|
||||
style_sheet.picked_split()
|
||||
} else {
|
||||
style_sheet.hovered_split()
|
||||
};
|
||||
|
||||
if let Some(highlight) = highlight {
|
||||
primitives.push(Primitive::Quad {
|
||||
bounds: match axis {
|
||||
Axis::Horizontal => Rectangle {
|
||||
x: split_region.x,
|
||||
y: (split_region.y
|
||||
+ (split_region.height - highlight.width)
|
||||
/ 2.0)
|
||||
.round(),
|
||||
width: split_region.width,
|
||||
height: highlight.width,
|
||||
},
|
||||
Axis::Vertical => Rectangle {
|
||||
x: (split_region.x
|
||||
+ (split_region.width - highlight.width)
|
||||
/ 2.0)
|
||||
.round(),
|
||||
y: split_region.y,
|
||||
width: highlight.width,
|
||||
height: split_region.height,
|
||||
},
|
||||
},
|
||||
background: highlight.color.into(),
|
||||
border_radius: 0.0,
|
||||
border_width: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
});
|
||||
}
|
||||
|
||||
(
|
||||
primitives,
|
||||
match axis {
|
||||
Axis::Horizontal => {
|
||||
mouse::Interaction::ResizingVertically
|
||||
}
|
||||
Axis::Vertical => {
|
||||
mouse::Interaction::ResizingHorizontally
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(primitives, mouse_interaction)
|
||||
};
|
||||
|
||||
(
|
||||
Primitive::Group { primitives },
|
||||
if dragging.is_some() {
|
||||
mouse::Interaction::Grabbing
|
||||
} else if let Some(axis) = resizing {
|
||||
match axis {
|
||||
Axis::Horizontal => mouse::Interaction::ResizingVertically,
|
||||
Axis::Vertical => mouse::Interaction::ResizingHorizontally,
|
||||
}
|
||||
} else {
|
||||
mouse_interaction
|
||||
},
|
||||
|
|
@ -127,16 +182,17 @@ where
|
|||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
bounds: Rectangle,
|
||||
style_sheet: &Self::Style,
|
||||
style_sheet: &<Self as container::Renderer>::Style,
|
||||
title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>,
|
||||
body: (&Element<'_, Message, Self>, Layout<'_>),
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) -> Self::Output {
|
||||
let style = style_sheet.style();
|
||||
let (body, body_layout) = body;
|
||||
|
||||
let (body_primitive, body_interaction) =
|
||||
body.draw(self, defaults, body_layout, cursor_position, &bounds);
|
||||
body.draw(self, defaults, body_layout, cursor_position, viewport);
|
||||
|
||||
let background = crate::widget::container::background(bounds, &style);
|
||||
|
||||
|
|
@ -150,6 +206,7 @@ where
|
|||
defaults,
|
||||
title_bar_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
show_controls,
|
||||
);
|
||||
|
||||
|
|
@ -161,10 +218,10 @@ where
|
|||
body_primitive,
|
||||
],
|
||||
},
|
||||
if is_over_pick_area {
|
||||
mouse::Interaction::Grab
|
||||
} else if title_bar_interaction > body_interaction {
|
||||
if title_bar_interaction > body_interaction {
|
||||
title_bar_interaction
|
||||
} else if is_over_pick_area {
|
||||
mouse::Interaction::Grab
|
||||
} else {
|
||||
body_interaction
|
||||
},
|
||||
|
|
@ -187,15 +244,14 @@ where
|
|||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
bounds: Rectangle,
|
||||
style_sheet: &Self::Style,
|
||||
title: &str,
|
||||
title_size: u16,
|
||||
title_font: Self::Font,
|
||||
title_bounds: Rectangle,
|
||||
style_sheet: &<Self as container::Renderer>::Style,
|
||||
content: (&Element<'_, Message, Self>, Layout<'_>),
|
||||
controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) -> Self::Output {
|
||||
let style = style_sheet.style();
|
||||
let (title_content, title_layout) = content;
|
||||
|
||||
let defaults = Self::Defaults {
|
||||
text: defaults::Text {
|
||||
|
|
@ -205,16 +261,12 @@ where
|
|||
|
||||
let background = crate::widget::container::background(bounds, &style);
|
||||
|
||||
let (title_primitive, _) = text::Renderer::draw(
|
||||
let (title_primitive, title_interaction) = title_content.draw(
|
||||
self,
|
||||
&defaults,
|
||||
title_bounds,
|
||||
title,
|
||||
title_size,
|
||||
title_font,
|
||||
None,
|
||||
HorizontalAlignment::Left,
|
||||
VerticalAlignment::Top,
|
||||
title_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
if let Some((controls, controls_layout)) = controls {
|
||||
|
|
@ -223,7 +275,7 @@ where
|
|||
&defaults,
|
||||
controls_layout,
|
||||
cursor_position,
|
||||
&bounds,
|
||||
viewport,
|
||||
);
|
||||
|
||||
(
|
||||
|
|
@ -234,7 +286,7 @@ where
|
|||
controls_primitive,
|
||||
],
|
||||
},
|
||||
controls_interaction,
|
||||
controls_interaction.max(title_interaction),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
|
|
@ -245,7 +297,7 @@ where
|
|||
} else {
|
||||
title_primitive
|
||||
},
|
||||
mouse::Interaction::default(),
|
||||
title_interaction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::{
|
||||
mouse, Font, HorizontalAlignment, Point, Rectangle, VerticalAlignment,
|
||||
mouse, Font, HorizontalAlignment, Padding, Point, Rectangle,
|
||||
VerticalAlignment,
|
||||
};
|
||||
use iced_style::menu;
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ where
|
|||
{
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
const DEFAULT_PADDING: u16 = 5;
|
||||
const DEFAULT_PADDING: Padding = Padding::new(5);
|
||||
|
||||
fn menu_style(style: &Box<dyn StyleSheet>) -> menu::Style {
|
||||
style.menu()
|
||||
|
|
@ -30,12 +31,14 @@ where
|
|||
bounds: Rectangle,
|
||||
cursor_position: Point,
|
||||
selected: Option<String>,
|
||||
padding: u16,
|
||||
placeholder: Option<&str>,
|
||||
padding: Padding,
|
||||
text_size: u16,
|
||||
font: Font,
|
||||
style: &Box<dyn StyleSheet>,
|
||||
) -> Self::Output {
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
let is_selected = selected.is_some();
|
||||
|
||||
let style = if is_mouse_over {
|
||||
style.hovered()
|
||||
|
|
@ -56,7 +59,7 @@ where
|
|||
font: B::ICON_FONT,
|
||||
size: bounds.height * style.icon_size,
|
||||
bounds: Rectangle {
|
||||
x: bounds.x + bounds.width - f32::from(padding) * 2.0,
|
||||
x: bounds.x + bounds.width - f32::from(padding.horizontal()),
|
||||
y: bounds.center_y(),
|
||||
..bounds
|
||||
},
|
||||
|
|
@ -67,14 +70,18 @@ where
|
|||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: if let Some(label) = selected {
|
||||
primitives: if let Some(label) =
|
||||
selected.or_else(|| placeholder.map(str::to_string))
|
||||
{
|
||||
let label = Primitive::Text {
|
||||
content: label,
|
||||
size: f32::from(text_size),
|
||||
font,
|
||||
color: style.text_color,
|
||||
color: is_selected
|
||||
.then(|| style.text_color)
|
||||
.unwrap_or(style.placeholder_color),
|
||||
bounds: Rectangle {
|
||||
x: bounds.x + f32::from(padding),
|
||||
x: bounds.x + f32::from(padding.left),
|
||||
y: bounds.center_y(),
|
||||
..bounds
|
||||
},
|
||||
|
|
|
|||
|
|
@ -134,8 +134,16 @@ where
|
|||
Primitive::None
|
||||
};
|
||||
|
||||
let scroll = Primitive::Clip {
|
||||
bounds,
|
||||
offset: Vector::new(0, 0),
|
||||
content: Box::new(Primitive::Group {
|
||||
primitives: vec![scrollbar, scroller],
|
||||
}),
|
||||
};
|
||||
|
||||
Primitive::Group {
|
||||
primitives: vec![clip, scrollbar, scroller],
|
||||
primitives: vec![clip, scroll],
|
||||
}
|
||||
} else {
|
||||
content
|
||||
|
|
|
|||
99
graphics/src/widget/toggler.rs
Normal file
99
graphics/src/widget/toggler.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::mouse;
|
||||
use iced_native::toggler;
|
||||
use iced_native::Rectangle;
|
||||
|
||||
pub use iced_style::toggler::{Style, StyleSheet};
|
||||
|
||||
/// Makes sure that the border radius of the toggler looks good at every size.
|
||||
const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0;
|
||||
|
||||
/// The space ratio between the background Quad and the Toggler bounds, and
|
||||
/// between the background Quad and foreground Quad.
|
||||
const SPACE_RATIO: f32 = 0.05;
|
||||
|
||||
/// A toggler that can be toggled.
|
||||
///
|
||||
/// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`.
|
||||
pub type Toggler<Message, Backend> =
|
||||
iced_native::Toggler<Message, Renderer<Backend>>;
|
||||
|
||||
impl<B> toggler::Renderer for Renderer<B>
|
||||
where
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
const DEFAULT_SIZE: u16 = 20;
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
bounds: Rectangle,
|
||||
is_active: bool,
|
||||
is_mouse_over: bool,
|
||||
label: Option<Self::Output>,
|
||||
style_sheet: &Self::Style,
|
||||
) -> Self::Output {
|
||||
let style = if is_mouse_over {
|
||||
style_sheet.hovered(is_active)
|
||||
} else {
|
||||
style_sheet.active(is_active)
|
||||
};
|
||||
|
||||
let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
|
||||
let space = SPACE_RATIO * bounds.height as f32;
|
||||
|
||||
let toggler_background_bounds = Rectangle {
|
||||
x: bounds.x + space,
|
||||
y: bounds.y + space,
|
||||
width: bounds.width - (2.0 * space),
|
||||
height: bounds.height - (2.0 * space),
|
||||
};
|
||||
|
||||
let toggler_background = Primitive::Quad {
|
||||
bounds: toggler_background_bounds,
|
||||
background: style.background.into(),
|
||||
border_radius,
|
||||
border_width: 1.0,
|
||||
border_color: style.background_border.unwrap_or(style.background),
|
||||
};
|
||||
|
||||
let toggler_foreground_bounds = Rectangle {
|
||||
x: bounds.x
|
||||
+ if is_active {
|
||||
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
|
||||
} else {
|
||||
2.0 * space
|
||||
},
|
||||
y: bounds.y + (2.0 * space),
|
||||
width: bounds.height - (4.0 * space),
|
||||
height: bounds.height - (4.0 * space),
|
||||
};
|
||||
|
||||
let toggler_foreground = Primitive::Quad {
|
||||
bounds: toggler_foreground_bounds,
|
||||
background: style.foreground.into(),
|
||||
border_radius,
|
||||
border_width: 1.0,
|
||||
border_color: style.foreground_border.unwrap_or(style.foreground),
|
||||
};
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: match label {
|
||||
Some((l, _)) => {
|
||||
vec![l, toggler_background, toggler_foreground]
|
||||
}
|
||||
None => vec![toggler_background, toggler_foreground],
|
||||
},
|
||||
},
|
||||
if is_mouse_over {
|
||||
mouse::Interaction::Pointer
|
||||
} else {
|
||||
mouse::Interaction::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
168
graphics/src/widget/tooltip.rs
Normal file
168
graphics/src/widget/tooltip.rs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
//! Decorate content and apply alignment.
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::defaults::{self, Defaults};
|
||||
use crate::{Primitive, Renderer, Vector};
|
||||
|
||||
use iced_native::container;
|
||||
use iced_native::layout::{self, Layout};
|
||||
use iced_native::{Element, Padding, Point, Rectangle, Size, Text};
|
||||
|
||||
/// An element decorating some content.
|
||||
///
|
||||
/// This is an alias of an `iced_native` tooltip with a default
|
||||
/// `Renderer`.
|
||||
pub type Tooltip<'a, Message, Backend> =
|
||||
iced_native::Tooltip<'a, Message, Renderer<Backend>>;
|
||||
|
||||
pub use iced_native::tooltip::Position;
|
||||
|
||||
impl<B> iced_native::tooltip::Renderer for Renderer<B>
|
||||
where
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
const DEFAULT_PADDING: u16 = 5;
|
||||
|
||||
fn draw<Message>(
|
||||
&mut self,
|
||||
defaults: &Defaults,
|
||||
cursor_position: Point,
|
||||
content_layout: Layout<'_>,
|
||||
viewport: &Rectangle,
|
||||
content: &Element<'_, Message, Self>,
|
||||
tooltip: &Text<Self>,
|
||||
position: Position,
|
||||
style_sheet: &<Self as container::Renderer>::Style,
|
||||
gap: u16,
|
||||
padding: u16,
|
||||
) -> Self::Output {
|
||||
let (content, mouse_interaction) = content.draw(
|
||||
self,
|
||||
&defaults,
|
||||
content_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
let bounds = content_layout.bounds();
|
||||
|
||||
if bounds.contains(cursor_position) {
|
||||
use iced_native::Widget;
|
||||
|
||||
let gap = f32::from(gap);
|
||||
let style = style_sheet.style();
|
||||
|
||||
let defaults = Defaults {
|
||||
text: defaults::Text {
|
||||
color: style.text_color.unwrap_or(defaults.text.color),
|
||||
},
|
||||
};
|
||||
|
||||
let text_layout = Widget::<(), Self>::layout(
|
||||
tooltip,
|
||||
self,
|
||||
&layout::Limits::new(Size::ZERO, viewport.size())
|
||||
.pad(Padding::new(padding)),
|
||||
);
|
||||
|
||||
let padding = f32::from(padding);
|
||||
let text_bounds = text_layout.bounds();
|
||||
let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0;
|
||||
let y_center =
|
||||
bounds.y + (bounds.height - text_bounds.height) / 2.0;
|
||||
|
||||
let mut tooltip_bounds = {
|
||||
let offset = match position {
|
||||
Position::Top => Vector::new(
|
||||
x_center,
|
||||
bounds.y - text_bounds.height - gap - padding,
|
||||
),
|
||||
Position::Bottom => Vector::new(
|
||||
x_center,
|
||||
bounds.y + bounds.height + gap + padding,
|
||||
),
|
||||
Position::Left => Vector::new(
|
||||
bounds.x - text_bounds.width - gap - padding,
|
||||
y_center,
|
||||
),
|
||||
Position::Right => Vector::new(
|
||||
bounds.x + bounds.width + gap + padding,
|
||||
y_center,
|
||||
),
|
||||
Position::FollowCursor => Vector::new(
|
||||
cursor_position.x,
|
||||
cursor_position.y - text_bounds.height,
|
||||
),
|
||||
};
|
||||
|
||||
Rectangle {
|
||||
x: offset.x - padding,
|
||||
y: offset.y - padding,
|
||||
width: text_bounds.width + padding * 2.0,
|
||||
height: text_bounds.height + padding * 2.0,
|
||||
}
|
||||
};
|
||||
|
||||
if tooltip_bounds.x < viewport.x {
|
||||
tooltip_bounds.x = viewport.x;
|
||||
} else if viewport.x + viewport.width
|
||||
< tooltip_bounds.x + tooltip_bounds.width
|
||||
{
|
||||
tooltip_bounds.x =
|
||||
viewport.x + viewport.width - tooltip_bounds.width;
|
||||
}
|
||||
|
||||
if tooltip_bounds.y < viewport.y {
|
||||
tooltip_bounds.y = viewport.y;
|
||||
} else if viewport.y + viewport.height
|
||||
< tooltip_bounds.y + tooltip_bounds.height
|
||||
{
|
||||
tooltip_bounds.y =
|
||||
viewport.y + viewport.height - tooltip_bounds.height;
|
||||
}
|
||||
|
||||
let (tooltip, _) = Widget::<(), Self>::draw(
|
||||
tooltip,
|
||||
self,
|
||||
&defaults,
|
||||
Layout::with_offset(
|
||||
Vector::new(
|
||||
tooltip_bounds.x + padding,
|
||||
tooltip_bounds.y + padding,
|
||||
),
|
||||
&text_layout,
|
||||
),
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: vec![
|
||||
content,
|
||||
Primitive::Clip {
|
||||
bounds: *viewport,
|
||||
offset: Vector::new(0, 0),
|
||||
content: Box::new(
|
||||
if let Some(background) =
|
||||
crate::container::background(
|
||||
tooltip_bounds,
|
||||
&style,
|
||||
)
|
||||
{
|
||||
Primitive::Group {
|
||||
primitives: vec![background, tooltip],
|
||||
}
|
||||
} else {
|
||||
tooltip
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
mouse_interaction,
|
||||
)
|
||||
} else {
|
||||
(content, mouse_interaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,10 @@ pub trait Compositor: Sized {
|
|||
type SwapChain;
|
||||
|
||||
/// Creates a new [`Compositor`].
|
||||
fn new(settings: Self::Settings) -> Result<(Self, Self::Renderer), Error>;
|
||||
fn new<W: HasRawWindowHandle>(
|
||||
settings: Self::Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Result<(Self, Self::Renderer), Error>;
|
||||
|
||||
/// Crates a new [`Surface`] for the given window.
|
||||
///
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue