summaryrefslogtreecommitdiffstats
path: root/widget
diff options
context:
space:
mode:
authorLibravatar Gigas002 <24297712+Gigas002@users.noreply.github.com>2024-04-16 00:08:17 +0900
committerLibravatar GitHub <noreply@github.com>2024-04-16 00:08:17 +0900
commit0ebe0629cef37aee5c48b9409fc36618a3a3e60d (patch)
tree909d9ecf28e7c491bae3afc81928c118517fa7a9 /widget
parent13bd106fc585034a7aba17b9c17589113274aaf5 (diff)
parent105b8bd5ad6ade1f203a0d8b0b93bd06f61f621a (diff)
downloadiced-0ebe0629cef37aee5c48b9409fc36618a3a3e60d.tar.gz
iced-0ebe0629cef37aee5c48b9409fc36618a3a3e60d.tar.bz2
iced-0ebe0629cef37aee5c48b9409fc36618a3a3e60d.zip
Merge branch 'iced-rs:master' into viewer_content_fit
Diffstat (limited to 'widget')
-rw-r--r--widget/Cargo.toml4
-rw-r--r--widget/src/canvas.rs6
-rw-r--r--widget/src/checkbox.rs2
-rw-r--r--widget/src/column.rs9
-rw-r--r--widget/src/combo_box.rs65
-rw-r--r--widget/src/helpers.rs17
-rw-r--r--widget/src/keyed/column.rs46
-rw-r--r--widget/src/lazy.rs21
-rw-r--r--widget/src/lib.rs7
-rw-r--r--widget/src/overlay/menu.rs2
-rw-r--r--widget/src/pane_grid/state.rs8
-rw-r--r--widget/src/pick_list.rs4
-rw-r--r--widget/src/row.rs9
-rw-r--r--widget/src/scrollable.rs6
-rw-r--r--widget/src/shader.rs11
-rw-r--r--widget/src/shader/program.rs4
-rw-r--r--widget/src/text_editor.rs15
-rw-r--r--widget/src/text_input.rs21
18 files changed, 176 insertions, 81 deletions
diff --git a/widget/Cargo.toml b/widget/Cargo.toml
index a45f47ef..3c9f6a54 100644
--- a/widget/Cargo.toml
+++ b/widget/Cargo.toml
@@ -10,6 +10,9 @@ homepage.workspace = true
categories.workspace = true
keywords.workspace = true
+[lints]
+workspace = true
+
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
all-features = true
@@ -28,6 +31,7 @@ iced_renderer.workspace = true
iced_runtime.workspace = true
num-traits.workspace = true
+rustc-hash.workspace = true
thiserror.workspace = true
unicode-segmentation.workspace = true
diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs
index 7a21895a..42f92de0 100644
--- a/widget/src/canvas.rs
+++ b/widget/src/canvas.rs
@@ -17,7 +17,7 @@ use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget::tree::{self, Tree};
use crate::core::{
- Clipboard, Element, Length, Rectangle, Shell, Size, Transformation, Widget,
+ Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
};
use crate::graphics::geometry;
@@ -222,8 +222,8 @@ where
let state = tree.state.downcast_ref::<P::State>();
- renderer.with_transformation(
- Transformation::translate(bounds.x, bounds.y),
+ renderer.with_translation(
+ Vector::new(bounds.x, bounds.y),
|renderer| {
let layers =
self.program.draw(state, renderer, theme, bounds, cursor);
diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs
index 48f6abf6..225c316d 100644
--- a/widget/src/checkbox.rs
+++ b/widget/src/checkbox.rs
@@ -340,7 +340,7 @@ where
if self.is_checked {
renderer.fill_text(
text::Text {
- content: &code_point.to_string(),
+ content: code_point.to_string(),
font: *font,
size,
line_height: *line_height,
diff --git a/widget/src/column.rs b/widget/src/column.rs
index d37ef695..df7829b3 100644
--- a/widget/src/column.rs
+++ b/widget/src/column.rs
@@ -33,11 +33,18 @@ where
Self::from_vec(Vec::new())
}
+ /// Creates a [`Column`] with the given capacity.
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self::from_vec(Vec::with_capacity(capacity))
+ }
+
/// Creates a [`Column`] with the given elements.
pub fn with_children(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Self {
- Self::new().extend(children)
+ let iterator = children.into_iter();
+
+ Self::with_capacity(iterator.size_hint().0).extend(iterator)
}
/// Creates a [`Column`] from an already allocated [`Vec`].
diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs
index e4f4a41f..253850df 100644
--- a/widget/src/combo_box.rs
+++ b/widget/src/combo_box.rs
@@ -700,38 +700,47 @@ where
..
} = tree.state.downcast_mut::<Menu<T>>();
- let bounds = layout.bounds();
-
self.state.sync_filtered_options(filtered_options);
- let mut menu = menu::Menu::new(
- menu,
- &filtered_options.options,
- hovered_option,
- |x| {
- tree.children[0]
- .state
- .downcast_mut::<text_input::State<Renderer::Paragraph>>(
- )
- .unfocus();
-
- (self.on_selected)(x)
- },
- self.on_option_hovered.as_deref(),
- &self.menu_class,
- )
- .width(bounds.width)
- .padding(self.padding);
-
- if let Some(font) = self.font {
- menu = menu.font(font);
- }
+ if filtered_options.options.is_empty() {
+ None
+ } else {
+ let bounds = layout.bounds();
+
+ let mut menu = menu::Menu::new(
+ menu,
+ &filtered_options.options,
+ hovered_option,
+ |x| {
+ tree.children[0]
+ .state
+ .downcast_mut::<text_input::State<Renderer::Paragraph>>(
+ )
+ .unfocus();
+
+ (self.on_selected)(x)
+ },
+ self.on_option_hovered.as_deref(),
+ &self.menu_class,
+ )
+ .width(bounds.width)
+ .padding(self.padding);
+
+ if let Some(font) = self.font {
+ menu = menu.font(font);
+ }
- if let Some(size) = self.size {
- menu = menu.text_size(size);
- }
+ if let Some(size) = self.size {
+ menu = menu.text_size(size);
+ }
- Some(menu.overlay(layout.position() + translation, bounds.height))
+ Some(
+ menu.overlay(
+ layout.position() + translation,
+ bounds.height,
+ ),
+ )
+ }
} else {
None
}
diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
index 77b30882..61789c19 100644
--- a/widget/src/helpers.rs
+++ b/widget/src/helpers.rs
@@ -145,13 +145,26 @@ where
///
/// [`Text`]: core::widget::Text
pub fn text<'a, Theme, Renderer>(
- text: impl ToString,
+ text: impl text::IntoFragment<'a>,
) -> Text<'a, Theme, Renderer>
where
Theme: text::Catalog + 'a,
Renderer: core::text::Renderer,
{
- Text::new(text.to_string())
+ Text::new(text)
+}
+
+/// Creates a new [`Text`] widget that displays the provided value.
+///
+/// [`Text`]: core::widget::Text
+pub fn value<'a, Theme, Renderer>(
+ value: impl ToString,
+) -> Text<'a, Theme, Renderer>
+where
+ Theme: text::Catalog + 'a,
+ Renderer: core::text::Renderer,
+{
+ Text::new(value.to_string())
}
/// Creates a new [`Checkbox`].
diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs
index 8a8d5fe7..a34ce9e6 100644
--- a/widget/src/keyed/column.rs
+++ b/widget/src/keyed/column.rs
@@ -40,27 +40,49 @@ where
{
/// Creates an empty [`Column`].
pub fn new() -> Self {
- Column {
+ Self::from_vecs(Vec::new(), Vec::new())
+ }
+
+ /// Creates a [`Column`] from already allocated [`Vec`]s.
+ ///
+ /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, which means
+ /// it won't automatically adapt to the sizing strategy of its contents.
+ ///
+ /// If any of the children have a [`Length::Fill`] strategy, you will need to
+ /// call [`Column::width`] or [`Column::height`] accordingly.
+ pub fn from_vecs(
+ keys: Vec<Key>,
+ children: Vec<Element<'a, Message, Theme, Renderer>>,
+ ) -> Self {
+ Self {
spacing: 0.0,
padding: Padding::ZERO,
width: Length::Shrink,
height: Length::Shrink,
max_width: f32::INFINITY,
align_items: Alignment::Start,
- keys: Vec::new(),
- children: Vec::new(),
+ keys,
+ children,
}
}
+ /// Creates a [`Column`] with the given capacity.
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self::from_vecs(
+ Vec::with_capacity(capacity),
+ Vec::with_capacity(capacity),
+ )
+ }
+
/// Creates a [`Column`] with the given elements.
pub fn with_children(
children: impl IntoIterator<
Item = (Key, Element<'a, Message, Theme, Renderer>),
>,
) -> Self {
- children
- .into_iter()
- .fold(Self::new(), |column, (key, child)| column.push(key, child))
+ let iterator = children.into_iter();
+
+ Self::with_capacity(iterator.size_hint().0).extend(iterator)
}
/// Sets the vertical spacing _between_ elements.
@@ -132,6 +154,18 @@ where
self
}
}
+
+ /// Extends the [`Column`] with the given children.
+ pub fn extend(
+ self,
+ children: impl IntoIterator<
+ Item = (Key, Element<'a, Message, Theme, Renderer>),
+ >,
+ ) -> Self {
+ children
+ .into_iter()
+ .fold(self, |column, (key, child)| column.push(key, child))
+ }
}
impl<'a, Key, Message, Renderer> Default for Column<'a, Key, Message, Renderer>
diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs
index eb663ea5..04783dbe 100644
--- a/widget/src/lazy.rs
+++ b/widget/src/lazy.rs
@@ -18,11 +18,12 @@ use crate::core::widget::tree::{self, Tree};
use crate::core::widget::{self, Widget};
use crate::core::Element;
use crate::core::{
- self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, Vector,
+ self, Clipboard, Length, Point, Rectangle, Shell, Size, Vector,
};
use crate::runtime::overlay::Nested;
use ouroboros::self_referencing;
+use rustc_hash::FxHasher;
use std::cell::RefCell;
use std::hash::{Hash, Hasher as H};
use std::rc::Rc;
@@ -106,9 +107,12 @@ where
}
fn state(&self) -> tree::State {
- let mut hasher = Hasher::default();
- self.dependency.hash(&mut hasher);
- let hash = hasher.finish();
+ let hash = {
+ let mut hasher = FxHasher::default();
+ self.dependency.hash(&mut hasher);
+
+ hasher.finish()
+ };
let element =
Rc::new(RefCell::new(Some((self.view)(&self.dependency).into())));
@@ -127,9 +131,12 @@ where
.state
.downcast_mut::<Internal<Message, Theme, Renderer>>();
- let mut hasher = Hasher::default();
- self.dependency.hash(&mut hasher);
- let new_hash = hasher.finish();
+ let new_hash = {
+ let mut hasher = FxHasher::default();
+ self.dependency.hash(&mut hasher);
+
+ hasher.finish()
+ };
if current.hash != new_hash {
current.hash = new_hash;
diff --git a/widget/src/lib.rs b/widget/src/lib.rs
index 209dfad9..1eeacbae 100644
--- a/widget/src/lib.rs
+++ b/widget/src/lib.rs
@@ -2,13 +2,6 @@
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
-#![forbid(unsafe_code, rust_2018_idioms)]
-#![deny(
- missing_debug_implementations,
- missing_docs,
- unused_results,
- rustdoc::broken_intra_doc_links
-)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub use iced_renderer as renderer;
pub use iced_renderer::graphics;
diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs
index d76caa8a..98efe305 100644
--- a/widget/src/overlay/menu.rs
+++ b/widget/src/overlay/menu.rs
@@ -526,7 +526,7 @@ where
renderer.fill_text(
Text {
- content: &option.to_string(),
+ content: option.to_string(),
bounds: Size::new(f32::INFINITY, bounds.height),
size: text_size,
line_height: self.text_line_height,
diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs
index 481cd770..c20c3b9c 100644
--- a/widget/src/pane_grid/state.rs
+++ b/widget/src/pane_grid/state.rs
@@ -6,7 +6,7 @@ use crate::pane_grid::{
Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target,
};
-use std::collections::HashMap;
+use rustc_hash::FxHashMap;
/// The state of a [`PaneGrid`].
///
@@ -25,7 +25,7 @@ pub struct State<T> {
/// The panes of the [`PaneGrid`].
///
/// [`PaneGrid`]: super::PaneGrid
- pub panes: HashMap<Pane, T>,
+ pub panes: FxHashMap<Pane, T>,
/// The internal state of the [`PaneGrid`].
///
@@ -52,7 +52,7 @@ impl<T> State<T> {
/// Creates a new [`State`] with the given [`Configuration`].
pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self {
- let mut panes = HashMap::new();
+ let mut panes = FxHashMap::default();
let internal =
Internal::from_configuration(&mut panes, config.into(), 0);
@@ -353,7 +353,7 @@ impl Internal {
///
/// [`PaneGrid`]: super::PaneGrid
pub fn from_configuration<T>(
- panes: &mut HashMap<Pane, T>,
+ panes: &mut FxHashMap<Pane, T>,
content: Configuration<T>,
next_id: usize,
) -> Self {
diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs
index 801e792b..edccfdaa 100644
--- a/widget/src/pick_list.rs
+++ b/widget/src/pick_list.rs
@@ -479,7 +479,7 @@ where
renderer.fill_text(
Text {
- content: &code_point.to_string(),
+ content: code_point.to_string(),
size,
line_height,
font,
@@ -502,7 +502,7 @@ where
let label = selected.map(ToString::to_string);
- if let Some(label) = label.as_deref().or(self.placeholder.as_deref()) {
+ if let Some(label) = label.or_else(|| self.placeholder.clone()) {
let text_size =
self.text_size.unwrap_or_else(|| renderer.default_size());
diff --git a/widget/src/row.rs b/widget/src/row.rs
index 47feff9c..fa352171 100644
--- a/widget/src/row.rs
+++ b/widget/src/row.rs
@@ -31,11 +31,18 @@ where
Self::from_vec(Vec::new())
}
+ /// Creates a [`Row`] with the given capacity.
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self::from_vec(Vec::with_capacity(capacity))
+ }
+
/// Creates a [`Row`] with the given elements.
pub fn with_children(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Self {
- Self::new().extend(children)
+ let iterator = children.into_iter();
+
+ Self::with_capacity(iterator.size_hint().0).extend(iterator)
}
/// Creates a [`Row`] from an already allocated [`Vec`].
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index 84e9ac15..668c5372 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -651,7 +651,7 @@ where
defaults: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
- _viewport: &Rectangle,
+ viewport: &Rectangle,
) {
let state = tree.state.downcast_ref::<State>();
@@ -767,8 +767,8 @@ where
renderer.with_layer(
Rectangle {
- width: bounds.width + 2.0,
- height: bounds.height + 2.0,
+ width: (bounds.width + 2.0).min(viewport.width),
+ height: (bounds.height + 2.0).min(viewport.height),
..bounds
},
|renderer| {
diff --git a/widget/src/shader.rs b/widget/src/shader.rs
index 68112f83..fad2f4eb 100644
--- a/widget/src/shader.rs
+++ b/widget/src/shader.rs
@@ -13,12 +13,13 @@ use crate::core::widget::tree::{self, Tree};
use crate::core::widget::{self, Widget};
use crate::core::window;
use crate::core::{Clipboard, Element, Length, Rectangle, Shell, Size};
-use crate::renderer::wgpu::primitive::pipeline;
+use crate::renderer::wgpu::primitive;
use std::marker::PhantomData;
+pub use crate::graphics::Viewport;
pub use crate::renderer::wgpu::wgpu;
-pub use pipeline::{Primitive, Storage};
+pub use primitive::{Primitive, Storage};
/// A widget which can render custom shaders with Iced's `wgpu` backend.
///
@@ -60,7 +61,7 @@ impl<P, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Shader<Message, P>
where
P: Program<Message>,
- Renderer: pipeline::Renderer,
+ Renderer: primitive::Renderer,
{
fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
@@ -160,7 +161,7 @@ where
let bounds = layout.bounds();
let state = tree.state.downcast_ref::<P::State>();
- renderer.draw_pipeline_primitive(
+ renderer.draw_primitive(
bounds,
self.program.draw(state, cursor_position, bounds),
);
@@ -171,7 +172,7 @@ impl<'a, Message, Theme, Renderer, P> From<Shader<Message, P>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
- Renderer: pipeline::Renderer,
+ Renderer: primitive::Renderer,
P: Program<Message> + 'a,
{
fn from(
diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs
index 6dd50404..902c7c3b 100644
--- a/widget/src/shader/program.rs
+++ b/widget/src/shader/program.rs
@@ -1,7 +1,7 @@
use crate::core::event;
use crate::core::mouse;
use crate::core::{Rectangle, Shell};
-use crate::renderer::wgpu::primitive::pipeline;
+use crate::renderer::wgpu::Primitive;
use crate::shader;
/// The state and logic of a [`Shader`] widget.
@@ -15,7 +15,7 @@ pub trait Program<Message> {
type State: Default + 'static;
/// The type of primitive this [`Program`] can draw.
- type Primitive: pipeline::Primitive + 'static;
+ type Primitive: Primitive + 'static;
/// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes
/// based on mouse & other events. You can use the [`Shell`] to publish messages, request a
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index a00df3c7..92cdb251 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -110,6 +110,21 @@ where
self
}
+ /// Sets the text size of the [`TextEditor`].
+ pub fn size(mut self, size: impl Into<Pixels>) -> Self {
+ self.text_size = Some(size.into());
+ self
+ }
+
+ /// Sets the [`text::LineHeight`] of the [`TextEditor`].
+ pub fn line_height(
+ mut self,
+ line_height: impl Into<text::LineHeight>,
+ ) -> Self {
+ self.line_height = line_height.into();
+ self
+ }
+
/// Sets the [`Padding`] of the [`TextEditor`].
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index dafe2fca..e9f07838 100644
--- a/widget/src/text_input.rs
+++ b/widget/src/text_input.rs
@@ -232,7 +232,7 @@ where
let placeholder_text = Text {
font,
line_height: self.line_height,
- content: &self.placeholder,
+ content: self.placeholder.as_str(),
bounds: Size::new(f32::INFINITY, text_bounds.height),
size: text_size,
horizontal_alignment: alignment::Horizontal::Left,
@@ -251,9 +251,11 @@ where
});
if let Some(icon) = &self.icon {
+ let mut content = [0; 4];
+
let icon_text = Text {
line_height: self.line_height,
- content: &icon.code_point.to_string(),
+ content: icon.code_point.encode_utf8(&mut content) as &_,
font: icon.font,
size: icon.size.unwrap_or_else(|| renderer.default_size()),
bounds: Size::new(f32::INFINITY, text_bounds.height),
@@ -366,7 +368,7 @@ where
let text = value.to_string();
- let (cursor, offset) = if let Some(focus) = state
+ let (cursor, offset, is_selecting) = if let Some(focus) = state
.is_focused
.as_ref()
.filter(|focus| focus.is_window_focused)
@@ -404,7 +406,7 @@ where
None
};
- (cursor, offset)
+ (cursor, offset, false)
}
cursor::State::Selection { start, end } => {
let left = start.min(end);
@@ -444,11 +446,12 @@ where
} else {
left_offset
},
+ true,
)
}
}
} else {
- (None, 0.0)
+ (None, 0.0, false)
};
let draw = |renderer: &mut Renderer, viewport| {
@@ -480,7 +483,7 @@ where
);
};
- if cursor.is_some() {
+ if is_selecting {
renderer
.with_layer(text_bounds, |renderer| draw(renderer, *viewport));
} else {
@@ -710,7 +713,8 @@ where
match key.as_ref() {
keyboard::Key::Character("c")
- if state.keyboard_modifiers.command() =>
+ if state.keyboard_modifiers.command()
+ && !self.is_secure =>
{
if let Some((start, end)) =
state.cursor.selection(&self.value)
@@ -724,7 +728,8 @@ where
return event::Status::Captured;
}
keyboard::Key::Character("x")
- if state.keyboard_modifiers.command() =>
+ if state.keyboard_modifiers.command()
+ && !self.is_secure =>
{
if let Some((start, end)) =
state.cursor.selection(&self.value)