iced_widget/helpers.rs
1//! Helper functions to create pure widgets.
2use crate::button::{self, Button};
3use crate::checkbox::{self, Checkbox};
4use crate::combo_box::{self, ComboBox};
5use crate::container::{self, Container};
6use crate::core;
7use crate::core::theme;
8use crate::core::widget::operation::{self, Operation};
9use crate::core::window;
10use crate::core::{Element, Length, Size, Widget};
11use crate::float::{self, Float};
12use crate::keyed;
13use crate::overlay;
14use crate::pane_grid::{self, PaneGrid};
15use crate::pick_list::{self, PickList};
16use crate::progress_bar::{self, ProgressBar};
17use crate::radio::{self, Radio};
18use crate::scrollable::{self, Scrollable};
19use crate::slider::{self, Slider};
20use crate::text::{self, Text};
21use crate::text_editor::{self, TextEditor};
22use crate::text_input::{self, TextInput};
23use crate::toggler::{self, Toggler};
24use crate::tooltip::{self, Tooltip};
25use crate::vertical_slider::{self, VerticalSlider};
26use crate::{
27 Column, Grid, MouseArea, Pin, Responsive, Row, Sensor, Space, Stack, Themer,
28};
29
30use std::borrow::Borrow;
31use std::ops::RangeInclusive;
32
33pub use crate::table::table;
34
35/// Creates a [`Column`] with the given children.
36///
37/// Columns distribute their children vertically.
38///
39/// # Example
40/// ```no_run
41/// # mod iced { pub mod widget { pub use iced_widget::*; } }
42/// # pub type State = ();
43/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
44/// use iced::widget::{button, column};
45///
46/// #[derive(Debug, Clone)]
47/// enum Message {
48/// // ...
49/// }
50///
51/// fn view(state: &State) -> Element<'_, Message> {
52/// column![
53/// "I am on top!",
54/// button("I am in the center!"),
55/// "I am below.",
56/// ].into()
57/// }
58/// ```
59#[macro_export]
60macro_rules! column {
61 () => (
62 $crate::Column::new()
63 );
64 ($($x:expr),+ $(,)?) => (
65 $crate::Column::with_children([$($crate::core::Element::from($x)),+])
66 );
67}
68
69/// Creates a [`Row`] with the given children.
70///
71/// Rows distribute their children horizontally.
72///
73/// # Example
74/// ```no_run
75/// # mod iced { pub mod widget { pub use iced_widget::*; } }
76/// # pub type State = ();
77/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
78/// use iced::widget::{button, row};
79///
80/// #[derive(Debug, Clone)]
81/// enum Message {
82/// // ...
83/// }
84///
85/// fn view(state: &State) -> Element<'_, Message> {
86/// row![
87/// "I am to the left!",
88/// button("I am in the middle!"),
89/// "I am to the right!",
90/// ].into()
91/// }
92/// ```
93#[macro_export]
94macro_rules! row {
95 () => (
96 $crate::Row::new()
97 );
98 ($($x:expr),+ $(,)?) => (
99 $crate::Row::with_children([$($crate::core::Element::from($x)),+])
100 );
101}
102
103/// Creates a [`Stack`] with the given children.
104///
105/// [`Stack`]: crate::Stack
106#[macro_export]
107macro_rules! stack {
108 () => (
109 $crate::Stack::new()
110 );
111 ($($x:expr),+ $(,)?) => (
112 $crate::Stack::with_children([$($crate::core::Element::from($x)),+])
113 );
114}
115
116/// Creates a new [`Text`] widget with the provided content.
117///
118/// [`Text`]: core::widget::Text
119///
120/// This macro uses the same syntax as [`format!`], but creates a new [`Text`] widget instead.
121///
122/// See [the formatting documentation in `std::fmt`](std::fmt)
123/// for details of the macro argument syntax.
124///
125/// # Examples
126///
127/// ```no_run
128/// # mod iced {
129/// # pub mod widget {
130/// # macro_rules! text {
131/// # ($($arg:tt)*) => {unimplemented!()}
132/// # }
133/// # pub(crate) use text;
134/// # }
135/// # }
136/// # pub type State = ();
137/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::core::Theme, ()>;
138/// use iced::widget::text;
139///
140/// enum Message {
141/// // ...
142/// }
143///
144/// fn view(_state: &State) -> Element<Message> {
145/// let simple = text!("Hello, world!");
146///
147/// let keyword = text!("Hello, {}", "world!");
148///
149/// let planet = "Earth";
150/// let local_variable = text!("Hello, {planet}!");
151/// // ...
152/// # unimplemented!()
153/// }
154/// ```
155#[macro_export]
156macro_rules! text {
157 ($($arg:tt)*) => {
158 $crate::Text::new(format!($($arg)*))
159 };
160}
161
162/// Creates some [`Rich`] text with the given spans.
163///
164/// [`Rich`]: text::Rich
165///
166/// # Example
167/// ```no_run
168/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; }
169/// # pub type State = ();
170/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
171/// use iced::font;
172/// use iced::widget::{rich_text, span};
173/// use iced::{color, never, Font};
174///
175/// #[derive(Debug, Clone)]
176/// enum Message {
177/// // ...
178/// }
179///
180/// fn view(state: &State) -> Element<'_, Message> {
181/// rich_text![
182/// span("I am red!").color(color!(0xff0000)),
183/// span(" "),
184/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }),
185/// ]
186/// .on_link_click(never)
187/// .size(20)
188/// .into()
189/// }
190/// ```
191#[macro_export]
192macro_rules! rich_text {
193 () => (
194 $crate::text::Rich::new()
195 );
196 ($($x:expr),+ $(,)?) => (
197 $crate::text::Rich::from_iter([$($crate::text::Span::from($x)),+])
198 );
199}
200
201/// Creates a new [`Container`] with the provided content.
202///
203/// Containers let you align a widget inside their boundaries.
204///
205/// # Example
206/// ```no_run
207/// # mod iced { pub mod widget { pub use iced_widget::*; } }
208/// # pub type State = ();
209/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
210/// use iced::widget::container;
211///
212/// enum Message {
213/// // ...
214/// }
215///
216/// fn view(state: &State) -> Element<'_, Message> {
217/// container("This text is centered inside a rounded box!")
218/// .padding(10)
219/// .center(800)
220/// .style(container::rounded_box)
221/// .into()
222/// }
223/// ```
224pub fn container<'a, Message, Theme, Renderer>(
225 content: impl Into<Element<'a, Message, Theme, Renderer>>,
226) -> Container<'a, Message, Theme, Renderer>
227where
228 Theme: container::Catalog + 'a,
229 Renderer: core::Renderer,
230{
231 Container::new(content)
232}
233
234/// Creates a new [`Container`] that fills all the available space
235/// and centers its contents inside.
236///
237/// This is equivalent to:
238/// ```rust,no_run
239/// # use iced_widget::core::Length::Fill;
240/// # use iced_widget::Container;
241/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
242/// let center = container("Center!").center(Fill);
243/// ```
244///
245/// [`Container`]: crate::Container
246pub fn center<'a, Message, Theme, Renderer>(
247 content: impl Into<Element<'a, Message, Theme, Renderer>>,
248) -> Container<'a, Message, Theme, Renderer>
249where
250 Theme: container::Catalog + 'a,
251 Renderer: core::Renderer,
252{
253 container(content).center(Length::Fill)
254}
255
256/// Creates a new [`Container`] that fills all the available space
257/// horizontally and centers its contents inside.
258///
259/// This is equivalent to:
260/// ```rust,no_run
261/// # use iced_widget::core::Length::Fill;
262/// # use iced_widget::Container;
263/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
264/// let center_x = container("Horizontal Center!").center_x(Fill);
265/// ```
266///
267/// [`Container`]: crate::Container
268pub fn center_x<'a, Message, Theme, Renderer>(
269 content: impl Into<Element<'a, Message, Theme, Renderer>>,
270) -> Container<'a, Message, Theme, Renderer>
271where
272 Theme: container::Catalog + 'a,
273 Renderer: core::Renderer,
274{
275 container(content).center_x(Length::Fill)
276}
277
278/// Creates a new [`Container`] that fills all the available space
279/// vertically and centers its contents inside.
280///
281/// This is equivalent to:
282/// ```rust,no_run
283/// # use iced_widget::core::Length::Fill;
284/// # use iced_widget::Container;
285/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
286/// let center_y = container("Vertical Center!").center_y(Fill);
287/// ```
288///
289/// [`Container`]: crate::Container
290pub fn center_y<'a, Message, Theme, Renderer>(
291 content: impl Into<Element<'a, Message, Theme, Renderer>>,
292) -> Container<'a, Message, Theme, Renderer>
293where
294 Theme: container::Catalog + 'a,
295 Renderer: core::Renderer,
296{
297 container(content).center_y(Length::Fill)
298}
299
300/// Creates a new [`Container`] that fills all the available space
301/// horizontally and right-aligns its contents inside.
302///
303/// This is equivalent to:
304/// ```rust,no_run
305/// # use iced_widget::core::Length::Fill;
306/// # use iced_widget::Container;
307/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
308/// let right = container("Right!").align_right(Fill);
309/// ```
310///
311/// [`Container`]: crate::Container
312pub fn right<'a, Message, Theme, Renderer>(
313 content: impl Into<Element<'a, Message, Theme, Renderer>>,
314) -> Container<'a, Message, Theme, Renderer>
315where
316 Theme: container::Catalog + 'a,
317 Renderer: core::Renderer,
318{
319 container(content).align_right(Length::Fill)
320}
321
322/// Creates a new [`Container`] that fills all the available space
323/// and aligns its contents inside to the right center.
324///
325/// This is equivalent to:
326/// ```rust,no_run
327/// # use iced_widget::core::Length::Fill;
328/// # use iced_widget::Container;
329/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
330/// let right_center = container("Bottom Center!").align_right(Fill).center_y(Fill);
331/// ```
332///
333/// [`Container`]: crate::Container
334pub fn right_center<'a, Message, Theme, Renderer>(
335 content: impl Into<Element<'a, Message, Theme, Renderer>>,
336) -> Container<'a, Message, Theme, Renderer>
337where
338 Theme: container::Catalog + 'a,
339 Renderer: core::Renderer,
340{
341 container(content)
342 .align_right(Length::Fill)
343 .center_y(Length::Fill)
344}
345
346/// Creates a new [`Container`] that fills all the available space
347/// vertically and bottom-aligns its contents inside.
348///
349/// This is equivalent to:
350/// ```rust,no_run
351/// # use iced_widget::core::Length::Fill;
352/// # use iced_widget::Container;
353/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
354/// let bottom = container("Bottom!").align_bottom(Fill);
355/// ```
356///
357/// [`Container`]: crate::Container
358pub fn bottom<'a, Message, Theme, Renderer>(
359 content: impl Into<Element<'a, Message, Theme, Renderer>>,
360) -> Container<'a, Message, Theme, Renderer>
361where
362 Theme: container::Catalog + 'a,
363 Renderer: core::Renderer,
364{
365 container(content).align_bottom(Length::Fill)
366}
367
368/// Creates a new [`Container`] that fills all the available space
369/// and aligns its contents inside to the bottom center.
370///
371/// This is equivalent to:
372/// ```rust,no_run
373/// # use iced_widget::core::Length::Fill;
374/// # use iced_widget::Container;
375/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
376/// let bottom_center = container("Bottom Center!").center_x(Fill).align_bottom(Fill);
377/// ```
378///
379/// [`Container`]: crate::Container
380pub fn bottom_center<'a, Message, Theme, Renderer>(
381 content: impl Into<Element<'a, Message, Theme, Renderer>>,
382) -> Container<'a, Message, Theme, Renderer>
383where
384 Theme: container::Catalog + 'a,
385 Renderer: core::Renderer,
386{
387 container(content)
388 .center_x(Length::Fill)
389 .align_bottom(Length::Fill)
390}
391
392/// Creates a new [`Container`] that fills all the available space
393/// and aligns its contents inside to the bottom right corner.
394///
395/// This is equivalent to:
396/// ```rust,no_run
397/// # use iced_widget::core::Length::Fill;
398/// # use iced_widget::Container;
399/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
400/// let bottom_right = container("Bottom!").align_right(Fill).align_bottom(Fill);
401/// ```
402///
403/// [`Container`]: crate::Container
404pub fn bottom_right<'a, Message, Theme, Renderer>(
405 content: impl Into<Element<'a, Message, Theme, Renderer>>,
406) -> Container<'a, Message, Theme, Renderer>
407where
408 Theme: container::Catalog + 'a,
409 Renderer: core::Renderer,
410{
411 container(content)
412 .align_right(Length::Fill)
413 .align_bottom(Length::Fill)
414}
415
416/// Creates a new [`Pin`] widget with the given content.
417///
418/// A [`Pin`] widget positions its contents at some fixed coordinates inside of its boundaries.
419///
420/// # Example
421/// ```no_run
422/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::Length::Fill; }
423/// # pub type State = ();
424/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
425/// use iced::widget::pin;
426/// use iced::Fill;
427///
428/// enum Message {
429/// // ...
430/// }
431///
432/// fn view(state: &State) -> Element<'_, Message> {
433/// pin("This text is displayed at coordinates (50, 50)!")
434/// .x(50)
435/// .y(50)
436/// .into()
437/// }
438/// ```
439pub fn pin<'a, Message, Theme, Renderer>(
440 content: impl Into<Element<'a, Message, Theme, Renderer>>,
441) -> Pin<'a, Message, Theme, Renderer>
442where
443 Renderer: core::Renderer,
444{
445 Pin::new(content)
446}
447
448/// Creates a new [`Column`] with the given children.
449///
450/// Columns distribute their children vertically.
451///
452/// # Example
453/// ```no_run
454/// # mod iced { pub mod widget { pub use iced_widget::*; } }
455/// # pub type State = ();
456/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
457/// use iced::widget::{column, text};
458///
459/// enum Message {
460/// // ...
461/// }
462///
463/// fn view(state: &State) -> Element<'_, Message> {
464/// column((0..5).map(|i| text!("Item {i}").into())).into()
465/// }
466/// ```
467pub fn column<'a, Message, Theme, Renderer>(
468 children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
469) -> Column<'a, Message, Theme, Renderer>
470where
471 Renderer: core::Renderer,
472{
473 Column::with_children(children)
474}
475
476/// Creates a new [`keyed::Column`] from an iterator of elements.
477///
478/// Keyed columns distribute content vertically while keeping continuity.
479///
480/// # Example
481/// ```no_run
482/// # mod iced { pub mod widget { pub use iced_widget::*; } }
483/// # pub type State = ();
484/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
485/// use iced::widget::{keyed_column, text};
486///
487/// enum Message {
488/// // ...
489/// }
490///
491/// fn view(state: &State) -> Element<'_, Message> {
492/// keyed_column((0..=100).map(|i| {
493/// (i, text!("Item {i}").into())
494/// })).into()
495/// }
496/// ```
497pub fn keyed_column<'a, Key, Message, Theme, Renderer>(
498 children: impl IntoIterator<Item = (Key, Element<'a, Message, Theme, Renderer>)>,
499) -> keyed::Column<'a, Key, Message, Theme, Renderer>
500where
501 Key: Copy + PartialEq,
502 Renderer: core::Renderer,
503{
504 keyed::Column::with_children(children)
505}
506
507/// Creates a new [`Row`] from an iterator.
508///
509/// Rows distribute their children horizontally.
510///
511/// # Example
512/// ```no_run
513/// # mod iced { pub mod widget { pub use iced_widget::*; } }
514/// # pub type State = ();
515/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
516/// use iced::widget::{row, text};
517///
518/// enum Message {
519/// // ...
520/// }
521///
522/// fn view(state: &State) -> Element<'_, Message> {
523/// row((0..5).map(|i| text!("Item {i}").into())).into()
524/// }
525/// ```
526pub fn row<'a, Message, Theme, Renderer>(
527 children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
528) -> Row<'a, Message, Theme, Renderer>
529where
530 Renderer: core::Renderer,
531{
532 Row::with_children(children)
533}
534
535/// Creates a new [`Grid`] from an iterator.
536pub fn grid<'a, Message, Theme, Renderer>(
537 children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
538) -> Grid<'a, Message, Theme, Renderer>
539where
540 Renderer: core::Renderer,
541{
542 Grid::with_children(children)
543}
544
545/// Creates a new [`Stack`] with the given children.
546///
547/// [`Stack`]: crate::Stack
548pub fn stack<'a, Message, Theme, Renderer>(
549 children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
550) -> Stack<'a, Message, Theme, Renderer>
551where
552 Renderer: core::Renderer,
553{
554 Stack::with_children(children)
555}
556
557/// Wraps the given widget and captures any mouse button presses inside the bounds of
558/// the widget—effectively making it _opaque_.
559///
560/// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse
561/// events from passing through layers.
562///
563/// [`Stack`]: crate::Stack
564pub fn opaque<'a, Message, Theme, Renderer>(
565 content: impl Into<Element<'a, Message, Theme, Renderer>>,
566) -> Element<'a, Message, Theme, Renderer>
567where
568 Message: 'a,
569 Theme: 'a,
570 Renderer: core::Renderer + 'a,
571{
572 use crate::core::layout::{self, Layout};
573 use crate::core::mouse;
574 use crate::core::renderer;
575 use crate::core::widget::tree::{self, Tree};
576 use crate::core::{Event, Rectangle, Shell, Size};
577
578 struct Opaque<'a, Message, Theme, Renderer> {
579 content: Element<'a, Message, Theme, Renderer>,
580 }
581
582 impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
583 for Opaque<'_, Message, Theme, Renderer>
584 where
585 Renderer: core::Renderer,
586 {
587 fn tag(&self) -> tree::Tag {
588 self.content.as_widget().tag()
589 }
590
591 fn state(&self) -> tree::State {
592 self.content.as_widget().state()
593 }
594
595 fn children(&self) -> Vec<Tree> {
596 self.content.as_widget().children()
597 }
598
599 fn diff(&self, tree: &mut Tree) {
600 self.content.as_widget().diff(tree);
601 }
602
603 fn size(&self) -> Size<Length> {
604 self.content.as_widget().size()
605 }
606
607 fn size_hint(&self) -> Size<Length> {
608 self.content.as_widget().size_hint()
609 }
610
611 fn layout(
612 &mut self,
613 tree: &mut Tree,
614 renderer: &Renderer,
615 limits: &layout::Limits,
616 ) -> layout::Node {
617 self.content.as_widget_mut().layout(tree, renderer, limits)
618 }
619
620 fn draw(
621 &self,
622 tree: &Tree,
623 renderer: &mut Renderer,
624 theme: &Theme,
625 style: &renderer::Style,
626 layout: Layout<'_>,
627 cursor: mouse::Cursor,
628 viewport: &Rectangle,
629 ) {
630 self.content
631 .as_widget()
632 .draw(tree, renderer, theme, style, layout, cursor, viewport);
633 }
634
635 fn operate(
636 &mut self,
637 state: &mut Tree,
638 layout: Layout<'_>,
639 renderer: &Renderer,
640 operation: &mut dyn operation::Operation,
641 ) {
642 self.content
643 .as_widget_mut()
644 .operate(state, layout, renderer, operation);
645 }
646
647 fn update(
648 &mut self,
649 state: &mut Tree,
650 event: &Event,
651 layout: Layout<'_>,
652 cursor: mouse::Cursor,
653 renderer: &Renderer,
654 clipboard: &mut dyn core::Clipboard,
655 shell: &mut Shell<'_, Message>,
656 viewport: &Rectangle,
657 ) {
658 let is_mouse_press = matches!(
659 event,
660 core::Event::Mouse(mouse::Event::ButtonPressed(_))
661 );
662
663 self.content.as_widget_mut().update(
664 state, event, layout, cursor, renderer, clipboard, shell,
665 viewport,
666 );
667
668 if is_mouse_press && cursor.is_over(layout.bounds()) {
669 shell.capture_event();
670 }
671 }
672
673 fn mouse_interaction(
674 &self,
675 state: &core::widget::Tree,
676 layout: core::Layout<'_>,
677 cursor: core::mouse::Cursor,
678 viewport: &core::Rectangle,
679 renderer: &Renderer,
680 ) -> core::mouse::Interaction {
681 let interaction = self
682 .content
683 .as_widget()
684 .mouse_interaction(state, layout, cursor, viewport, renderer);
685
686 if interaction == mouse::Interaction::None
687 && cursor.is_over(layout.bounds())
688 {
689 mouse::Interaction::Idle
690 } else {
691 interaction
692 }
693 }
694
695 fn overlay<'b>(
696 &'b mut self,
697 state: &'b mut core::widget::Tree,
698 layout: core::Layout<'b>,
699 renderer: &Renderer,
700 viewport: &Rectangle,
701 translation: core::Vector,
702 ) -> Option<core::overlay::Element<'b, Message, Theme, Renderer>>
703 {
704 self.content.as_widget_mut().overlay(
705 state,
706 layout,
707 renderer,
708 viewport,
709 translation,
710 )
711 }
712 }
713
714 Element::new(Opaque {
715 content: content.into(),
716 })
717}
718
719/// Displays a widget on top of another one, only when the base widget is hovered.
720///
721/// This works analogously to a [`stack`], but it will only display the layer on top
722/// when the cursor is over the base. It can be useful for removing visual clutter.
723///
724/// [`stack`]: stack()
725pub fn hover<'a, Message, Theme, Renderer>(
726 base: impl Into<Element<'a, Message, Theme, Renderer>>,
727 top: impl Into<Element<'a, Message, Theme, Renderer>>,
728) -> Element<'a, Message, Theme, Renderer>
729where
730 Message: 'a,
731 Theme: 'a,
732 Renderer: core::Renderer + 'a,
733{
734 use crate::core::layout::{self, Layout};
735 use crate::core::mouse;
736 use crate::core::renderer;
737 use crate::core::widget::tree::{self, Tree};
738 use crate::core::{Event, Rectangle, Shell, Size};
739
740 struct Hover<'a, Message, Theme, Renderer> {
741 base: Element<'a, Message, Theme, Renderer>,
742 top: Element<'a, Message, Theme, Renderer>,
743 is_top_focused: bool,
744 is_top_overlay_active: bool,
745 is_hovered: bool,
746 }
747
748 impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
749 for Hover<'_, Message, Theme, Renderer>
750 where
751 Renderer: core::Renderer,
752 {
753 fn tag(&self) -> tree::Tag {
754 struct Tag;
755 tree::Tag::of::<Tag>()
756 }
757
758 fn children(&self) -> Vec<Tree> {
759 vec![Tree::new(&self.base), Tree::new(&self.top)]
760 }
761
762 fn diff(&self, tree: &mut Tree) {
763 tree.diff_children(&[&self.base, &self.top]);
764 }
765
766 fn size(&self) -> Size<Length> {
767 self.base.as_widget().size()
768 }
769
770 fn size_hint(&self) -> Size<Length> {
771 self.base.as_widget().size_hint()
772 }
773
774 fn layout(
775 &mut self,
776 tree: &mut Tree,
777 renderer: &Renderer,
778 limits: &layout::Limits,
779 ) -> layout::Node {
780 let base = self.base.as_widget_mut().layout(
781 &mut tree.children[0],
782 renderer,
783 limits,
784 );
785
786 let top = self.top.as_widget_mut().layout(
787 &mut tree.children[1],
788 renderer,
789 &layout::Limits::new(Size::ZERO, base.size()),
790 );
791
792 layout::Node::with_children(base.size(), vec![base, top])
793 }
794
795 fn draw(
796 &self,
797 tree: &Tree,
798 renderer: &mut Renderer,
799 theme: &Theme,
800 style: &renderer::Style,
801 layout: Layout<'_>,
802 cursor: mouse::Cursor,
803 viewport: &Rectangle,
804 ) {
805 if let Some(bounds) = layout.bounds().intersection(viewport) {
806 let mut children = layout.children().zip(&tree.children);
807
808 let (base_layout, base_tree) = children.next().unwrap();
809
810 self.base.as_widget().draw(
811 base_tree,
812 renderer,
813 theme,
814 style,
815 base_layout,
816 cursor,
817 viewport,
818 );
819
820 if cursor.is_over(layout.bounds())
821 || self.is_top_focused
822 || self.is_top_overlay_active
823 {
824 let (top_layout, top_tree) = children.next().unwrap();
825
826 renderer.with_layer(bounds, |renderer| {
827 self.top.as_widget().draw(
828 top_tree, renderer, theme, style, top_layout,
829 cursor, viewport,
830 );
831 });
832 }
833 }
834 }
835
836 fn operate(
837 &mut self,
838 tree: &mut Tree,
839 layout: Layout<'_>,
840 renderer: &Renderer,
841 operation: &mut dyn operation::Operation,
842 ) {
843 let children = [&mut self.base, &mut self.top]
844 .into_iter()
845 .zip(layout.children().zip(&mut tree.children));
846
847 for (child, (layout, tree)) in children {
848 child
849 .as_widget_mut()
850 .operate(tree, layout, renderer, operation);
851 }
852 }
853
854 fn update(
855 &mut self,
856 tree: &mut Tree,
857 event: &Event,
858 layout: Layout<'_>,
859 cursor: mouse::Cursor,
860 renderer: &Renderer,
861 clipboard: &mut dyn core::Clipboard,
862 shell: &mut Shell<'_, Message>,
863 viewport: &Rectangle,
864 ) {
865 let mut children = layout.children().zip(&mut tree.children);
866 let (base_layout, base_tree) = children.next().unwrap();
867 let (top_layout, top_tree) = children.next().unwrap();
868
869 let is_hovered = cursor.is_over(layout.bounds());
870
871 if matches!(event, Event::Window(window::Event::RedrawRequested(_)))
872 {
873 let mut count_focused = operation::focusable::count();
874
875 self.top.as_widget_mut().operate(
876 top_tree,
877 top_layout,
878 renderer,
879 &mut operation::black_box(&mut count_focused),
880 );
881
882 self.is_top_focused = match count_focused.finish() {
883 operation::Outcome::Some(count) => count.focused.is_some(),
884 _ => false,
885 };
886
887 self.is_hovered = is_hovered;
888 } else if is_hovered != self.is_hovered {
889 shell.request_redraw();
890 }
891
892 let is_visible =
893 is_hovered || self.is_top_focused || self.is_top_overlay_active;
894
895 if matches!(
896 event,
897 Event::Mouse(
898 mouse::Event::CursorMoved { .. }
899 | mouse::Event::ButtonReleased(_)
900 )
901 ) || is_visible
902 {
903 let redraw_request = shell.redraw_request();
904
905 self.top.as_widget_mut().update(
906 top_tree, event, top_layout, cursor, renderer, clipboard,
907 shell, viewport,
908 );
909
910 // Ignore redraw requests of invisible content
911 if !is_visible {
912 Shell::replace_redraw_request(shell, redraw_request);
913 }
914 };
915
916 if shell.is_event_captured() {
917 return;
918 }
919
920 self.base.as_widget_mut().update(
921 base_tree,
922 event,
923 base_layout,
924 cursor,
925 renderer,
926 clipboard,
927 shell,
928 viewport,
929 );
930 }
931
932 fn mouse_interaction(
933 &self,
934 tree: &Tree,
935 layout: Layout<'_>,
936 cursor: mouse::Cursor,
937 viewport: &Rectangle,
938 renderer: &Renderer,
939 ) -> mouse::Interaction {
940 [&self.base, &self.top]
941 .into_iter()
942 .rev()
943 .zip(layout.children().rev().zip(tree.children.iter().rev()))
944 .map(|(child, (layout, tree))| {
945 child.as_widget().mouse_interaction(
946 tree, layout, cursor, viewport, renderer,
947 )
948 })
949 .find(|&interaction| interaction != mouse::Interaction::None)
950 .unwrap_or_default()
951 }
952
953 fn overlay<'b>(
954 &'b mut self,
955 tree: &'b mut core::widget::Tree,
956 layout: core::Layout<'b>,
957 renderer: &Renderer,
958 viewport: &Rectangle,
959 translation: core::Vector,
960 ) -> Option<core::overlay::Element<'b, Message, Theme, Renderer>>
961 {
962 let mut overlays = [&mut self.base, &mut self.top]
963 .into_iter()
964 .zip(layout.children().zip(tree.children.iter_mut()))
965 .map(|(child, (layout, tree))| {
966 child.as_widget_mut().overlay(
967 tree,
968 layout,
969 renderer,
970 viewport,
971 translation,
972 )
973 });
974
975 if let Some(base_overlay) = overlays.next()? {
976 return Some(base_overlay);
977 }
978
979 let top_overlay = overlays.next()?;
980 self.is_top_overlay_active = top_overlay.is_some();
981
982 top_overlay
983 }
984 }
985
986 Element::new(Hover {
987 base: base.into(),
988 top: top.into(),
989 is_top_focused: false,
990 is_top_overlay_active: false,
991 is_hovered: false,
992 })
993}
994
995/// Creates a new [`Sensor`] widget.
996///
997/// A [`Sensor`] widget can generate messages when its contents are shown,
998/// hidden, or resized.
999///
1000/// It can even notify you with anticipation at a given distance!
1001pub fn sensor<'a, Message, Theme, Renderer>(
1002 content: impl Into<Element<'a, Message, Theme, Renderer>>,
1003) -> Sensor<'a, (), Message, Theme, Renderer>
1004where
1005 Renderer: core::Renderer,
1006{
1007 Sensor::new(content)
1008}
1009
1010/// Creates a new [`Scrollable`] with the provided content.
1011///
1012/// Scrollables let users navigate an endless amount of content with a scrollbar.
1013///
1014/// # Example
1015/// ```no_run
1016/// # mod iced { pub mod widget { pub use iced_widget::*; } }
1017/// # pub type State = ();
1018/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1019/// use iced::widget::{column, scrollable, space};
1020///
1021/// enum Message {
1022/// // ...
1023/// }
1024///
1025/// fn view(state: &State) -> Element<'_, Message> {
1026/// scrollable(column![
1027/// "Scroll me!",
1028/// space().height(3000),
1029/// "You did it!",
1030/// ]).into()
1031/// }
1032/// ```
1033pub fn scrollable<'a, Message, Theme, Renderer>(
1034 content: impl Into<Element<'a, Message, Theme, Renderer>>,
1035) -> Scrollable<'a, Message, Theme, Renderer>
1036where
1037 Theme: scrollable::Catalog + 'a,
1038 Renderer: core::Renderer,
1039{
1040 Scrollable::new(content)
1041}
1042
1043/// Creates a new [`Button`] with the provided content.
1044///
1045/// # Example
1046/// ```no_run
1047/// # mod iced { pub mod widget { pub use iced_widget::*; } }
1048/// # pub type State = ();
1049/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1050/// use iced::widget::button;
1051///
1052/// #[derive(Clone)]
1053/// enum Message {
1054/// ButtonPressed,
1055/// }
1056///
1057/// fn view(state: &State) -> Element<'_, Message> {
1058/// button("Press me!").on_press(Message::ButtonPressed).into()
1059/// }
1060/// ```
1061pub fn button<'a, Message, Theme, Renderer>(
1062 content: impl Into<Element<'a, Message, Theme, Renderer>>,
1063) -> Button<'a, Message, Theme, Renderer>
1064where
1065 Theme: button::Catalog + 'a,
1066 Renderer: core::Renderer,
1067{
1068 Button::new(content)
1069}
1070
1071/// Creates a new [`Tooltip`] for the provided content with the given
1072/// [`Element`] and [`tooltip::Position`].
1073///
1074/// Tooltips display a hint of information over some element when hovered.
1075///
1076/// # Example
1077/// ```no_run
1078/// # mod iced { pub mod widget { pub use iced_widget::*; } }
1079/// # pub type State = ();
1080/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1081/// use iced::widget::{container, tooltip};
1082///
1083/// enum Message {
1084/// // ...
1085/// }
1086///
1087/// fn view(_state: &State) -> Element<'_, Message> {
1088/// tooltip(
1089/// "Hover me to display the tooltip!",
1090/// container("This is the tooltip contents!")
1091/// .padding(10)
1092/// .style(container::rounded_box),
1093/// tooltip::Position::Bottom,
1094/// ).into()
1095/// }
1096/// ```
1097pub fn tooltip<'a, Message, Theme, Renderer>(
1098 content: impl Into<Element<'a, Message, Theme, Renderer>>,
1099 tooltip: impl Into<Element<'a, Message, Theme, Renderer>>,
1100 position: tooltip::Position,
1101) -> crate::Tooltip<'a, Message, Theme, Renderer>
1102where
1103 Theme: container::Catalog + 'a,
1104 Renderer: core::text::Renderer,
1105{
1106 Tooltip::new(content, tooltip, position)
1107}
1108
1109/// Creates a new [`Text`] widget with the provided content.
1110///
1111/// # Example
1112/// ```no_run
1113/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1114/// # pub type State = ();
1115/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::core::Theme, ()>;
1116/// use iced::widget::text;
1117/// use iced::color;
1118///
1119/// enum Message {
1120/// // ...
1121/// }
1122///
1123/// fn view(state: &State) -> Element<'_, Message> {
1124/// text("Hello, this is iced!")
1125/// .size(20)
1126/// .color(color!(0x0000ff))
1127/// .into()
1128/// }
1129/// ```
1130pub fn text<'a, Theme, Renderer>(
1131 text: impl text::IntoFragment<'a>,
1132) -> Text<'a, Theme, Renderer>
1133where
1134 Theme: text::Catalog + 'a,
1135 Renderer: core::text::Renderer,
1136{
1137 Text::new(text)
1138}
1139
1140/// Creates a new [`Text`] widget that displays the provided value.
1141pub fn value<'a, Theme, Renderer>(
1142 value: impl ToString,
1143) -> Text<'a, Theme, Renderer>
1144where
1145 Theme: text::Catalog + 'a,
1146 Renderer: core::text::Renderer,
1147{
1148 Text::new(value.to_string())
1149}
1150
1151/// Creates a new [`Rich`] text widget with the provided spans.
1152///
1153/// [`Rich`]: text::Rich
1154///
1155/// # Example
1156/// ```no_run
1157/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; }
1158/// # pub type State = ();
1159/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1160/// use iced::font;
1161/// use iced::widget::{rich_text, span};
1162/// use iced::{color, never, Font};
1163///
1164/// #[derive(Debug, Clone)]
1165/// enum Message {
1166/// LinkClicked(&'static str),
1167/// // ...
1168/// }
1169///
1170/// fn view(state: &State) -> Element<'_, Message> {
1171/// rich_text([
1172/// span("I am red!").color(color!(0xff0000)),
1173/// span(" "),
1174/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }),
1175/// ])
1176/// .on_link_click(never)
1177/// .size(20)
1178/// .into()
1179/// }
1180/// ```
1181pub fn rich_text<'a, Link, Message, Theme, Renderer>(
1182 spans: impl AsRef<[text::Span<'a, Link, Renderer::Font>]> + 'a,
1183) -> text::Rich<'a, Link, Message, Theme, Renderer>
1184where
1185 Link: Clone + 'static,
1186 Theme: text::Catalog + 'a,
1187 Renderer: core::text::Renderer,
1188 Renderer::Font: 'a,
1189{
1190 text::Rich::with_spans(spans)
1191}
1192
1193/// Creates a new [`Span`] of text with the provided content.
1194///
1195/// A [`Span`] is a fragment of some [`Rich`] text.
1196///
1197/// [`Span`]: text::Span
1198/// [`Rich`]: text::Rich
1199///
1200/// # Example
1201/// ```no_run
1202/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; }
1203/// # pub type State = ();
1204/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1205/// use iced::font;
1206/// use iced::widget::{rich_text, span};
1207/// use iced::{color, never, Font};
1208///
1209/// #[derive(Debug, Clone)]
1210/// enum Message {
1211/// // ...
1212/// }
1213///
1214/// fn view(state: &State) -> Element<'_, Message> {
1215/// rich_text![
1216/// span("I am red!").color(color!(0xff0000)),
1217/// " ",
1218/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }),
1219/// ]
1220/// .on_link_click(never)
1221/// .size(20)
1222/// .into()
1223/// }
1224/// ```
1225pub fn span<'a, Link, Font>(
1226 text: impl text::IntoFragment<'a>,
1227) -> text::Span<'a, Link, Font> {
1228 text::Span::new(text)
1229}
1230
1231#[cfg(feature = "markdown")]
1232#[doc(inline)]
1233pub use crate::markdown::view as markdown;
1234
1235/// Creates a new [`Checkbox`].
1236///
1237/// # Example
1238/// ```no_run
1239/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1240/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1241/// #
1242/// use iced::widget::checkbox;
1243///
1244/// struct State {
1245/// is_checked: bool,
1246/// }
1247///
1248/// enum Message {
1249/// CheckboxToggled(bool),
1250/// }
1251///
1252/// fn view(state: &State) -> Element<'_, Message> {
1253/// checkbox("Toggle me!", state.is_checked)
1254/// .on_toggle(Message::CheckboxToggled)
1255/// .into()
1256/// }
1257///
1258/// fn update(state: &mut State, message: Message) {
1259/// match message {
1260/// Message::CheckboxToggled(is_checked) => {
1261/// state.is_checked = is_checked;
1262/// }
1263/// }
1264/// }
1265/// ```
1266/// 
1267pub fn checkbox<'a, Message, Theme, Renderer>(
1268 label: impl Into<String>,
1269 is_checked: bool,
1270) -> Checkbox<'a, Message, Theme, Renderer>
1271where
1272 Theme: checkbox::Catalog + 'a,
1273 Renderer: core::text::Renderer,
1274{
1275 Checkbox::new(label, is_checked)
1276}
1277
1278/// Creates a new [`Radio`].
1279///
1280/// Radio buttons let users choose a single option from a bunch of options.
1281///
1282/// # Example
1283/// ```no_run
1284/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1285/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1286/// #
1287/// use iced::widget::{column, radio};
1288///
1289/// struct State {
1290/// selection: Option<Choice>,
1291/// }
1292///
1293/// #[derive(Debug, Clone, Copy)]
1294/// enum Message {
1295/// RadioSelected(Choice),
1296/// }
1297///
1298/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1299/// enum Choice {
1300/// A,
1301/// B,
1302/// C,
1303/// All,
1304/// }
1305///
1306/// fn view(state: &State) -> Element<'_, Message> {
1307/// let a = radio(
1308/// "A",
1309/// Choice::A,
1310/// state.selection,
1311/// Message::RadioSelected,
1312/// );
1313///
1314/// let b = radio(
1315/// "B",
1316/// Choice::B,
1317/// state.selection,
1318/// Message::RadioSelected,
1319/// );
1320///
1321/// let c = radio(
1322/// "C",
1323/// Choice::C,
1324/// state.selection,
1325/// Message::RadioSelected,
1326/// );
1327///
1328/// let all = radio(
1329/// "All of the above",
1330/// Choice::All,
1331/// state.selection,
1332/// Message::RadioSelected
1333/// );
1334///
1335/// column![a, b, c, all].into()
1336/// }
1337/// ```
1338pub fn radio<'a, Message, Theme, Renderer, V>(
1339 label: impl Into<String>,
1340 value: V,
1341 selected: Option<V>,
1342 on_click: impl FnOnce(V) -> Message,
1343) -> Radio<'a, Message, Theme, Renderer>
1344where
1345 Message: Clone,
1346 Theme: radio::Catalog + 'a,
1347 Renderer: core::text::Renderer,
1348 V: Copy + Eq,
1349{
1350 Radio::new(label, value, selected, on_click)
1351}
1352
1353/// Creates a new [`Toggler`].
1354///
1355/// Togglers let users make binary choices by toggling a switch.
1356///
1357/// # Example
1358/// ```no_run
1359/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1360/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1361/// #
1362/// use iced::widget::toggler;
1363///
1364/// struct State {
1365/// is_checked: bool,
1366/// }
1367///
1368/// enum Message {
1369/// TogglerToggled(bool),
1370/// }
1371///
1372/// fn view(state: &State) -> Element<'_, Message> {
1373/// toggler(state.is_checked)
1374/// .label("Toggle me!")
1375/// .on_toggle(Message::TogglerToggled)
1376/// .into()
1377/// }
1378///
1379/// fn update(state: &mut State, message: Message) {
1380/// match message {
1381/// Message::TogglerToggled(is_checked) => {
1382/// state.is_checked = is_checked;
1383/// }
1384/// }
1385/// }
1386/// ```
1387pub fn toggler<'a, Message, Theme, Renderer>(
1388 is_checked: bool,
1389) -> Toggler<'a, Message, Theme, Renderer>
1390where
1391 Theme: toggler::Catalog + 'a,
1392 Renderer: core::text::Renderer,
1393{
1394 Toggler::new(is_checked)
1395}
1396
1397/// Creates a new [`TextInput`].
1398///
1399/// Text inputs display fields that can be filled with text.
1400///
1401/// # Example
1402/// ```no_run
1403/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1404/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1405/// #
1406/// use iced::widget::text_input;
1407///
1408/// struct State {
1409/// content: String,
1410/// }
1411///
1412/// #[derive(Debug, Clone)]
1413/// enum Message {
1414/// ContentChanged(String)
1415/// }
1416///
1417/// fn view(state: &State) -> Element<'_, Message> {
1418/// text_input("Type something here...", &state.content)
1419/// .on_input(Message::ContentChanged)
1420/// .into()
1421/// }
1422///
1423/// fn update(state: &mut State, message: Message) {
1424/// match message {
1425/// Message::ContentChanged(content) => {
1426/// state.content = content;
1427/// }
1428/// }
1429/// }
1430/// ```
1431pub fn text_input<'a, Message, Theme, Renderer>(
1432 placeholder: &str,
1433 value: &str,
1434) -> TextInput<'a, Message, Theme, Renderer>
1435where
1436 Message: Clone,
1437 Theme: text_input::Catalog + 'a,
1438 Renderer: core::text::Renderer,
1439{
1440 TextInput::new(placeholder, value)
1441}
1442
1443/// Creates a new [`TextEditor`].
1444///
1445/// Text editors display a multi-line text input for text editing.
1446///
1447/// # Example
1448/// ```no_run
1449/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1450/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1451/// #
1452/// use iced::widget::text_editor;
1453///
1454/// struct State {
1455/// content: text_editor::Content,
1456/// }
1457///
1458/// #[derive(Debug, Clone)]
1459/// enum Message {
1460/// Edit(text_editor::Action)
1461/// }
1462///
1463/// fn view(state: &State) -> Element<'_, Message> {
1464/// text_editor(&state.content)
1465/// .placeholder("Type something here...")
1466/// .on_action(Message::Edit)
1467/// .into()
1468/// }
1469///
1470/// fn update(state: &mut State, message: Message) {
1471/// match message {
1472/// Message::Edit(action) => {
1473/// state.content.perform(action);
1474/// }
1475/// }
1476/// }
1477/// ```
1478pub fn text_editor<'a, Message, Theme, Renderer>(
1479 content: &'a text_editor::Content<Renderer>,
1480) -> TextEditor<'a, core::text::highlighter::PlainText, Message, Theme, Renderer>
1481where
1482 Message: Clone,
1483 Theme: text_editor::Catalog + 'a,
1484 Renderer: core::text::Renderer,
1485{
1486 TextEditor::new(content)
1487}
1488
1489/// Creates a new [`Slider`].
1490///
1491/// Sliders let users set a value by moving an indicator.
1492///
1493/// # Example
1494/// ```no_run
1495/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1496/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1497/// #
1498/// use iced::widget::slider;
1499///
1500/// struct State {
1501/// value: f32,
1502/// }
1503///
1504/// #[derive(Debug, Clone)]
1505/// enum Message {
1506/// ValueChanged(f32),
1507/// }
1508///
1509/// fn view(state: &State) -> Element<'_, Message> {
1510/// slider(0.0..=100.0, state.value, Message::ValueChanged).into()
1511/// }
1512///
1513/// fn update(state: &mut State, message: Message) {
1514/// match message {
1515/// Message::ValueChanged(value) => {
1516/// state.value = value;
1517/// }
1518/// }
1519/// }
1520/// ```
1521pub fn slider<'a, T, Message, Theme>(
1522 range: std::ops::RangeInclusive<T>,
1523 value: T,
1524 on_change: impl Fn(T) -> Message + 'a,
1525) -> Slider<'a, T, Message, Theme>
1526where
1527 T: Copy + From<u8> + std::cmp::PartialOrd,
1528 Message: Clone,
1529 Theme: slider::Catalog + 'a,
1530{
1531 Slider::new(range, value, on_change)
1532}
1533
1534/// Creates a new [`VerticalSlider`].
1535///
1536/// Sliders let users set a value by moving an indicator.
1537///
1538/// # Example
1539/// ```no_run
1540/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1541/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1542/// #
1543/// use iced::widget::vertical_slider;
1544///
1545/// struct State {
1546/// value: f32,
1547/// }
1548///
1549/// #[derive(Debug, Clone)]
1550/// enum Message {
1551/// ValueChanged(f32),
1552/// }
1553///
1554/// fn view(state: &State) -> Element<'_, Message> {
1555/// vertical_slider(0.0..=100.0, state.value, Message::ValueChanged).into()
1556/// }
1557///
1558/// fn update(state: &mut State, message: Message) {
1559/// match message {
1560/// Message::ValueChanged(value) => {
1561/// state.value = value;
1562/// }
1563/// }
1564/// }
1565/// ```
1566pub fn vertical_slider<'a, T, Message, Theme>(
1567 range: std::ops::RangeInclusive<T>,
1568 value: T,
1569 on_change: impl Fn(T) -> Message + 'a,
1570) -> VerticalSlider<'a, T, Message, Theme>
1571where
1572 T: Copy + From<u8> + std::cmp::PartialOrd,
1573 Message: Clone,
1574 Theme: vertical_slider::Catalog + 'a,
1575{
1576 VerticalSlider::new(range, value, on_change)
1577}
1578
1579/// Creates a new [`PickList`].
1580///
1581/// Pick lists display a dropdown list of selectable options.
1582///
1583/// # Example
1584/// ```no_run
1585/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1586/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1587/// #
1588/// use iced::widget::pick_list;
1589///
1590/// struct State {
1591/// favorite: Option<Fruit>,
1592/// }
1593///
1594/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1595/// enum Fruit {
1596/// Apple,
1597/// Orange,
1598/// Strawberry,
1599/// Tomato,
1600/// }
1601///
1602/// #[derive(Debug, Clone)]
1603/// enum Message {
1604/// FruitSelected(Fruit),
1605/// }
1606///
1607/// fn view(state: &State) -> Element<'_, Message> {
1608/// let fruits = [
1609/// Fruit::Apple,
1610/// Fruit::Orange,
1611/// Fruit::Strawberry,
1612/// Fruit::Tomato,
1613/// ];
1614///
1615/// pick_list(
1616/// fruits,
1617/// state.favorite,
1618/// Message::FruitSelected,
1619/// )
1620/// .placeholder("Select your favorite fruit...")
1621/// .into()
1622/// }
1623///
1624/// fn update(state: &mut State, message: Message) {
1625/// match message {
1626/// Message::FruitSelected(fruit) => {
1627/// state.favorite = Some(fruit);
1628/// }
1629/// }
1630/// }
1631///
1632/// impl std::fmt::Display for Fruit {
1633/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1634/// f.write_str(match self {
1635/// Self::Apple => "Apple",
1636/// Self::Orange => "Orange",
1637/// Self::Strawberry => "Strawberry",
1638/// Self::Tomato => "Tomato",
1639/// })
1640/// }
1641/// }
1642/// ```
1643pub fn pick_list<'a, T, L, V, Message, Theme, Renderer>(
1644 options: L,
1645 selected: Option<V>,
1646 on_selected: impl Fn(T) -> Message + 'a,
1647) -> PickList<'a, T, L, V, Message, Theme, Renderer>
1648where
1649 T: ToString + PartialEq + Clone + 'a,
1650 L: Borrow<[T]> + 'a,
1651 V: Borrow<T> + 'a,
1652 Message: Clone,
1653 Theme: pick_list::Catalog + overlay::menu::Catalog,
1654 Renderer: core::text::Renderer,
1655{
1656 PickList::new(options, selected, on_selected)
1657}
1658
1659/// Creates a new [`ComboBox`].
1660///
1661/// Combo boxes display a dropdown list of searchable and selectable options.
1662///
1663/// # Example
1664/// ```no_run
1665/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1666/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1667/// #
1668/// use iced::widget::combo_box;
1669///
1670/// struct State {
1671/// fruits: combo_box::State<Fruit>,
1672/// favorite: Option<Fruit>,
1673/// }
1674///
1675/// #[derive(Debug, Clone)]
1676/// enum Fruit {
1677/// Apple,
1678/// Orange,
1679/// Strawberry,
1680/// Tomato,
1681/// }
1682///
1683/// #[derive(Debug, Clone)]
1684/// enum Message {
1685/// FruitSelected(Fruit),
1686/// }
1687///
1688/// fn view(state: &State) -> Element<'_, Message> {
1689/// combo_box(
1690/// &state.fruits,
1691/// "Select your favorite fruit...",
1692/// state.favorite.as_ref(),
1693/// Message::FruitSelected
1694/// )
1695/// .into()
1696/// }
1697///
1698/// fn update(state: &mut State, message: Message) {
1699/// match message {
1700/// Message::FruitSelected(fruit) => {
1701/// state.favorite = Some(fruit);
1702/// }
1703/// }
1704/// }
1705///
1706/// impl std::fmt::Display for Fruit {
1707/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1708/// f.write_str(match self {
1709/// Self::Apple => "Apple",
1710/// Self::Orange => "Orange",
1711/// Self::Strawberry => "Strawberry",
1712/// Self::Tomato => "Tomato",
1713/// })
1714/// }
1715/// }
1716/// ```
1717pub fn combo_box<'a, T, Message, Theme, Renderer>(
1718 state: &'a combo_box::State<T>,
1719 placeholder: &str,
1720 selection: Option<&T>,
1721 on_selected: impl Fn(T) -> Message + 'static,
1722) -> ComboBox<'a, T, Message, Theme, Renderer>
1723where
1724 T: std::fmt::Display + Clone,
1725 Theme: combo_box::Catalog + 'a,
1726 Renderer: core::text::Renderer,
1727{
1728 ComboBox::new(state, placeholder, selection, on_selected)
1729}
1730
1731/// Creates some empty [`Space`] with no size.
1732///
1733/// This is considered the "identity" widget. It will take
1734/// no space and do nothing.
1735pub fn space() -> Space {
1736 Space::new()
1737}
1738
1739/// Creates a new [`ProgressBar`].
1740///
1741/// Progress bars visualize the progression of an extended computer operation, such as a download, file transfer, or installation.
1742///
1743/// It expects:
1744/// * an inclusive range of possible values, and
1745/// * the current value of the [`ProgressBar`].
1746///
1747/// # Example
1748/// ```no_run
1749/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1750/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1751/// #
1752/// use iced::widget::progress_bar;
1753///
1754/// struct State {
1755/// progress: f32,
1756/// }
1757///
1758/// enum Message {
1759/// // ...
1760/// }
1761///
1762/// fn view(state: &State) -> Element<'_, Message> {
1763/// progress_bar(0.0..=100.0, state.progress).into()
1764/// }
1765/// ```
1766pub fn progress_bar<'a, Theme>(
1767 range: RangeInclusive<f32>,
1768 value: f32,
1769) -> ProgressBar<'a, Theme>
1770where
1771 Theme: progress_bar::Catalog + 'a,
1772{
1773 ProgressBar::new(range, value)
1774}
1775
1776/// Creates a new [`Image`].
1777///
1778/// Images display raster graphics in different formats (PNG, JPG, etc.).
1779///
1780/// [`Image`]: crate::Image
1781///
1782/// # Example
1783/// ```no_run
1784/// # mod iced { pub mod widget { pub use iced_widget::*; } }
1785/// # pub type State = ();
1786/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1787/// use iced::widget::image;
1788///
1789/// enum Message {
1790/// // ...
1791/// }
1792///
1793/// fn view(state: &State) -> Element<'_, Message> {
1794/// image("ferris.png").into()
1795/// }
1796/// ```
1797/// <img src="https://github.com/iced-rs/iced/blob/9712b319bb7a32848001b96bd84977430f14b623/examples/resources/ferris.png?raw=true" width="300">
1798#[cfg(feature = "image")]
1799pub fn image<Handle>(handle: impl Into<Handle>) -> crate::Image<Handle> {
1800 crate::Image::new(handle.into())
1801}
1802
1803/// Creates a new [`Svg`] widget from the given [`Handle`].
1804///
1805/// Svg widgets display vector graphics in your application.
1806///
1807/// [`Svg`]: crate::Svg
1808/// [`Handle`]: crate::svg::Handle
1809///
1810/// # Example
1811/// ```no_run
1812/// # mod iced { pub mod widget { pub use iced_widget::*; } }
1813/// # pub type State = ();
1814/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1815/// use iced::widget::svg;
1816///
1817/// enum Message {
1818/// // ...
1819/// }
1820///
1821/// fn view(state: &State) -> Element<'_, Message> {
1822/// svg("tiger.svg").into()
1823/// }
1824/// ```
1825#[cfg(feature = "svg")]
1826pub fn svg<'a, Theme>(
1827 handle: impl Into<core::svg::Handle>,
1828) -> crate::Svg<'a, Theme>
1829where
1830 Theme: crate::svg::Catalog,
1831{
1832 crate::Svg::new(handle)
1833}
1834
1835/// Creates an [`Element`] that displays the iced logo with the given `text_size`.
1836///
1837/// Useful for showing some love to your favorite GUI library in your "About" screen,
1838/// for instance.
1839#[cfg(feature = "svg")]
1840pub fn iced<'a, Message, Theme, Renderer>(
1841 text_size: impl Into<core::Pixels>,
1842) -> Element<'a, Message, Theme, Renderer>
1843where
1844 Message: 'a,
1845 Renderer: core::Renderer
1846 + core::text::Renderer<Font = core::Font>
1847 + core::svg::Renderer
1848 + 'a,
1849 Theme: text::Catalog + crate::svg::Catalog + 'a,
1850{
1851 use crate::core::{Alignment, Font};
1852 use crate::svg;
1853 use std::sync::LazyLock;
1854
1855 static LOGO: LazyLock<svg::Handle> = LazyLock::new(|| {
1856 svg::Handle::from_memory(include_bytes!("../assets/iced-logo.svg"))
1857 });
1858
1859 let text_size = text_size.into();
1860
1861 row![
1862 svg(LOGO.clone()).width(text_size * 1.3),
1863 text("iced")
1864 .size(text_size)
1865 .font(Font::MONOSPACE)
1866 .shaping(text::Shaping::Advanced)
1867 ]
1868 .spacing(text_size.0 / 3.0)
1869 .align_y(Alignment::Center)
1870 .into()
1871}
1872
1873/// Creates a new [`Canvas`].
1874///
1875/// Canvases can be leveraged to draw interactive 2D graphics.
1876///
1877/// [`Canvas`]: crate::Canvas
1878///
1879/// # Example: Drawing a Simple Circle
1880/// ```no_run
1881/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1882/// # pub type State = ();
1883/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1884/// #
1885/// use iced::mouse;
1886/// use iced::widget::canvas;
1887/// use iced::{Color, Rectangle, Renderer, Theme};
1888///
1889/// // First, we define the data we need for drawing
1890/// #[derive(Debug)]
1891/// struct Circle {
1892/// radius: f32,
1893/// }
1894///
1895/// // Then, we implement the `Program` trait
1896/// impl<Message> canvas::Program<Message> for Circle {
1897/// // No internal state
1898/// type State = ();
1899///
1900/// fn draw(
1901/// &self,
1902/// _state: &(),
1903/// renderer: &Renderer,
1904/// _theme: &Theme,
1905/// bounds: Rectangle,
1906/// _cursor: mouse::Cursor
1907/// ) -> Vec<canvas::Geometry> {
1908/// // We prepare a new `Frame`
1909/// let mut frame = canvas::Frame::new(renderer, bounds.size());
1910///
1911/// // We create a `Path` representing a simple circle
1912/// let circle = canvas::Path::circle(frame.center(), self.radius);
1913///
1914/// // And fill it with some color
1915/// frame.fill(&circle, Color::BLACK);
1916///
1917/// // Then, we produce the geometry
1918/// vec![frame.into_geometry()]
1919/// }
1920/// }
1921///
1922/// // Finally, we simply use our `Circle` to create the `Canvas`!
1923/// fn view<'a, Message: 'a>(_state: &'a State) -> Element<'a, Message> {
1924/// canvas(Circle { radius: 50.0 }).into()
1925/// }
1926/// ```
1927#[cfg(feature = "canvas")]
1928pub fn canvas<P, Message, Theme, Renderer>(
1929 program: P,
1930) -> crate::Canvas<P, Message, Theme, Renderer>
1931where
1932 Renderer: crate::graphics::geometry::Renderer,
1933 P: crate::canvas::Program<Message, Theme, Renderer>,
1934{
1935 crate::Canvas::new(program)
1936}
1937
1938/// Creates a new [`QRCode`] widget from the given [`Data`].
1939///
1940/// QR codes display information in a type of two-dimensional matrix barcode.
1941///
1942/// [`QRCode`]: crate::QRCode
1943/// [`Data`]: crate::qr_code::Data
1944///
1945/// # Example
1946/// ```no_run
1947/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
1948/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
1949/// #
1950/// use iced::widget::qr_code;
1951///
1952/// struct State {
1953/// data: qr_code::Data,
1954/// }
1955///
1956/// #[derive(Debug, Clone)]
1957/// enum Message {
1958/// // ...
1959/// }
1960///
1961/// fn view(state: &State) -> Element<'_, Message> {
1962/// qr_code(&state.data).into()
1963/// }
1964/// ```
1965#[cfg(feature = "qr_code")]
1966pub fn qr_code<'a, Theme>(
1967 data: &'a crate::qr_code::Data,
1968) -> crate::QRCode<'a, Theme>
1969where
1970 Theme: crate::qr_code::Catalog + 'a,
1971{
1972 crate::QRCode::new(data)
1973}
1974
1975/// Creates a new [`Shader`].
1976///
1977/// [`Shader`]: crate::Shader
1978#[cfg(feature = "wgpu")]
1979pub fn shader<Message, P>(program: P) -> crate::Shader<Message, P>
1980where
1981 P: crate::shader::Program<Message>,
1982{
1983 crate::Shader::new(program)
1984}
1985
1986/// Creates a new [`MouseArea`].
1987pub fn mouse_area<'a, Message, Theme, Renderer>(
1988 widget: impl Into<Element<'a, Message, Theme, Renderer>>,
1989) -> MouseArea<'a, Message, Theme, Renderer>
1990where
1991 Renderer: core::Renderer,
1992{
1993 MouseArea::new(widget)
1994}
1995
1996/// A widget that applies any `Theme` to its contents.
1997pub fn themer<'a, Message, Theme, Renderer>(
1998 theme: Option<Theme>,
1999 content: impl Into<Element<'a, Message, Theme, Renderer>>,
2000) -> Themer<'a, Message, Theme, Renderer>
2001where
2002 Theme: theme::Base,
2003 Renderer: core::Renderer,
2004{
2005 Themer::new(theme, content)
2006}
2007
2008/// Creates a [`PaneGrid`] with the given [`pane_grid::State`] and view function.
2009///
2010/// Pane grids let your users split regions of your application and organize layout dynamically.
2011///
2012/// # Example
2013/// ```no_run
2014/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
2015/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
2016/// #
2017/// use iced::widget::{pane_grid, text};
2018///
2019/// struct State {
2020/// panes: pane_grid::State<Pane>,
2021/// }
2022///
2023/// enum Pane {
2024/// SomePane,
2025/// AnotherKindOfPane,
2026/// }
2027///
2028/// enum Message {
2029/// PaneDragged(pane_grid::DragEvent),
2030/// PaneResized(pane_grid::ResizeEvent),
2031/// }
2032///
2033/// fn view(state: &State) -> Element<'_, Message> {
2034/// pane_grid(&state.panes, |pane, state, is_maximized| {
2035/// pane_grid::Content::new(match state {
2036/// Pane::SomePane => text("This is some pane"),
2037/// Pane::AnotherKindOfPane => text("This is another kind of pane"),
2038/// })
2039/// })
2040/// .on_drag(Message::PaneDragged)
2041/// .on_resize(10, Message::PaneResized)
2042/// .into()
2043/// }
2044/// ```
2045pub fn pane_grid<'a, T, Message, Theme, Renderer>(
2046 state: &'a pane_grid::State<T>,
2047 view: impl Fn(
2048 pane_grid::Pane,
2049 &'a T,
2050 bool,
2051 ) -> pane_grid::Content<'a, Message, Theme, Renderer>,
2052) -> PaneGrid<'a, Message, Theme, Renderer>
2053where
2054 Theme: pane_grid::Catalog,
2055 Renderer: core::Renderer,
2056{
2057 PaneGrid::new(state, view)
2058}
2059
2060/// Creates a new [`Float`] widget with the given content.
2061pub fn float<'a, Message, Theme, Renderer>(
2062 content: impl Into<Element<'a, Message, Theme, Renderer>>,
2063) -> Float<'a, Message, Theme, Renderer>
2064where
2065 Theme: float::Catalog,
2066 Renderer: core::Renderer,
2067{
2068 Float::new(content)
2069}
2070
2071/// Creates a new [`Responsive`] widget with a closure that produces its
2072/// contents.
2073///
2074/// The `view` closure will receive the maximum available space for
2075/// the [`Responsive`] during layout. You can use this [`Size`] to
2076/// conditionally build the contents.
2077pub fn responsive<'a, Message, Theme, Renderer>(
2078 f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
2079) -> Responsive<'a, Message, Theme, Renderer>
2080where
2081 Renderer: core::Renderer,
2082{
2083 Responsive::new(f)
2084}