Merge branch 'iced-rs:master' into viewer_content_fit

This commit is contained in:
Gigas002 2024-04-16 00:08:17 +09:00 committed by GitHub
commit 0ebe0629ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
124 changed files with 5702 additions and 4490 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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`].

View file

@ -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();
if filtered_options.options.is_empty() {
None
} else {
let bounds = layout.bounds();
(self.on_selected)(x)
},
self.on_option_hovered.as_deref(),
&self.menu_class,
)
.width(bounds.width)
.padding(self.padding);
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();
if let Some(font) = self.font {
menu = menu.font(font);
(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);
}
Some(
menu.overlay(
layout.position() + translation,
bounds.height,
),
)
}
if let Some(size) = self.size {
menu = menu.text_size(size);
}
Some(menu.overlay(layout.position() + translation, bounds.height))
} else {
None
}

View file

@ -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`].

View file

@ -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>

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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 {

View file

@ -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());

View file

@ -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`].

View file

@ -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| {

View file

@ -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(

View file

@ -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

View file

@ -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();

View file

@ -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)