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