diff options
Diffstat (limited to 'examples')
36 files changed, 377 insertions, 1683 deletions
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 35b5182c..11e4828e 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -71,7 +71,7 @@ mod bezier { use iced::{ canvas::event::{self, Event}, canvas::{self, Canvas, Cursor, Frame, Geometry, Path, Stroke}, - mouse, Element, Length, Point, Rectangle, + mouse, Element, Length, Point, Rectangle, Theme, }; #[derive(Default)] @@ -158,7 +158,12 @@ mod bezier { } } - fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry> { + fn draw( + &self, + _theme: &Theme, + bounds: Rectangle, + cursor: Cursor, + ) -> Vec<Geometry> { let content = self.state.cache.draw(bounds.size(), |frame: &mut Frame| { Curve::draw_all(self.curves, frame); diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 3b8a1d6a..48b4cd7b 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,7 +1,10 @@ +use iced::canvas::{ + self, Cache, Canvas, Cursor, Geometry, LineCap, Path, Stroke, +}; +use iced::executor; use iced::{ - canvas::{self, Cache, Canvas, Cursor, Geometry, LineCap, Path, Stroke}, - executor, Application, Color, Command, Container, Element, Length, Point, - Rectangle, Settings, Subscription, Vector, + Application, Color, Command, Container, Element, Length, Point, Rectangle, + Settings, Subscription, Theme, Vector, }; pub fn main() -> iced::Result { @@ -22,8 +25,9 @@ enum Message { } impl Application for Clock { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Self, Command<Message>) { @@ -77,7 +81,12 @@ impl Application for Clock { } impl<Message> canvas::Program<Message> for Clock { - fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry> { + fn draw( + &self, + _theme: &Theme, + bounds: Rectangle, + _cursor: Cursor, + ) -> Vec<Geometry> { let clock = self.clock.draw(bounds.size(), |frame| { let center = frame.center(); let radius = frame.width().min(frame.height()) / 2.0; diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index f5fab251..16c87a75 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -236,7 +236,12 @@ impl Theme { } impl<Message> canvas::Program<Message> for Theme { - fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry> { + fn draw( + &self, + _theme: &iced::Theme, + bounds: Rectangle, + _cursor: Cursor, + ) -> Vec<Geometry> { let theme = self.canvas_cache.draw(bounds.size(), |frame| { self.draw(frame); }); @@ -288,7 +293,7 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> { range: RangeInclusive<f64>, component: f32, update: impl Fn(f32) -> C + 'static, - ) -> Slider<f64, C> { + ) -> Slider<f64, C, iced::Renderer> { Slider::new(state, range, f64::from(component), move |v| { update(v as f32) }) diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index 39335cf1..b6ff0600 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -54,7 +54,7 @@ mod numeric_input { use iced_native::text; use iced_native::widget::button::{self, Button}; use iced_native::widget::text_input::{self, TextInput}; - use iced_native::widget::{Row, Text}; + use iced_native::widget::{self, Row, Text}; use iced_native::{Element, Length}; pub struct NumericInput<'a, Message> { @@ -95,6 +95,9 @@ mod numeric_input { for NumericInput<'a, Message> where Renderer: 'a + text::Renderer, + Renderer::Theme: button::StyleSheet + + text_input::StyleSheet + + widget::text::StyleSheet, { type Event = Event; @@ -172,6 +175,9 @@ mod numeric_input { where Message: 'a, Renderer: text::Renderer + 'a, + Renderer::Theme: button::StyleSheet + + text_input::StyleSheet + + widget::text::StyleSheet, { fn from(numeric_input: NumericInput<'a, Message>) -> Self { component::view(numeric_input) diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 931cf5e1..e92f07f2 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -1,6 +1,5 @@ -use iced::{ - button, Alignment, Button, Column, Element, Sandbox, Settings, Text, -}; +use iced::button::{self, Button}; +use iced::{Alignment, Column, Element, Sandbox, Settings, Text}; pub fn main() -> iced::Result { Counter::run(Settings::default()) diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 28edf256..ce5306ba 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -46,6 +46,7 @@ mod circle { fn draw( &self, renderer: &mut Renderer, + _theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 21804a0a..4a801ba4 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -1,6 +1,8 @@ +use iced::button; +use iced::executor; use iced::{ - button, executor, Alignment, Application, Button, Column, Command, - Container, Element, Length, ProgressBar, Settings, Subscription, Text, + Alignment, Application, Button, Column, Command, Container, Element, + Length, ProgressBar, Settings, Subscription, Text, Theme, }; mod download; @@ -24,8 +26,9 @@ pub enum Message { } impl Application for Example { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Example, Command<Message>) { diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index 7f024c56..c87fbc72 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -1,6 +1,9 @@ +use iced::alignment; +use iced::button; +use iced::executor; use iced::{ - alignment, button, executor, Alignment, Application, Button, Checkbox, - Column, Command, Container, Element, Length, Settings, Subscription, Text, + Alignment, Application, Button, Checkbox, Column, Command, Container, + Element, Length, Settings, Subscription, Text, Theme, }; use iced_native::{window, Event}; @@ -27,8 +30,9 @@ enum Message { } impl Application for Events { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Events, Command<Message>) { diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index ab8b80e4..35399584 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -1,18 +1,18 @@ //! This example showcases an interactive version of the Game of Life, invented //! by John Conway. It leverages a `Canvas` together with other widgets. mod preset; -mod style; use grid::Grid; use iced::button::{self, Button}; use iced::executor; use iced::pick_list::{self, PickList}; use iced::slider::{self, Slider}; +use iced::theme::{self, Theme}; use iced::time; use iced::window; use iced::{ - Alignment, Application, Checkbox, Column, Command, Container, Element, - Length, Row, Settings, Subscription, Text, + Alignment, Application, Checkbox, Column, Command, Element, Length, Row, + Settings, Subscription, Text, }; use preset::Preset; use std::time::{Duration, Instant}; @@ -55,6 +55,7 @@ enum Message { impl Application for GameOfLife { type Message = Message; + type Theme = Theme; type Executor = executor::Default; type Flags = (); @@ -141,20 +142,19 @@ impl Application for GameOfLife { self.grid.preset(), ); - let content = Column::new() + Column::new() .push( self.grid .view() .map(move |message| Message::Grid(message, version)), ) - .push(controls); - - Container::new(content) - .width(Length::Fill) - .height(Length::Fill) - .style(style::Container) + .push(controls) .into() } + + fn theme(&self) -> Theme { + Theme::Dark + } } mod grid { @@ -163,7 +163,7 @@ mod grid { alignment, canvas::event::{self, Event}, canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text}, - mouse, Color, Element, Length, Point, Rectangle, Size, Vector, + mouse, Color, Element, Length, Point, Rectangle, Size, Theme, Vector, }; use rustc_hash::{FxHashMap, FxHashSet}; use std::future::Future; @@ -445,7 +445,12 @@ mod grid { } } - fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry> { + fn draw( + &self, + _theme: &Theme, + bounds: Rectangle, + cursor: Cursor, + ) -> Vec<Geometry> { let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0); let life = self.life_cache.draw(bounds.size(), |frame| { @@ -836,27 +841,24 @@ impl Controls { Text::new(if is_playing { "Pause" } else { "Play" }), ) .on_press(Message::TogglePlayback) - .style(style::Button), + .style(theme::Button::Primary), ) .push( Button::new(&mut self.next_button, Text::new("Next")) .on_press(Message::Next) - .style(style::Button), + .style(theme::Button::Secondary), ); let speed_controls = Row::new() .width(Length::Fill) .align_items(Alignment::Center) .spacing(10) - .push( - Slider::new( - &mut self.speed_slider, - 1.0..=1000.0, - speed as f32, - Message::SpeedChanged, - ) - .style(style::Slider), - ) + .push(Slider::new( + &mut self.speed_slider, + 1.0..=1000.0, + speed as f32, + Message::SpeedChanged, + )) .push(Text::new(format!("x{}", speed)).size(16)); Row::new() @@ -879,13 +881,12 @@ impl Controls { Message::PresetPicked, ) .padding(8) - .text_size(16) - .style(style::PickList), + .text_size(16), ) .push( Button::new(&mut self.clear_button, Text::new("Clear")) .on_press(Message::Clear) - .style(style::Clear), + .style(theme::Button::Destructive), ) .into() } diff --git a/examples/game_of_life/src/style.rs b/examples/game_of_life/src/style.rs deleted file mode 100644 index be9a0e96..00000000 --- a/examples/game_of_life/src/style.rs +++ /dev/null @@ -1,189 +0,0 @@ -use iced::{button, container, pick_list, slider, Background, Color}; - -const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, -); - -const DESTRUCTIVE: Color = Color::from_rgb( - 0xC0 as f32 / 255.0, - 0x47 as f32 / 255.0, - 0x47 as f32 / 255.0, -); - -const HOVERED: Color = Color::from_rgb( - 0x67 as f32 / 255.0, - 0x7B as f32 / 255.0, - 0xC4 as f32 / 255.0, -); - -const BACKGROUND: Color = Color::from_rgb( - 0x2F as f32 / 255.0, - 0x31 as f32 / 255.0, - 0x36 as f32 / 255.0, -); - -pub struct Container; - -impl container::StyleSheet for Container { - fn style(&self) -> container::Style { - container::Style { - background: Some(Background::Color(Color::from_rgb8( - 0x36, 0x39, 0x3F, - ))), - text_color: Some(Color::WHITE), - ..container::Style::default() - } - } -} - -pub struct Button; - -impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(ACTIVE)), - border_radius: 3.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - background: Some(Background::Color(HOVERED)), - text_color: Color::WHITE, - ..self.active() - } - } - - fn pressed(&self) -> button::Style { - button::Style { - border_width: 1.0, - border_color: Color::WHITE, - ..self.hovered() - } - } -} - -pub struct Clear; - -impl button::StyleSheet for Clear { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(DESTRUCTIVE)), - border_radius: 3.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - background: Some(Background::Color(Color { - a: 0.5, - ..DESTRUCTIVE - })), - text_color: Color::WHITE, - ..self.active() - } - } - - fn pressed(&self) -> button::Style { - button::Style { - border_width: 1.0, - border_color: Color::WHITE, - ..self.hovered() - } - } -} - -pub struct Slider; - -impl slider::StyleSheet for Slider { - fn active(&self) -> slider::Style { - slider::Style { - rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }), - handle: slider::Handle { - shape: slider::HandleShape::Circle { radius: 9.0 }, - color: ACTIVE, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: HOVERED, - ..active.handle - }, - ..active - } - } - - fn dragging(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: Color::from_rgb(0.85, 0.85, 0.85), - ..active.handle - }, - ..active - } - } -} - -pub struct PickList; - -impl pick_list::StyleSheet for PickList { - fn menu(&self) -> pick_list::Menu { - pick_list::Menu { - text_color: Color::WHITE, - background: BACKGROUND.into(), - border_width: 1.0, - border_color: Color { - a: 0.7, - ..Color::BLACK - }, - selected_background: Color { - a: 0.5, - ..Color::BLACK - } - .into(), - selected_text_color: Color::WHITE, - } - } - - fn active(&self) -> pick_list::Style { - pick_list::Style { - text_color: Color::WHITE, - background: BACKGROUND.into(), - border_width: 1.0, - border_color: Color { - a: 0.6, - ..Color::BLACK - }, - border_radius: 2.0, - icon_size: 0.5, - ..pick_list::Style::default() - } - } - - fn hovered(&self) -> pick_list::Style { - let active = self.active(); - - pick_list::Style { - border_color: Color { - a: 0.9, - ..Color::BLACK - }, - ..active - } - } -} diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 58dfa3ad..ba4b808e 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -25,7 +25,7 @@ mod rainbow { } } - impl<Message, B> Widget<Message, Renderer<B>> for Rainbow + impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow where B: Backend, { @@ -39,7 +39,7 @@ mod rainbow { fn layout( &self, - _renderer: &Renderer<B>, + _renderer: &Renderer<B, T>, limits: &layout::Limits, ) -> layout::Node { let size = limits.width(Length::Fill).resolve(Size::ZERO); @@ -49,7 +49,8 @@ mod rainbow { fn draw( &self, - renderer: &mut Renderer<B>, + renderer: &mut Renderer<B, T>, + _theme: &T, _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, @@ -147,11 +148,11 @@ mod rainbow { } } - impl<'a, Message, B> Into<Element<'a, Message, Renderer<B>>> for Rainbow + impl<'a, Message, B, T> Into<Element<'a, Message, Renderer<B, T>>> for Rainbow where B: Backend, { - fn into(self) -> Element<'a, Message, Renderer<B>> { + fn into(self) -> Element<'a, Message, Renderer<B, T>> { Element::new(self) } } diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs index f387b4e5..fdaa29d5 100644 --- a/examples/integration_opengl/src/controls.rs +++ b/examples/integration_opengl/src/controls.rs @@ -89,13 +89,13 @@ impl Program for Controls { .spacing(10) .push( Text::new("Background color") - .color(Color::WHITE), + .style(Color::WHITE), ) .push(sliders) .push( Text::new(format!("{:?}", background_color)) .size(14) - .color(Color::WHITE), + .style(Color::WHITE), ), ), ) diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs index 1007b90f..1a78a493 100644 --- a/examples/integration_opengl/src/main.rs +++ b/examples/integration_opengl/src/main.rs @@ -12,7 +12,8 @@ use iced_glow::glow; use iced_glow::{Backend, Renderer, Settings, Viewport}; use iced_glutin::conversion; use iced_glutin::glutin; -use iced_glutin::{program, Clipboard, Debug, Size}; +use iced_glutin::renderer; +use iced_glutin::{program, Clipboard, Color, Debug, Size}; pub fn main() { env_logger::init(); @@ -125,6 +126,10 @@ pub fn main() { viewport.scale_factor(), ), &mut renderer, + &iced_glow::Theme::Dark, + &renderer::Style { + text_color: Color::WHITE, + }, &mut clipboard, &mut debug, ); diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 9bca40eb..cb2c423f 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -100,13 +100,13 @@ impl Program for Controls { .spacing(10) .push( Text::new("Background color") - .color(Color::WHITE), + .style(Color::WHITE), ) .push(sliders) .push( Text::new(format!("{:?}", background_color)) .size(14) - .color(Color::WHITE), + .style(Color::WHITE), ) .push(TextInput::new( t, diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 89ae03c6..3d27a0f0 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -5,7 +5,10 @@ use controls::Controls; use scene::Scene; use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; -use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size}; +use iced_winit::{ + conversion, futures, program, renderer, winit, Clipboard, Color, Debug, + Size, +}; use winit::{ dpi::PhysicalPosition, @@ -188,6 +191,8 @@ pub fn main() { viewport.scale_factor(), ), &mut renderer, + &iced_wgpu::Theme::Dark, + &renderer::Style { text_color: Color::WHITE }, &mut clipboard, &mut debug, ); diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 2962ca25..5fbcea2c 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -4,6 +4,7 @@ use iced::executor; use iced::keyboard; use iced::pane_grid::{self, PaneGrid}; use iced::scrollable::{self, Scrollable}; +use iced::theme::{self, Theme}; use iced::{ Application, Color, Column, Command, Container, Element, Length, Row, Settings, Size, Subscription, Text, @@ -36,6 +37,7 @@ enum Message { impl Application for Example { type Message = Message; + type Theme = Theme; type Executor = executor::Default; type Flags = (); @@ -171,14 +173,14 @@ impl Application for Example { let text = if *is_pinned { "Unpin" } else { "Pin" }; let pin_button = Button::new(pin_button, Text::new(text).size(14)) .on_press(Message::TogglePin(id)) - .style(style::Button::Pin) + .style(theme::Button::Secondary) .padding(3); let title = Row::with_children(vec![ pin_button.into(), Text::new("Pane").into(), Text::new(content.id.to_string()) - .color(if is_focused { + .style(if is_focused { PANE_ID_COLOR_FOCUSED } else { PANE_ID_COLOR_UNFOCUSED @@ -191,9 +193,9 @@ impl Application for Example { .controls(pane.controls.view(id, total_panes, *is_pinned)) .padding(10) .style(if is_focused { - style::TitleBar::Focused + style::title_bar_focused } else { - style::TitleBar::Active + style::title_bar_active }); pane_grid::Content::new(Responsive::new(responsive, move |size| { @@ -201,9 +203,9 @@ impl Application for Example { })) .title_bar(title_bar) .style(if is_focused { - style::Pane::Focused + style::pane_focused } else { - style::Pane::Active + style::pane_active }) }) .width(Length::Fill) @@ -309,7 +311,7 @@ impl Content { .. } = self; - let button = |state, label, message, style| { + let button = |state, label, message| { Button::new( state, Text::new(label) @@ -320,7 +322,6 @@ impl Content { .width(Length::Fill) .padding(8) .on_press(message) - .style(style) }; let mut controls = Column::new() @@ -330,22 +331,18 @@ impl Content { split_horizontally, "Split horizontally", Message::Split(pane_grid::Axis::Horizontal, pane), - style::Button::Primary, )) .push(button( split_vertically, "Split vertically", Message::Split(pane_grid::Axis::Vertical, pane), - style::Button::Primary, )); if total_panes > 1 && !is_pinned { - controls = controls.push(button( - close, - "Close", - Message::Close(pane), - style::Button::Destructive, - )); + controls = controls.push( + button(close, "Close", Message::Close(pane)) + .style(theme::Button::Destructive), + ); } let content = Scrollable::new(scroll) @@ -379,8 +376,9 @@ impl Controls { ) -> Element<Message> { let mut button = Button::new(&mut self.close, Text::new("Close").size(14)) - .style(style::Button::Control) + .style(theme::Button::Destructive) .padding(3); + if total_panes > 1 && !is_pinned { button = button.on_press(Message::Close(pane)); } @@ -389,111 +387,47 @@ impl Controls { } mod style { - use crate::PANE_ID_COLOR_FOCUSED; - use iced::{button, container, Background, Color, Vector}; - - const SURFACE: Color = Color::from_rgb( - 0xF2 as f32 / 255.0, - 0xF3 as f32 / 255.0, - 0xF5 as f32 / 255.0, - ); - - const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, - ); - - const HOVERED: Color = Color::from_rgb( - 0x67 as f32 / 255.0, - 0x7B as f32 / 255.0, - 0xC4 as f32 / 255.0, - ); - - pub enum TitleBar { - Active, - Focused, - } + use iced::{container, Theme}; - impl container::StyleSheet for TitleBar { - fn style(&self) -> container::Style { - let pane = match self { - Self::Active => Pane::Active, - Self::Focused => Pane::Focused, - } - .style(); + pub fn title_bar_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - container::Style { - text_color: Some(Color::WHITE), - background: Some(pane.border_color.into()), - ..Default::default() - } + container::Appearance { + text_color: Some(palette.background.strong.text), + background: Some(palette.background.strong.color.into()), + ..Default::default() } } - pub enum Pane { - Active, - Focused, - } + pub fn title_bar_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - impl container::StyleSheet for Pane { - fn style(&self) -> container::Style { - container::Style { - background: Some(Background::Color(SURFACE)), - border_width: 2.0, - border_color: match self { - Self::Active => Color::from_rgb(0.7, 0.7, 0.7), - Self::Focused => Color::BLACK, - }, - ..Default::default() - } + container::Appearance { + text_color: Some(palette.primary.strong.text), + background: Some(palette.primary.strong.color.into()), + ..Default::default() } } - pub enum Button { - Primary, - Destructive, - Control, - Pin, - } + pub fn pane_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - let (background, text_color) = match self { - Button::Primary => (Some(ACTIVE), Color::WHITE), - Button::Destructive => { - (None, Color::from_rgb8(0xFF, 0x47, 0x47)) - } - Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE), - Button::Pin => (Some(ACTIVE), Color::WHITE), - }; - - button::Style { - text_color, - background: background.map(Background::Color), - border_radius: 5.0, - shadow_offset: Vector::new(0.0, 0.0), - ..button::Style::default() - } + container::Appearance { + background: Some(palette.background.weak.color.into()), + border_width: 2.0, + border_color: palette.background.strong.color, + ..Default::default() } + } - fn hovered(&self) -> button::Style { - let active = self.active(); - - let background = match self { - Button::Primary => Some(HOVERED), - Button::Destructive => Some(Color { - a: 0.2, - ..active.text_color - }), - Button::Control => Some(PANE_ID_COLOR_FOCUSED), - Button::Pin => Some(HOVERED), - }; - - button::Style { - background: background.map(Background::Color), - ..active - } + pub fn pane_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border_width: 2.0, + border_color: palette.primary.strong.color, + ..Default::default() } } } diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 85c26987..89d865e4 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -1,6 +1,9 @@ +use iced::button; +use iced::futures; +use iced::image; use iced::{ - button, futures, image, Alignment, Application, Button, Column, Command, - Container, Element, Length, Row, Settings, Text, + Alignment, Application, Button, Color, Column, Command, Container, Element, + Length, Row, Settings, Text, Theme, }; pub fn main() -> iced::Result { @@ -26,8 +29,9 @@ enum Message { } impl Application for Pokedex { - type Executor = iced::executor::Default; type Message = Message; + type Theme = Theme; + type Executor = iced::executor::Default; type Flags = (); fn new(_flags: ()) -> (Pokedex, Command<Message>) { @@ -139,7 +143,7 @@ impl Pokemon { .push( Text::new(format!("#{}", self.number)) .size(20) - .color([0.5, 0.5, 0.5]), + .style(Color::from([0.5, 0.5, 0.5])), ), ) .push(Text::new(&self.description)), @@ -238,29 +242,5 @@ impl From<reqwest::Error> for Error { } fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { - Button::new(state, Text::new(text)) - .padding(10) - .style(style::Button::Primary) -} - -mod style { - use iced::{button, Background, Color, Vector}; - - pub enum Button { - Primary, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(match self { - Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), - })), - border_radius: 12.0, - shadow_offset: Vector::new(1.0, 1.0), - text_color: Color::WHITE, - ..button::Style::default() - } - } - } + Button::new(state, Text::new(text)).padding(10) } diff --git a/examples/pure/component/src/main.rs b/examples/pure/component/src/main.rs index b38d6fca..64935afd 100644 --- a/examples/pure/component/src/main.rs +++ b/examples/pure/component/src/main.rs @@ -47,12 +47,13 @@ impl Sandbox for Component { } mod numeric_input { - use iced::pure::{button, row, text, text_input}; use iced_lazy::pure::{self, Component}; use iced_native::alignment::{self, Alignment}; use iced_native::text; + use iced_native::widget; use iced_native::Length; use iced_pure::Element; + use iced_pure::{button, row, text, text_input}; pub struct NumericInput<Message> { value: Option<u32>, @@ -88,6 +89,9 @@ mod numeric_input { impl<Message, Renderer> Component<Message, Renderer> for NumericInput<Message> where Renderer: text::Renderer + 'static, + Renderer::Theme: widget::button::StyleSheet + + widget::text_input::StyleSheet + + widget::text::StyleSheet, { type State = (); type Event = Event; @@ -158,6 +162,9 @@ mod numeric_input { where Message: 'a, Renderer: 'static + text::Renderer, + Renderer::Theme: widget::button::StyleSheet + + widget::text_input::StyleSheet + + widget::text::StyleSheet, { fn from(numeric_input: NumericInput<Message>) -> Self { pure::component(numeric_input) diff --git a/examples/pure/game_of_life/src/main.rs b/examples/pure/game_of_life/src/main.rs index a3164701..851fbd47 100644 --- a/examples/pure/game_of_life/src/main.rs +++ b/examples/pure/game_of_life/src/main.rs @@ -1,18 +1,19 @@ //! This example showcases an interactive version of the Game of Life, invented //! by John Conway. It leverages a `Canvas` together with other widgets. mod preset; -mod style; use grid::Grid; +use preset::Preset; + use iced::executor; use iced::pure::{ button, checkbox, column, container, pick_list, row, slider, text, }; use iced::pure::{Application, Element}; +use iced::theme::{self, Theme}; use iced::time; use iced::window; -use iced::{Alignment, Color, Command, Length, Settings, Subscription}; -use preset::Preset; +use iced::{Alignment, Command, Length, Settings, Subscription}; use std::time::{Duration, Instant}; pub fn main() -> iced::Result { @@ -52,6 +53,7 @@ enum Message { impl Application for GameOfLife { type Message = Message; + type Theme = Theme; type Executor = executor::Default; type Flags = (); @@ -69,10 +71,6 @@ impl Application for GameOfLife { String::from("Game of Life - Iced") } - fn background_color(&self) -> Color { - style::BACKGROUND - } - fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Grid(message, version) => { @@ -153,9 +151,12 @@ impl Application for GameOfLife { container(content) .width(Length::Fill) .height(Length::Fill) - .style(style::Container) .into() } + + fn theme(&self) -> Theme { + Theme::Dark + } } fn view_controls<'a>( @@ -168,19 +169,19 @@ fn view_controls<'a>( .spacing(10) .push( button(if is_playing { "Pause" } else { "Play" }) - .on_press(Message::TogglePlayback) - .style(style::Button), + .on_press(Message::TogglePlayback), ) - .push(button("Next").on_press(Message::Next).style(style::Button)); + .push( + button("Next") + .on_press(Message::Next) + .style(theme::Button::Secondary), + ); let speed_controls = row() .width(Length::Fill) .align_items(Alignment::Center) .spacing(10) - .push( - slider(1.0..=1000.0, speed as f32, Message::SpeedChanged) - .style(style::Slider), - ) + .push(slider(1.0..=1000.0, speed as f32, Message::SpeedChanged)) .push(text(format!("x{}", speed)).size(16)); row() @@ -198,10 +199,13 @@ fn view_controls<'a>( .push( pick_list(preset::ALL, Some(preset), Message::PresetPicked) .padding(8) - .text_size(16) - .style(style::PickList), + .text_size(16), + ) + .push( + button("Clear") + .on_press(Message::Clear) + .style(theme::Button::Destructive), ) - .push(button("Clear").on_press(Message::Clear).style(style::Clear)) .into() } @@ -213,7 +217,7 @@ mod grid { }; use iced::pure::Element; use iced::{ - alignment, mouse, Color, Length, Point, Rectangle, Size, Vector, + alignment, mouse, Color, Length, Point, Rectangle, Size, Theme, Vector, }; use rustc_hash::{FxHashMap, FxHashSet}; use std::future::Future; @@ -522,6 +526,7 @@ mod grid { fn draw( &self, _interaction: &Interaction, + _theme: &Theme, bounds: Rectangle, cursor: Cursor, ) -> Vec<Geometry> { diff --git a/examples/pure/game_of_life/src/style.rs b/examples/pure/game_of_life/src/style.rs deleted file mode 100644 index 1a64cf4a..00000000 --- a/examples/pure/game_of_life/src/style.rs +++ /dev/null @@ -1,186 +0,0 @@ -use iced::{button, container, pick_list, slider, Background, Color}; - -const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, -); - -const DESTRUCTIVE: Color = Color::from_rgb( - 0xC0 as f32 / 255.0, - 0x47 as f32 / 255.0, - 0x47 as f32 / 255.0, -); - -const HOVERED: Color = Color::from_rgb( - 0x67 as f32 / 255.0, - 0x7B as f32 / 255.0, - 0xC4 as f32 / 255.0, -); - -pub const BACKGROUND: Color = Color::from_rgb( - 0x2F as f32 / 255.0, - 0x31 as f32 / 255.0, - 0x36 as f32 / 255.0, -); - -pub struct Container; - -impl container::StyleSheet for Container { - fn style(&self) -> container::Style { - container::Style { - text_color: Some(Color::WHITE), - ..container::Style::default() - } - } -} - -pub struct Button; - -impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(ACTIVE)), - border_radius: 3.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - background: Some(Background::Color(HOVERED)), - text_color: Color::WHITE, - ..self.active() - } - } - - fn pressed(&self) -> button::Style { - button::Style { - border_width: 1.0, - border_color: Color::WHITE, - ..self.hovered() - } - } -} - -pub struct Clear; - -impl button::StyleSheet for Clear { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(DESTRUCTIVE)), - border_radius: 3.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - background: Some(Background::Color(Color { - a: 0.5, - ..DESTRUCTIVE - })), - text_color: Color::WHITE, - ..self.active() - } - } - - fn pressed(&self) -> button::Style { - button::Style { - border_width: 1.0, - border_color: Color::WHITE, - ..self.hovered() - } - } -} - -pub struct Slider; - -impl slider::StyleSheet for Slider { - fn active(&self) -> slider::Style { - slider::Style { - rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }), - handle: slider::Handle { - shape: slider::HandleShape::Circle { radius: 9.0 }, - color: ACTIVE, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: HOVERED, - ..active.handle - }, - ..active - } - } - - fn dragging(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: Color::from_rgb(0.85, 0.85, 0.85), - ..active.handle - }, - ..active - } - } -} - -pub struct PickList; - -impl pick_list::StyleSheet for PickList { - fn menu(&self) -> pick_list::Menu { - pick_list::Menu { - text_color: Color::WHITE, - background: BACKGROUND.into(), - border_width: 1.0, - border_color: Color { - a: 0.7, - ..Color::BLACK - }, - selected_background: Color { - a: 0.5, - ..Color::BLACK - } - .into(), - selected_text_color: Color::WHITE, - } - } - - fn active(&self) -> pick_list::Style { - pick_list::Style { - text_color: Color::WHITE, - background: BACKGROUND.into(), - border_width: 1.0, - border_color: Color { - a: 0.6, - ..Color::BLACK - }, - border_radius: 2.0, - icon_size: 0.5, - ..pick_list::Style::default() - } - } - - fn hovered(&self) -> pick_list::Style { - let active = self.active(); - - pick_list::Style { - border_color: Color { - a: 0.9, - ..Color::BLACK - }, - ..active - } - } -} diff --git a/examples/pure/pane_grid/src/main.rs b/examples/pure/pane_grid/src/main.rs index 65516956..e85ed78d 100644 --- a/examples/pure/pane_grid/src/main.rs +++ b/examples/pure/pane_grid/src/main.rs @@ -4,6 +4,7 @@ use iced::keyboard; use iced::pure::widget::pane_grid::{self, PaneGrid}; use iced::pure::{button, column, container, row, scrollable, text}; use iced::pure::{Application, Element}; +use iced::theme::{self, Theme}; use iced::{Color, Command, Length, Settings, Size, Subscription}; use iced_lazy::pure::responsive; use iced_native::{event, subscription, Event}; @@ -33,6 +34,7 @@ enum Message { impl Application for Example { type Message = Message; + type Theme = Theme; type Executor = executor::Default; type Flags = (); @@ -161,13 +163,12 @@ impl Application for Example { text(if pane.is_pinned { "Unpin" } else { "Pin" }).size(14), ) .on_press(Message::TogglePin(id)) - .style(style::Button::Pin) .padding(3); let title = row() .push(pin_button) .push("Pane") - .push(text(pane.id.to_string()).color(if is_focused { + .push(text(pane.id.to_string()).style(if is_focused { PANE_ID_COLOR_FOCUSED } else { PANE_ID_COLOR_UNFOCUSED @@ -178,9 +179,9 @@ impl Application for Example { .controls(view_controls(id, total_panes, pane.is_pinned)) .padding(10) .style(if is_focused { - style::TitleBar::Focused + style::title_bar_focused } else { - style::TitleBar::Active + style::title_bar_active }); pane_grid::Content::new(responsive(move |size| { @@ -188,9 +189,9 @@ impl Application for Example { })) .title_bar(title_bar) .style(if is_focused { - style::Pane::Focused + style::pane_focused } else { - style::Pane::Active + style::pane_active }) }) .width(Length::Fill) @@ -259,7 +260,7 @@ fn view_content<'a>( is_pinned: bool, size: Size, ) -> Element<'a, Message> { - let button = |label, message, style| { + let button = |label, message| { button( text(label) .width(Length::Fill) @@ -269,7 +270,6 @@ fn view_content<'a>( .width(Length::Fill) .padding(8) .on_press(message) - .style(style) }; let mut controls = column() @@ -278,20 +278,17 @@ fn view_content<'a>( .push(button( "Split horizontally", Message::Split(pane_grid::Axis::Horizontal, pane), - style::Button::Primary, )) .push(button( "Split vertically", Message::Split(pane_grid::Axis::Vertical, pane), - style::Button::Primary, )); if total_panes > 1 && !is_pinned { - controls = controls.push(button( - "Close", - Message::Close(pane), - style::Button::Destructive, - )); + controls = controls.push( + button("Close", Message::Close(pane)) + .style(theme::Button::Destructive), + ); } let content = column() @@ -315,7 +312,7 @@ fn view_controls<'a>( is_pinned: bool, ) -> Element<'a, Message> { let mut button = button(text("Close").size(14)) - .style(style::Button::Control) + .style(theme::Button::Destructive) .padding(3); if total_panes > 1 && !is_pinned { @@ -326,111 +323,47 @@ fn view_controls<'a>( } mod style { - use crate::PANE_ID_COLOR_FOCUSED; - use iced::{button, container, Background, Color, Vector}; - - const SURFACE: Color = Color::from_rgb( - 0xF2 as f32 / 255.0, - 0xF3 as f32 / 255.0, - 0xF5 as f32 / 255.0, - ); - - const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, - ); - - const HOVERED: Color = Color::from_rgb( - 0x67 as f32 / 255.0, - 0x7B as f32 / 255.0, - 0xC4 as f32 / 255.0, - ); - - pub enum TitleBar { - Active, - Focused, - } + use iced::{container, Theme}; - impl container::StyleSheet for TitleBar { - fn style(&self) -> container::Style { - let pane = match self { - Self::Active => Pane::Active, - Self::Focused => Pane::Focused, - } - .style(); + pub fn title_bar_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - container::Style { - text_color: Some(Color::WHITE), - background: Some(pane.border_color.into()), - ..Default::default() - } + container::Appearance { + text_color: Some(palette.background.strong.text), + background: Some(palette.background.strong.color.into()), + ..Default::default() } } - pub enum Pane { - Active, - Focused, - } + pub fn title_bar_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - impl container::StyleSheet for Pane { - fn style(&self) -> container::Style { - container::Style { - background: Some(Background::Color(SURFACE)), - border_width: 2.0, - border_color: match self { - Self::Active => Color::from_rgb(0.7, 0.7, 0.7), - Self::Focused => Color::BLACK, - }, - ..Default::default() - } + container::Appearance { + text_color: Some(palette.primary.strong.text), + background: Some(palette.primary.strong.color.into()), + ..Default::default() } } - pub enum Button { - Primary, - Destructive, - Control, - Pin, - } + pub fn pane_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - let (background, text_color) = match self { - Button::Primary => (Some(ACTIVE), Color::WHITE), - Button::Destructive => { - (None, Color::from_rgb8(0xFF, 0x47, 0x47)) - } - Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE), - Button::Pin => (Some(ACTIVE), Color::WHITE), - }; - - button::Style { - text_color, - background: background.map(Background::Color), - border_radius: 5.0, - shadow_offset: Vector::new(0.0, 0.0), - ..button::Style::default() - } + container::Appearance { + background: Some(palette.background.weak.color.into()), + border_width: 2.0, + border_color: palette.background.strong.color, + ..Default::default() } + } - fn hovered(&self) -> button::Style { - let active = self.active(); - - let background = match self { - Button::Primary => Some(HOVERED), - Button::Destructive => Some(Color { - a: 0.2, - ..active.text_color - }), - Button::Control => Some(PANE_ID_COLOR_FOCUSED), - Button::Pin => Some(HOVERED), - }; - - button::Style { - background: background.map(Background::Color), - ..active - } + pub fn pane_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border_width: 2.0, + border_color: palette.primary.strong.color, + ..Default::default() } } } diff --git a/examples/pure/todos/src/main.rs b/examples/pure/todos/src/main.rs index 6a6c6300..723386ad 100644 --- a/examples/pure/todos/src/main.rs +++ b/examples/pure/todos/src/main.rs @@ -4,8 +4,9 @@ use iced::pure::{ button, checkbox, column, container, row, scrollable, text, text_input, Application, Element, }; +use iced::theme::{self, Theme}; use iced::window; -use iced::{Command, Font, Length, Settings}; +use iced::{Color, Command, Font, Length, Settings}; use serde::{Deserialize, Serialize}; pub fn main() -> iced::Result { @@ -44,8 +45,9 @@ enum Message { } impl Application for Todos { - type Executor = iced::executor::Default; type Message = Message; + type Theme = Theme; + type Executor = iced::executor::Default; type Flags = (); fn new(_flags: ()) -> (Todos, Command<Message>) { @@ -153,7 +155,7 @@ impl Application for Todos { let title = text("todos") .width(Length::Fill) .size(100) - .color([0.5, 0.5, 0.5]) + .style(Color::from([0.5, 0.5, 0.5])) .horizontal_alignment(alignment::Horizontal::Center); let input = text_input( @@ -287,7 +289,7 @@ impl Task { button(edit_icon()) .on_press(TaskMessage::Edit) .padding(10) - .style(style::Button::Icon), + .style(theme::Button::Text), ) .into() } @@ -313,7 +315,7 @@ impl Task { ) .on_press(TaskMessage::Delete) .padding(10) - .style(style::Button::Destructive), + .style(theme::Button::Destructive), ) .into() } @@ -328,9 +330,9 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> { let label = text(label).size(16); let button = button(label).style(if filter == current_filter { - style::Button::FilterSelected + theme::Button::Primary } else { - style::Button::FilterActive + theme::Button::Text }); button.on_press(Message::FilterChanged(filter)).padding(8) @@ -404,7 +406,7 @@ fn empty_message(message: &str) -> Element<'_, Message> { .width(Length::Fill) .size(25) .horizontal_alignment(alignment::Horizontal::Center) - .color([0.7, 0.7, 0.7]), + .style(Color::from([0.7, 0.7, 0.7])), ) .width(Length::Fill) .height(Length::Units(200)) @@ -552,57 +554,3 @@ impl SavedState { Ok(()) } } - -mod style { - use iced::{button, Background, Color, Vector}; - - pub enum Button { - FilterActive, - FilterSelected, - Icon, - Destructive, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - match self { - Button::FilterActive => button::Style::default(), - Button::FilterSelected => button::Style { - background: Some(Background::Color(Color::from_rgb( - 0.2, 0.2, 0.7, - ))), - border_radius: 10.0, - text_color: Color::WHITE, - ..button::Style::default() - }, - Button::Icon => button::Style { - text_color: Color::from_rgb(0.5, 0.5, 0.5), - ..button::Style::default() - }, - Button::Destructive => button::Style { - background: Some(Background::Color(Color::from_rgb( - 0.8, 0.2, 0.2, - ))), - border_radius: 5.0, - text_color: Color::WHITE, - shadow_offset: Vector::new(1.0, 1.0), - ..button::Style::default() - }, - } - } - - fn hovered(&self) -> button::Style { - let active = self.active(); - - button::Style { - text_color: match self { - Button::Icon => Color::from_rgb(0.2, 0.2, 0.7), - Button::FilterActive => Color::from_rgb(0.2, 0.2, 0.7), - _ => active.text_color, - }, - shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), - ..active - } - } - } -} diff --git a/examples/pure/tooltip/src/main.rs b/examples/pure/tooltip/src/main.rs index dbd83f5f..e9a6c111 100644 --- a/examples/pure/tooltip/src/main.rs +++ b/examples/pure/tooltip/src/main.rs @@ -1,6 +1,7 @@ -use iced::pure::{ - button, container, tooltip, widget::tooltip::Position, Element, Sandbox, -}; +use iced::pure::widget::tooltip::Position; +use iced::pure::{button, container, tooltip}; +use iced::pure::{Element, Sandbox}; +use iced::theme; use iced::{Length, Settings}; pub fn main() -> iced::Result { @@ -53,7 +54,7 @@ impl Sandbox for Example { self.position, ) .gap(10) - .style(style::Tooltip); + .style(theme::Container::Box); container(tooltip) .width(Length::Fill) @@ -73,21 +74,3 @@ fn position_to_text<'a>(position: Position) -> &'a str { Position::Right => "Right", } } - -mod style { - use iced::container; - use iced::Color; - - pub struct Tooltip; - - impl container::StyleSheet for Tooltip { - fn style(&self) -> container::Style { - container::Style { - text_color: Some(Color::from_rgb8(0xEE, 0xEE, 0xEE)), - background: Some(Color::from_rgb(0.11, 0.42, 0.87).into()), - border_radius: 12.0, - ..container::Style::default() - } - } - } -} diff --git a/examples/pure/tour/src/main.rs b/examples/pure/tour/src/main.rs index a44d99f3..477a1ec7 100644 --- a/examples/pure/tour/src/main.rs +++ b/examples/pure/tour/src/main.rs @@ -5,7 +5,8 @@ use iced::pure::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::pure::{Element, Sandbox}; -use iced::{Color, Length, Settings}; +use iced::theme; +use iced::{Color, Length, Renderer, Settings}; pub fn main() -> iced::Result { env_logger::init(); @@ -55,7 +56,7 @@ impl Sandbox for Tour { controls = controls.push( button("Back") .on_press(Message::BackPressed) - .style(style::Button::Secondary), + .style(theme::Button::Secondary), ); } @@ -65,7 +66,7 @@ impl Sandbox for Tour { controls = controls.push( button("Next") .on_press(Message::NextPressed) - .style(style::Button::Primary), + .style(theme::Button::Primary), ); } @@ -432,7 +433,7 @@ impl<'a> Step { .padding(20) .spacing(20) .push("And its color:") - .push(text(format!("{:?}", color)).color(color)) + .push(text(format!("{:?}", color)).style(color)) .push(color_sliders); Self::container("Text") @@ -575,7 +576,7 @@ impl<'a> Step { .push(if cfg!(target_arch = "wasm32") { Element::new( text("Not available on web yet!") - .color([0.7, 0.7, 0.7]) + .style(Color::from([0.7, 0.7, 0.7])) .horizontal_alignment(alignment::Horizontal::Center), ) } else { @@ -621,7 +622,7 @@ fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { fn color_slider<'a>( component: f32, update: impl Fn(f32) -> Color + 'a, -) -> Slider<'a, f64, StepMessage> { +) -> Slider<'a, f64, StepMessage, Renderer> { slider(0.0..=1.0, f64::from(component), move |c| { StepMessage::TextColorChanged(update(c as f32)) }) @@ -669,35 +670,3 @@ pub enum Layout { Row, Column, } - -mod style { - use iced::{button, Background, Color, Vector}; - - pub enum Button { - Primary, - Secondary, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(match self { - Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), - Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), - })), - border_radius: 12.0, - shadow_offset: Vector::new(1.0, 1.0), - text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - text_color: Color::WHITE, - shadow_offset: Vector::new(1.0, 2.0), - ..self.active() - } - } - } -} diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 92c82d45..3e9ba921 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -1,7 +1,8 @@ use iced::qr_code::{self, QRCode}; use iced::text_input::{self, TextInput}; use iced::{ - Alignment, Column, Container, Element, Length, Sandbox, Settings, Text, + Alignment, Color, Column, Container, Element, Length, Sandbox, Settings, + Text, }; pub fn main() -> iced::Result { @@ -48,7 +49,7 @@ impl Sandbox for QRGenerator { fn view(&mut self) -> Element<Message> { let title = Text::new("QR Code Generator") .size(70) - .color([0.5, 0.5, 0.5]); + .style(Color::from([0.5, 0.5, 0.5])); let input = TextInput::new( &mut self.input, diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 8e027504..f66d2180 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -1,8 +1,8 @@ -mod style; - +use iced::button; +use iced::scrollable; use iced::{ - button, scrollable, Button, Column, Container, Element, Length, - ProgressBar, Radio, Row, Rule, Sandbox, Scrollable, Settings, Space, Text, + Button, Column, Container, Element, Length, ProgressBar, Radio, Row, Rule, + Sandbox, Scrollable, Settings, Space, Text, Theme, }; pub fn main() -> iced::Result { @@ -10,13 +10,13 @@ pub fn main() -> iced::Result { } struct ScrollableDemo { - theme: style::Theme, + theme: Theme, variants: Vec<Variant>, } #[derive(Debug, Clone)] enum Message { - ThemeChanged(style::Theme), + ThemeChanged(Theme), ScrollToTop(usize), ScrollToBottom(usize), Scrolled(usize, f32), @@ -66,18 +66,15 @@ impl Sandbox for ScrollableDemo { theme, variants, .. } = self; - let choose_theme = style::Theme::ALL.iter().fold( + let choose_theme = [Theme::Light, Theme::Dark].iter().fold( Column::new().spacing(10).push(Text::new("Choose a theme:")), |column, option| { - column.push( - Radio::new( - *option, - format!("{:?}", option), - Some(*theme), - Message::ThemeChanged, - ) - .style(*theme), - ) + column.push(Radio::new( + *option, + format!("{:?}", option), + Some(*theme), + Message::ThemeChanged, + )) }, ); @@ -95,7 +92,6 @@ impl Sandbox for ScrollableDemo { .on_scroll(move |offset| { Message::Scrolled(i, offset) }) - .style(*theme) .push(Text::new(variant.title)) .push( Button::new( @@ -160,12 +156,7 @@ impl Sandbox for ScrollableDemo { .width(Length::Fill) .height(Length::Fill) .spacing(10) - .push( - Container::new(scrollable) - .width(Length::Fill) - .height(Length::Fill) - .style(*theme), - ) + .push(scrollable) .push(ProgressBar::new( 0.0..=1.0, variant.latest_offset, @@ -182,7 +173,7 @@ impl Sandbox for ScrollableDemo { .spacing(20) .padding(20) .push(choose_theme) - .push(Rule::horizontal(20).style(self.theme)) + .push(Rule::horizontal(20)) .push(scrollable_row); Container::new(content) @@ -190,9 +181,12 @@ impl Sandbox for ScrollableDemo { .height(Length::Fill) .center_x() .center_y() - .style(self.theme) .into() } + + fn theme(&self) -> Theme { + self.theme + } } /// A version of a scrollable diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs deleted file mode 100644 index 0ed38b00..00000000 --- a/examples/scrollable/src/style.rs +++ /dev/null @@ -1,191 +0,0 @@ -use iced::{container, radio, rule, scrollable}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Theme { - Light, - Dark, -} - -impl Theme { - pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; -} - -impl Default for Theme { - fn default() -> Theme { - Theme::Light - } -} - -impl<'a> From<Theme> for Box<dyn container::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Container.into(), - } - } -} - -impl<'a> From<Theme> for Box<dyn radio::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Radio.into(), - } - } -} - -impl<'a> From<Theme> for Box<dyn scrollable::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), - } - } -} - -impl From<Theme> for Box<dyn rule::StyleSheet> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Rule.into(), - } - } -} - -mod dark { - use iced::{container, radio, rule, scrollable, Color}; - - const BACKGROUND: Color = Color::from_rgb( - 0x36 as f32 / 255.0, - 0x39 as f32 / 255.0, - 0x3F as f32 / 255.0, - ); - - const SURFACE: Color = Color::from_rgb( - 0x40 as f32 / 255.0, - 0x44 as f32 / 255.0, - 0x4B as f32 / 255.0, - ); - - const ACCENT: Color = Color::from_rgb( - 0x6F as f32 / 255.0, - 0xFF as f32 / 255.0, - 0xE9 as f32 / 255.0, - ); - - const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, - ); - - const SCROLLBAR: Color = Color::from_rgb( - 0x2E as f32 / 255.0, - 0x33 as f32 / 255.0, - 0x38 as f32 / 255.0, - ); - - const SCROLLER: Color = Color::from_rgb( - 0x20 as f32 / 255.0, - 0x22 as f32 / 255.0, - 0x25 as f32 / 255.0, - ); - - pub struct Container; - - impl container::StyleSheet for Container { - fn style(&self) -> container::Style { - container::Style { - background: Color { - a: 0.99, - ..BACKGROUND - } - .into(), - text_color: Color::WHITE.into(), - ..container::Style::default() - } - } - } - - pub struct Radio; - - impl radio::StyleSheet for Radio { - fn active(&self) -> radio::Style { - radio::Style { - background: SURFACE.into(), - dot_color: ACTIVE, - border_width: 1.0, - border_color: ACTIVE, - text_color: None, - } - } - - fn hovered(&self) -> radio::Style { - radio::Style { - background: Color { a: 0.5, ..SURFACE }.into(), - ..self.active() - } - } - } - - pub struct Scrollable; - - impl scrollable::StyleSheet for Scrollable { - fn active(&self) -> scrollable::Scrollbar { - scrollable::Scrollbar { - background: Color { - a: 0.8, - ..SCROLLBAR - } - .into(), - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - scroller: scrollable::Scroller { - color: Color { a: 0.7, ..SCROLLER }, - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> scrollable::Scrollbar { - let active = self.active(); - - scrollable::Scrollbar { - background: SCROLLBAR.into(), - scroller: scrollable::Scroller { - color: SCROLLER, - ..active.scroller - }, - ..active - } - } - - fn dragging(&self) -> scrollable::Scrollbar { - let hovered = self.hovered(); - - scrollable::Scrollbar { - scroller: scrollable::Scroller { - color: ACCENT, - ..hovered.scroller - }, - ..hovered - } - } - } - - pub struct Rule; - - impl rule::StyleSheet for Rule { - fn style(&self) -> rule::Style { - rule::Style { - color: SURFACE, - width: 2, - radius: 1.0, - fill_mode: rule::FillMode::Percent(30.0), - } - } - } -} diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index e96b53ff..cee9a02f 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -6,10 +6,15 @@ //! Inspired by the example found in the MDN docs[1]. //! //! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system +use iced::application; +use iced::canvas::{self, Cursor, Path, Stroke}; +use iced::executor; +use iced::theme::{self, Theme}; +use iced::time; +use iced::window; use iced::{ - canvas::{self, Cursor, Path, Stroke}, - executor, time, window, Application, Canvas, Color, Command, Element, - Length, Point, Rectangle, Settings, Size, Subscription, Vector, + Application, Canvas, Color, Command, Element, Length, Point, Rectangle, + Settings, Size, Subscription, Vector, }; use std::time::Instant; @@ -31,8 +36,9 @@ enum Message { } impl Application for SolarSystem { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Self, Command<Message>) { @@ -48,10 +54,6 @@ impl Application for SolarSystem { String::from("Solar system - Iced") } - fn background_color(&self) -> Color { - Color::BLACK - } - fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Tick(instant) => { @@ -73,6 +75,17 @@ impl Application for SolarSystem { .height(Length::Fill) .into() } + + fn theme(&self) -> Theme { + Theme::Dark + } + + fn style(&self) -> theme::Application { + theme::Application::Custom(|_theme| application::Appearance { + background_color: Color::BLACK, + text_color: Color::WHITE, + }) + } } #[derive(Debug)] @@ -135,6 +148,7 @@ impl State { impl<Message> canvas::Program<Message> for State { fn draw( &self, + _theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec<canvas::Geometry> { diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index 377d7a2d..b83b92ec 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -1,7 +1,13 @@ +use iced::alignment; +use iced::button; +use iced::executor; +use iced::theme::{self, Theme}; +use iced::time; use iced::{ - alignment, button, executor, time, Alignment, Application, Button, Column, - Command, Container, Element, Length, Row, Settings, Subscription, Text, + Alignment, Application, Button, Column, Command, Container, Element, + Length, Row, Settings, Subscription, Text, }; + use std::time::{Duration, Instant}; pub fn main() -> iced::Result { @@ -28,8 +34,9 @@ enum Message { } impl Application for Stopwatch { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Stopwatch, Command<Message>) { @@ -99,7 +106,7 @@ impl Application for Stopwatch { )) .size(40); - let button = |state, label, style| { + let button = |state, label| { Button::new( state, Text::new(label) @@ -107,21 +114,20 @@ impl Application for Stopwatch { ) .padding(10) .width(Length::Units(80)) - .style(style) }; let toggle_button = { - let (label, color) = match self.state { - State::Idle => ("Start", style::Button::Primary), - State::Ticking { .. } => ("Stop", style::Button::Destructive), + let label = match self.state { + State::Idle => "Start", + State::Ticking { .. } => "Stop", }; - button(&mut self.toggle, label, color).on_press(Message::Toggle) + button(&mut self.toggle, label).on_press(Message::Toggle) }; - let reset_button = - button(&mut self.reset, "Reset", style::Button::Secondary) - .on_press(Message::Reset); + let reset_button = button(&mut self.reset, "Reset") + .style(theme::Button::Destructive) + .on_press(Message::Reset); let controls = Row::new() .spacing(20) @@ -142,29 +148,3 @@ impl Application for Stopwatch { .into() } } - -mod style { - use iced::{button, Background, Color, Vector}; - - pub enum Button { - Primary, - Secondary, - Destructive, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(match self { - Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), - Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), - Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), - })), - border_radius: 12.0, - shadow_offset: Vector::new(1.0, 1.0), - text_color: Color::WHITE, - ..button::Style::default() - } - } - } -} diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index b4ef3e87..aa90d17c 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -1,7 +1,11 @@ +use iced::button; +use iced::scrollable; +use iced::slider; +use iced::text_input; use iced::{ - button, scrollable, slider, text_input, Alignment, Button, Checkbox, - Column, Container, Element, Length, ProgressBar, Radio, Row, Rule, Sandbox, - Scrollable, Settings, Slider, Space, Text, TextInput, Toggler, + Alignment, Button, Checkbox, Column, Container, Element, Length, + ProgressBar, Radio, Row, Rule, Sandbox, Scrollable, Settings, Slider, + Space, Text, TextInput, Theme, Toggler, }; pub fn main() -> iced::Result { @@ -10,7 +14,7 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Styling { - theme: style::Theme, + theme: Theme, scroll: scrollable::State, input: text_input::State, input_value: String, @@ -23,7 +27,7 @@ struct Styling { #[derive(Debug, Clone)] enum Message { - ThemeChanged(style::Theme), + ThemeChanged(Theme), InputChanged(String), ButtonPressed, SliderChanged(f32), @@ -54,18 +58,15 @@ impl Sandbox for Styling { } fn view(&mut self) -> Element<Message> { - let choose_theme = style::Theme::ALL.iter().fold( + let choose_theme = [Theme::Light, Theme::Dark].iter().fold( Column::new().spacing(10).push(Text::new("Choose a theme:")), |column, theme| { - column.push( - Radio::new( - *theme, - format!("{:?}", theme), - Some(self.theme), - Message::ThemeChanged, - ) - .style(self.theme), - ) + column.push(Radio::new( + *theme, + format!("{:?}", theme), + Some(self.theme), + Message::ThemeChanged, + )) }, ); @@ -76,29 +77,24 @@ impl Sandbox for Styling { Message::InputChanged, ) .padding(10) - .size(20) - .style(self.theme); + .size(20); let button = Button::new(&mut self.button, Text::new("Submit")) .padding(10) - .on_press(Message::ButtonPressed) - .style(self.theme); + .on_press(Message::ButtonPressed); let slider = Slider::new( &mut self.slider, 0.0..=100.0, self.slider_value, Message::SliderChanged, - ) - .style(self.theme); + ); - let progress_bar = - ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); + let progress_bar = ProgressBar::new(0.0..=100.0, self.slider_value); let scrollable = Scrollable::new(&mut self.scroll) .width(Length::Fill) .height(Length::Units(100)) - .style(self.theme) .push(Text::new("Scroll me!")) .push(Space::with_height(Length::Units(800))) .push(Text::new("You did it!")); @@ -107,8 +103,7 @@ impl Sandbox for Styling { self.checkbox_value, "Check me!", Message::CheckboxToggled, - ) - .style(self.theme); + ); let toggler = Toggler::new( self.toggler_value, @@ -116,15 +111,14 @@ impl Sandbox for Styling { Message::TogglerToggled, ) .width(Length::Shrink) - .spacing(10) - .style(self.theme); + .spacing(10); let content = Column::new() .spacing(20) .padding(20) .max_width(600) .push(choose_theme) - .push(Rule::horizontal(38).style(self.theme)) + .push(Rule::horizontal(38)) .push(Row::new().spacing(10).push(text_input).push(button)) .push(slider) .push(progress_bar) @@ -134,7 +128,7 @@ impl Sandbox for Styling { .height(Length::Units(100)) .align_items(Alignment::Center) .push(scrollable) - .push(Rule::vertical(38).style(self.theme)) + .push(Rule::vertical(38)) .push( Column::new() .width(Length::Shrink) @@ -149,445 +143,10 @@ impl Sandbox for Styling { .height(Length::Fill) .center_x() .center_y() - .style(self.theme) .into() } -} - -mod style { - use iced::{ - button, checkbox, container, progress_bar, radio, rule, scrollable, - slider, text_input, toggler, - }; - - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum Theme { - Light, - Dark, - } - - impl Theme { - pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; - } - impl Default for Theme { - fn default() -> Theme { - Theme::Light - } - } - - impl<'a> From<Theme> for Box<dyn container::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Container.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn radio::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Radio.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn text_input::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::TextInput.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn button::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => light::Button.into(), - Theme::Dark => dark::Button.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn scrollable::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn slider::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Slider.into(), - } - } - } - - impl From<Theme> for Box<dyn progress_bar::StyleSheet> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::ProgressBar.into(), - } - } - } - - impl<'a> From<Theme> for Box<dyn checkbox::StyleSheet + 'a> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Checkbox.into(), - } - } - } - - impl From<Theme> for Box<dyn toggler::StyleSheet> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Toggler.into(), - } - } - } - - impl From<Theme> for Box<dyn rule::StyleSheet> { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Rule.into(), - } - } - } - - mod light { - use iced::{button, Color, Vector}; - - pub struct Button; - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Color::from_rgb(0.11, 0.42, 0.87).into(), - border_radius: 12.0, - shadow_offset: Vector::new(1.0, 1.0), - text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - text_color: Color::WHITE, - shadow_offset: Vector::new(1.0, 2.0), - ..self.active() - } - } - } - } - - mod dark { - use iced::{ - button, checkbox, container, progress_bar, radio, rule, scrollable, - slider, text_input, toggler, Color, - }; - - const SURFACE: Color = Color::from_rgb( - 0x40 as f32 / 255.0, - 0x44 as f32 / 255.0, - 0x4B as f32 / 255.0, - ); - - const ACCENT: Color = Color::from_rgb( - 0x6F as f32 / 255.0, - 0xFF as f32 / 255.0, - 0xE9 as f32 / 255.0, - ); - - const ACTIVE: Color = Color::from_rgb( - 0x72 as f32 / 255.0, - 0x89 as f32 / 255.0, - 0xDA as f32 / 255.0, - ); - - const HOVERED: Color = Color::from_rgb( - 0x67 as f32 / 255.0, - 0x7B as f32 / 255.0, - 0xC4 as f32 / 255.0, - ); - - pub struct Container; - - impl container::StyleSheet for Container { - fn style(&self) -> container::Style { - container::Style { - background: Color::from_rgb8(0x36, 0x39, 0x3F).into(), - text_color: Color::WHITE.into(), - ..container::Style::default() - } - } - } - - pub struct Radio; - - impl radio::StyleSheet for Radio { - fn active(&self) -> radio::Style { - radio::Style { - background: SURFACE.into(), - dot_color: ACTIVE, - border_width: 1.0, - border_color: ACTIVE, - text_color: None, - } - } - - fn hovered(&self) -> radio::Style { - radio::Style { - background: Color { a: 0.5, ..SURFACE }.into(), - ..self.active() - } - } - } - - pub struct TextInput; - - impl text_input::StyleSheet for TextInput { - fn active(&self) -> text_input::Style { - text_input::Style { - background: SURFACE.into(), - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - } - } - - fn focused(&self) -> text_input::Style { - text_input::Style { - border_width: 1.0, - border_color: ACCENT, - ..self.active() - } - } - - fn hovered(&self) -> text_input::Style { - text_input::Style { - border_width: 1.0, - border_color: Color { a: 0.3, ..ACCENT }, - ..self.focused() - } - } - - fn placeholder_color(&self) -> Color { - Color::from_rgb(0.4, 0.4, 0.4) - } - - fn value_color(&self) -> Color { - Color::WHITE - } - - fn selection_color(&self) -> Color { - ACTIVE - } - } - - pub struct Button; - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: ACTIVE.into(), - border_radius: 3.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - background: HOVERED.into(), - text_color: Color::WHITE, - ..self.active() - } - } - - fn pressed(&self) -> button::Style { - button::Style { - border_width: 1.0, - border_color: Color::WHITE, - ..self.hovered() - } - } - } - - pub struct Scrollable; - - impl scrollable::StyleSheet for Scrollable { - fn active(&self) -> scrollable::Scrollbar { - scrollable::Scrollbar { - background: SURFACE.into(), - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - scroller: scrollable::Scroller { - color: ACTIVE, - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> scrollable::Scrollbar { - let active = self.active(); - - scrollable::Scrollbar { - background: Color { a: 0.5, ..SURFACE }.into(), - scroller: scrollable::Scroller { - color: HOVERED, - ..active.scroller - }, - ..active - } - } - - fn dragging(&self) -> scrollable::Scrollbar { - let hovered = self.hovered(); - - scrollable::Scrollbar { - scroller: scrollable::Scroller { - color: Color::from_rgb(0.85, 0.85, 0.85), - ..hovered.scroller - }, - ..hovered - } - } - } - - pub struct Slider; - - impl slider::StyleSheet for Slider { - fn active(&self) -> slider::Style { - slider::Style { - rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }), - handle: slider::Handle { - shape: slider::HandleShape::Circle { radius: 9.0 }, - color: ACTIVE, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: HOVERED, - ..active.handle - }, - ..active - } - } - - fn dragging(&self) -> slider::Style { - let active = self.active(); - - slider::Style { - handle: slider::Handle { - color: Color::from_rgb(0.85, 0.85, 0.85), - ..active.handle - }, - ..active - } - } - } - - pub struct ProgressBar; - - impl progress_bar::StyleSheet for ProgressBar { - fn style(&self) -> progress_bar::Style { - progress_bar::Style { - background: SURFACE.into(), - bar: ACTIVE.into(), - border_radius: 10.0, - } - } - } - - pub struct Checkbox; - - impl checkbox::StyleSheet for Checkbox { - fn active(&self, is_checked: bool) -> checkbox::Style { - checkbox::Style { - background: if is_checked { ACTIVE } else { SURFACE } - .into(), - checkmark_color: Color::WHITE, - border_radius: 2.0, - border_width: 1.0, - border_color: ACTIVE, - text_color: None, - } - } - - fn hovered(&self, is_checked: bool) -> checkbox::Style { - checkbox::Style { - background: Color { - a: 0.8, - ..if is_checked { ACTIVE } else { SURFACE } - } - .into(), - ..self.active(is_checked) - } - } - } - - pub struct Toggler; - - impl toggler::StyleSheet for Toggler { - fn active(&self, is_active: bool) -> toggler::Style { - toggler::Style { - background: if is_active { ACTIVE } else { SURFACE }, - background_border: None, - foreground: if is_active { Color::WHITE } else { ACTIVE }, - foreground_border: None, - } - } - - fn hovered(&self, is_active: bool) -> toggler::Style { - toggler::Style { - background: if is_active { ACTIVE } else { SURFACE }, - background_border: None, - foreground: if is_active { - Color { - a: 0.5, - ..Color::WHITE - } - } else { - Color { a: 0.5, ..ACTIVE } - }, - foreground_border: None, - } - } - } - - pub struct Rule; - - impl rule::StyleSheet for Rule { - fn style(&self) -> rule::Style { - rule::Style { - color: SURFACE, - width: 2, - radius: 1.0, - fill_mode: rule::FillMode::Padded(15), - } - } - } + fn theme(&self) -> Theme { + self.theme } } diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index 560220b8..9e6a2f61 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -1,6 +1,6 @@ use iced::{ button, executor, system, Application, Button, Column, Command, Container, - Element, Length, Settings, Text, + Element, Length, Settings, Text, Theme, }; use bytesize::ByteSize; @@ -25,6 +25,7 @@ enum Message { impl Application for Example { type Message = Message; + type Theme = Theme; type Executor = executor::Default; type Flags = (); diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 0b889407..dc080ef5 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -2,9 +2,10 @@ use iced::alignment::{self, Alignment}; use iced::button::{self, Button}; use iced::scrollable::{self, Scrollable}; use iced::text_input::{self, TextInput}; +use iced::theme::{self, Theme}; use iced::{ - Application, Checkbox, Column, Command, Container, Element, Font, Length, - Row, Settings, Text, + Application, Checkbox, Color, Column, Command, Container, Element, Font, + Length, Row, Settings, Text, }; use serde::{Deserialize, Serialize}; @@ -42,6 +43,7 @@ enum Message { impl Application for Todos { type Executor = iced::executor::Default; + type Theme = Theme; type Message = Message; type Flags = (); @@ -153,7 +155,7 @@ impl Application for Todos { let title = Text::new("todos") .width(Length::Fill) .size(100) - .color([0.5, 0.5, 0.5]) + .style(Color::from([0.5, 0.5, 0.5])) .horizontal_alignment(alignment::Horizontal::Center); let input = TextInput::new( @@ -304,7 +306,7 @@ impl Task { Button::new(edit_button, edit_icon()) .on_press(TaskMessage::Edit) .padding(10) - .style(style::Button::Icon), + .style(theme::Button::Text), ) .into() } @@ -335,7 +337,7 @@ impl Task { ) .on_press(TaskMessage::Delete) .padding(10) - .style(style::Button::Destructive), + .style(theme::Button::Destructive), ) .into() } @@ -364,9 +366,9 @@ impl Controls { let label = Text::new(label).size(16); let button = Button::new(state, label).style(if filter == current_filter { - style::Button::FilterSelected + theme::Button::Primary } else { - style::Button::FilterActive + theme::Button::Text }); button.on_press(Message::FilterChanged(filter)).padding(8) @@ -451,7 +453,7 @@ fn empty_message<'a>(message: &str) -> Element<'a, Message> { .width(Length::Fill) .size(25) .horizontal_alignment(alignment::Horizontal::Center) - .color([0.7, 0.7, 0.7]), + .style(Color::from([0.7, 0.7, 0.7])), ) .width(Length::Fill) .height(Length::Units(200)) @@ -599,57 +601,3 @@ impl SavedState { Ok(()) } } - -mod style { - use iced::{button, Background, Color, Vector}; - - pub enum Button { - FilterActive, - FilterSelected, - Icon, - Destructive, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - match self { - Button::FilterActive => button::Style::default(), - Button::FilterSelected => button::Style { - background: Some(Background::Color(Color::from_rgb( - 0.2, 0.2, 0.7, - ))), - border_radius: 10.0, - text_color: Color::WHITE, - ..button::Style::default() - }, - Button::Icon => button::Style { - text_color: Color::from_rgb(0.5, 0.5, 0.5), - ..button::Style::default() - }, - Button::Destructive => button::Style { - background: Some(Background::Color(Color::from_rgb( - 0.8, 0.2, 0.2, - ))), - border_radius: 5.0, - text_color: Color::WHITE, - shadow_offset: Vector::new(1.0, 1.0), - ..button::Style::default() - }, - } - } - - fn hovered(&self) -> button::Style { - let active = self.active(); - - button::Style { - text_color: match self { - Button::Icon => Color::from_rgb(0.2, 0.2, 0.7), - Button::FilterActive => Color::from_rgb(0.2, 0.2, 0.7), - _ => active.text_color, - }, - shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), - ..active - } - } - } -} diff --git a/examples/tooltip/src/main.rs b/examples/tooltip/src/main.rs index cfeaf6a6..1bd1133c 100644 --- a/examples/tooltip/src/main.rs +++ b/examples/tooltip/src/main.rs @@ -1,7 +1,9 @@ +use iced::alignment::{self, Alignment}; +use iced::button; +use iced::theme; use iced::tooltip::{self, Tooltip}; use iced::{ - alignment, button, Alignment, Button, Column, Container, Element, Length, - Row, Sandbox, Settings, Text, + Button, Column, Container, Element, Length, Row, Sandbox, Settings, Text, }; pub fn main() { @@ -115,24 +117,6 @@ fn tooltip<'a>( ) .gap(5) .padding(10) - .style(style::Tooltip) + .style(theme::Container::Box) .into() } - -mod style { - use iced::container; - use iced::Color; - - pub struct Tooltip; - - impl container::StyleSheet for Tooltip { - fn style(&self) -> container::Style { - container::Style { - text_color: Some(Color::from_rgb8(0xEE, 0xEE, 0xEE)), - background: Some(Color::from_rgb(0.11, 0.42, 0.87).into()), - border_radius: 12.0, - ..container::Style::default() - } - } - } -} diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 2024d25a..d85f2916 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,7 +1,13 @@ +use iced::alignment; +use iced::button; +use iced::scrollable; +use iced::slider; +use iced::text_input; +use iced::theme; use iced::{ - alignment, button, scrollable, slider, text_input, Button, Checkbox, Color, - Column, Container, ContentFit, Element, Image, Length, Radio, Row, Sandbox, - Scrollable, Settings, Slider, Space, Text, TextInput, Toggler, + Button, Checkbox, Color, Column, Container, ContentFit, Element, Image, + Length, Radio, Row, Sandbox, Scrollable, Settings, Slider, Space, Text, + TextInput, Toggler, }; pub fn main() -> iced::Result { @@ -64,7 +70,7 @@ impl Sandbox for Tour { controls = controls.push( button(back_button, "Back") .on_press(Message::BackPressed) - .style(style::Button::Secondary), + .style(theme::Button::Secondary), ); } @@ -74,7 +80,7 @@ impl Sandbox for Tour { controls = controls.push( button(next_button, "Next") .on_press(Message::NextPressed) - .style(style::Button::Primary), + .style(theme::Button::Primary), ); } @@ -132,7 +138,7 @@ impl Steps { size_slider: slider::State::new(), size: 30, color_sliders: [slider::State::new(); 3], - color: Color::BLACK, + color: Color::from_rgb(0.5, 0.5, 0.5), }, Step::Radio { selection: None }, Step::Toggler { @@ -528,7 +534,7 @@ impl<'a> Step { .padding(20) .spacing(20) .push(Text::new("And its color:")) - .push(Text::new(format!("{:?}", color)).color(color)) + .push(Text::new(format!("{:?}", color)).style(color)) .push(color_sliders); Self::container("Text") @@ -710,7 +716,7 @@ impl<'a> Step { .push(if cfg!(target_arch = "wasm32") { Element::new( Text::new("Not available on web yet!") - .color([0.7, 0.7, 0.7]) + .style(Color::from([0.7, 0.7, 0.7])) .horizontal_alignment(alignment::Horizontal::Center), ) } else { @@ -770,7 +776,7 @@ fn color_slider( state: &mut slider::State, component: f32, update: impl Fn(f32) -> Color + 'static, -) -> Slider<f64, StepMessage> { +) -> Slider<f64, StepMessage, iced::Renderer> { Slider::new(state, 0.0..=1.0, f64::from(component), move |c| { StepMessage::TextColorChanged(update(c as f32)) }) @@ -818,36 +824,3 @@ pub enum Layout { Row, Column, } - -mod style { - use iced::button; - use iced::{Background, Color, Vector}; - - pub enum Button { - Primary, - Secondary, - } - - impl button::StyleSheet for Button { - fn active(&self) -> button::Style { - button::Style { - background: Some(Background::Color(match self { - Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), - Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), - })), - border_radius: 12.0, - shadow_offset: Vector::new(1.0, 1.0), - text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), - ..button::Style::default() - } - } - - fn hovered(&self) -> button::Style { - button::Style { - text_color: Color::WHITE, - shadow_offset: Vector::new(1.0, 2.0), - ..self.active() - } - } - } -} diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs index ee2d249a..b544c30d 100644 --- a/examples/url_handler/src/main.rs +++ b/examples/url_handler/src/main.rs @@ -1,6 +1,7 @@ +use iced::executor; use iced::{ - executor, Application, Command, Container, Element, Length, Settings, - Subscription, Text, + Application, Command, Container, Element, Length, Settings, Subscription, + Text, Theme, }; use iced_native::{ event::{MacOS, PlatformSpecific}, @@ -22,8 +23,9 @@ enum Message { } impl Application for App { - type Executor = executor::Default; type Message = Message; + type Theme = Theme; + type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (App, Command<Message>) { diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index c03a9f3a..64addc8f 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -7,7 +7,7 @@ use iced::scrollable::{self, Scrollable}; use iced::text_input::{self, TextInput}; use iced::{ Application, Color, Column, Command, Container, Element, Length, Row, - Settings, Subscription, Text, + Settings, Subscription, Text, Theme, }; pub fn main() -> iced::Result { @@ -34,6 +34,7 @@ enum Message { impl Application for WebSocket { type Message = Message; + type Theme = Theme; type Flags = (); type Executor = executor::Default; @@ -91,7 +92,7 @@ impl Application for WebSocket { let message_log = if self.messages.is_empty() { Container::new( Text::new("Your messages will appear here...") - .color(Color::from_rgb8(0x88, 0x88, 0x88)), + .style(Color::from_rgb8(0x88, 0x88, 0x88)), ) .width(Length::Fill) .height(Length::Fill) |