Merge branch 'master' into feature/test-recorder

This commit is contained in:
Héctor Ramón Jiménez 2025-09-11 04:57:17 +02:00
commit a052ce58b0
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
69 changed files with 1555 additions and 833 deletions

View file

@ -10,7 +10,7 @@ use iced::{Element, Fill, Point, Rectangle, Renderer, Subscription, Theme};
pub fn main() -> iced::Result {
iced::application(Arc::new, Arc::update, Arc::view)
.subscription(Arc::subscription)
.theme(|_| Theme::Dark)
.theme(Theme::Dark)
.run()
}

View file

@ -4,7 +4,7 @@ use iced::{Element, Theme};
pub fn main() -> iced::Result {
iced::application(Example::default, Example::update, Example::view)
.theme(|_| Theme::CatppuccinMocha)
.theme(Theme::CatppuccinMocha)
.run()
}

View file

@ -128,26 +128,25 @@ impl Primitive {
}
impl shader::Primitive for Primitive {
fn prepare(
type Renderer = Pipeline;
fn initialize(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
storage: &mut shader::Storage,
) -> Pipeline {
Pipeline::new(device, queue, format)
}
fn prepare(
&self,
pipeline: &mut Pipeline,
device: &wgpu::Device,
queue: &wgpu::Queue,
_bounds: &Rectangle,
viewport: &Viewport,
) {
if !storage.has::<Pipeline>() {
storage.store(Pipeline::new(
device,
queue,
format,
viewport.physical_size(),
));
}
let pipeline = storage.get_mut::<Pipeline>().unwrap();
// Upload data to GPU
pipeline.update(
device,
@ -161,14 +160,11 @@ impl shader::Primitive for Primitive {
fn render(
&self,
pipeline: &Pipeline,
encoder: &mut wgpu::CommandEncoder,
storage: &shader::Storage,
target: &wgpu::TextureView,
clip_bounds: &Rectangle<u32>,
) {
// At this point our pipeline should always be initialized
let pipeline = storage.get::<Pipeline>().unwrap();
// Render primitive
pipeline.render(
target,

View file

@ -32,7 +32,6 @@ impl Pipeline {
device: &wgpu::Device,
queue: &wgpu::Queue,
format: wgpu::TextureFormat,
target_size: Size<u32>,
) -> Self {
//vertices of one cube
let vertices =
@ -62,8 +61,8 @@ impl Pipeline {
let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("cubes depth texture"),
size: wgpu::Extent3d {
width: target_size.width,
height: target_size.height,
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
@ -297,7 +296,7 @@ impl Pipeline {
uniforms,
uniform_bind_group,
vertices,
depth_texture_size: target_size,
depth_texture_size: Size::new(1, 1),
depth_view,
depth_pipeline,
}

View file

@ -326,5 +326,8 @@ fn open_icon<'a, Message>() -> Element<'a, Message> {
fn icon<'a, Message>(codepoint: char) -> Element<'a, Message> {
const ICON_FONT: Font = Font::with_name("editor-icons");
text(codepoint).font(ICON_FONT).into()
text(codepoint)
.font(ICON_FONT)
.shaping(text::Shaping::Basic)
.into()
}

View file

@ -37,7 +37,7 @@ impl Events {
}
Message::EventOccurred(event) => {
if let Event::Window(window::Event::CloseRequested) = event {
window::get_latest().and_then(window::close)
window::latest().and_then(window::close)
} else {
Task::none()
}
@ -47,7 +47,7 @@ impl Events {
Task::none()
}
Message::Exit => window::get_latest().and_then(window::close),
Message::Exit => window::latest().and_then(window::close),
}
}

View file

@ -20,7 +20,7 @@ enum Message {
impl Exit {
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Confirm => window::get_latest().and_then(window::close),
Message::Confirm => window::latest().and_then(window::close),
Message::Exit => {
self.show_confirm = true;

View file

@ -11,7 +11,7 @@ use iced::{
pub fn main() -> iced::Result {
iced::application(Image::default, Image::update, Image::view)
.subscription(Image::subscription)
.theme(|_| Theme::TokyoNight)
.theme(Theme::TokyoNight)
.run()
}

View file

@ -16,7 +16,7 @@ pub fn main() -> iced::Result {
iced::application(GameOfLife::default, GameOfLife::update, GameOfLife::view)
.subscription(GameOfLife::subscription)
.theme(|_| Theme::Dark)
.theme(Theme::Dark)
.centered()
.run()
}

View file

@ -67,7 +67,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
let physical_size = window.inner_size();
let viewport = Viewport::with_physical_size(
Size::new(physical_size.width, physical_size.height),
window.scale_factor(),
window.scale_factor() as f32,
);
let clipboard = Clipboard::connect(window.clone());
@ -212,7 +212,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
*viewport = Viewport::with_physical_size(
Size::new(size.width, size.height),
window.scale_factor(),
window.scale_factor() as f32,
);
surface.configure(
@ -345,7 +345,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
// Map window event to iced event
if let Some(event) = conversion::window_event(
event,
window.scale_factor(),
window.scale_factor() as f32,
*modifiers,
) {
events.push(event);

View file

@ -19,11 +19,11 @@ pub fn main() -> iced::Result {
.run()
}
#[derive(Default, Debug)]
#[derive(Debug, Default)]
struct Layout {
example: Example,
explain: bool,
theme: Theme,
theme: Option<Theme>,
}
#[derive(Debug, Clone)]
@ -51,7 +51,7 @@ impl Layout {
self.explain = explain;
}
Message::ThemeSelected(theme) => {
self.theme = theme;
self.theme = Some(theme);
}
}
}
@ -74,7 +74,8 @@ impl Layout {
horizontal_space(),
checkbox("Explain", self.explain)
.on_toggle(Message::ExplainToggled),
pick_list(Theme::ALL, Some(&self.theme), Message::ThemeSelected),
pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeSelected)
.placeholder("Theme"),
]
.spacing(20)
.align_y(Center);
@ -94,14 +95,14 @@ impl Layout {
let controls = row([
(!self.example.is_first()).then_some(
button(text("← Previous").shaping(text::Shaping::Advanced))
button(text("← Previous"))
.padding([5, 10])
.on_press(Message::Previous)
.into(),
),
Some(horizontal_space().into()),
(!self.example.is_last()).then_some(
button(text("Next →").shaping(text::Shaping::Advanced))
button(text("Next →"))
.padding([5, 10])
.on_press(Message::Next)
.into(),
@ -116,7 +117,7 @@ impl Layout {
.into()
}
fn theme(&self) -> Theme {
fn theme(&self) -> Option<Theme> {
self.theme.clone()
}
}
@ -313,7 +314,7 @@ fn quotes<'a>() -> Element<'a, Message> {
"This is another reply",
),
horizontal_rule(1),
text("A separator ↑").shaping(text::Shaping::Advanced),
text("A separator ↑"),
]
.width(Shrink)
.spacing(10)

View file

@ -26,7 +26,7 @@ struct Example {
struct Window {
title: String,
scale_input: String,
current_scale: f64,
current_scale: f32,
theme: Theme,
}
@ -66,7 +66,7 @@ impl Example {
return Task::none();
};
window::get_position(*last_window)
window::position(*last_window)
.then(|last_position| {
let position = last_position.map_or(
window::Position::Default,
@ -113,7 +113,7 @@ impl Example {
Message::ScaleChanged(id, scale) => {
if let Some(window) = self.windows.get_mut(&id) {
window.current_scale = scale
.parse::<f64>()
.parse()
.unwrap_or(window.current_scale)
.clamp(0.5, 5.0);
}
@ -138,15 +138,11 @@ impl Example {
}
}
fn theme(&self, window: window::Id) -> Theme {
if let Some(window) = self.windows.get(&window) {
window.theme.clone()
} else {
Theme::default()
}
fn theme(&self, window: window::Id) -> Option<Theme> {
Some(self.windows.get(&window)?.theme.clone())
}
fn scale_factor(&self, window: window::Id) -> f64 {
fn scale_factor(&self, window: window::Id) -> f32 {
self.windows
.get(&window)
.map(|window| window.current_scale)

View file

@ -20,7 +20,7 @@ struct QRGenerator {
data: String,
qr_code: Option<qr_code::Data>,
total_size: Option<f32>,
theme: Theme,
theme: Option<Theme>,
}
#[derive(Debug, Clone)]
@ -58,7 +58,7 @@ impl QRGenerator {
self.total_size = Some(total_size);
}
Message::ThemeChanged(theme) => {
self.theme = theme;
self.theme = Some(theme);
}
}
}
@ -78,7 +78,8 @@ impl QRGenerator {
let choose_theme = row![
text("Theme:"),
pick_list(Theme::ALL, Some(&self.theme), Message::ThemeChanged,)
pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeChanged)
.placeholder("Theme")
]
.spacing(10)
.align_y(Center);
@ -107,7 +108,7 @@ impl QRGenerator {
center(content).padding(20).into()
}
fn theme(&self) -> Theme {
fn theme(&self) -> Option<Theme> {
self.theme.clone()
}
}

View file

@ -49,7 +49,7 @@ impl Example {
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Screenshot => {
return window::get_latest()
return window::latest()
.and_then(window::screenshot)
.map(Message::Screenshotted);
}

View file

@ -1 +1 @@
0650eb2c27c21c5d48e1e00031a52d8471d8a3b4e827ad502c4628914f5c1c13
129523830df064908cfa911214ba61dadc31d8975425ba3efaae69da9514a3d0

View file

@ -15,7 +15,7 @@ pub fn main() -> iced::Result {
#[derive(Default)]
struct Styling {
theme: Theme,
theme: Option<Theme>,
input_value: String,
slider_value: f32,
checkbox_value: bool,
@ -32,13 +32,14 @@ enum Message {
TogglerToggled(bool),
PreviousTheme,
NextTheme,
ClearTheme,
}
impl Styling {
fn update(&mut self, message: Message) {
match message {
Message::ThemeChanged(theme) => {
self.theme = theme;
self.theme = Some(theme);
}
Message::InputChanged(value) => self.input_value = value,
Message::ButtonPressed => {}
@ -46,21 +47,29 @@ impl Styling {
Message::CheckboxToggled(value) => self.checkbox_value = value,
Message::TogglerToggled(value) => self.toggler_value = value,
Message::PreviousTheme | Message::NextTheme => {
if let Some(current) = Theme::ALL
.iter()
.position(|candidate| &self.theme == candidate)
{
self.theme = if matches!(message, Message::NextTheme) {
Theme::ALL[(current + 1) % Theme::ALL.len()].clone()
} else if current == 0 {
let current = Theme::ALL.iter().position(|candidate| {
self.theme.as_ref() == Some(candidate)
});
self.theme = Some(if matches!(message, Message::NextTheme) {
Theme::ALL[current.map(|current| current + 1).unwrap_or(0)
% Theme::ALL.len()]
.clone()
} else {
let current = current.unwrap_or(0);
if current == 0 {
Theme::ALL
.last()
.expect("Theme::ALL must not be empty")
.clone()
} else {
Theme::ALL[current - 1].clone()
};
}
}
});
}
Message::ClearTheme => {
self.theme = None;
}
}
}
@ -68,8 +77,9 @@ impl Styling {
fn view(&self) -> Element<'_, Message> {
let choose_theme = column![
text("Theme:"),
pick_list(Theme::ALL, Some(&self.theme), Message::ThemeChanged)
.width(Fill),
pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeChanged)
.width(Fill)
.placeholder("System"),
]
.spacing(10);
@ -186,11 +196,14 @@ impl Styling {
keyboard::key::Named::ArrowDown
| keyboard::key::Named::ArrowRight,
) => Some(Message::NextTheme),
keyboard::Key::Named(keyboard::key::Named::Space) => {
Some(Message::ClearTheme)
}
_ => None,
})
}
fn theme(&self) -> Theme {
fn theme(&self) -> Option<Theme> {
self.theme.clone()
}
}
@ -210,9 +223,7 @@ mod tests {
.cloned()
.map(|theme| {
let mut styling = Styling::default();
styling.update(Message::ThemeChanged(theme));
let theme = styling.theme();
styling.update(Message::ThemeChanged(theme.clone()));
let mut ui = simulator(styling.view());
let snapshot = ui.snapshot(&theme)?;

View file

@ -7,6 +7,6 @@ publish = false
[dependencies]
iced.workspace = true
iced.features = ["system"]
iced.features = ["sysinfo"]
bytesize = "1.1"

View file

@ -1,5 +1,6 @@
use iced::system;
use iced::widget::{button, center, column, text};
use iced::{Element, Task, system};
use iced::{Element, Task};
pub fn main() -> iced::Result {
iced::application(Example::new, Example::update, Example::view).run()
@ -26,7 +27,7 @@ impl Example {
fn new() -> (Self, Task<Message>) {
(
Self::Loading,
system::fetch_information().map(Message::InformationReceived),
system::information().map(Message::InformationReceived),
)
}

View file

@ -8,7 +8,7 @@ use iced::{Center, Element, Fill, Font, Right, Theme};
pub fn main() -> iced::Result {
iced::application(Table::new, Table::update, Table::view)
.theme(|_| Theme::CatppuccinMocha)
.theme(Theme::CatppuccinMocha)
.run()
}

View file

@ -1 +1 @@
99f418007af163f172e163565f166da31015521e1bf7de95fa55cda2fb5a7db5
0acb67235c6a11014a2d2b825e0a70069bca0c67bee0cdb38a0144fc72b25220

View file

@ -6,7 +6,7 @@ use iced::widget::{
use iced::window;
use iced::{
Application, Center, Element, Fill, Font, Function, Preset, Program,
Subscription, Task as Command,
Subscription, Task as Command, Theme,
};
use serde::{Deserialize, Serialize};
@ -156,7 +156,7 @@ impl Todos {
operation::focus_next()
}
}
Message::ToggleFullscreen(mode) => window::get_latest()
Message::ToggleFullscreen(mode) => window::latest()
.and_then(move |window| window::set_mode(window, mode)),
Message::Loaded(_) => Command::none(),
};
@ -199,7 +199,7 @@ impl Todos {
let title = text("todos")
.width(Fill)
.size(100)
.color([0.5, 0.5, 0.5])
.style(subtle)
.align_x(Center);
let input = text_input("What needs to be done?", input_value)
@ -452,7 +452,7 @@ fn empty_message(message: &str) -> Element<'_, Message> {
.width(Fill)
.size(25)
.align_x(Center)
.color([0.7, 0.7, 0.7]),
.style(subtle),
)
.height(200)
.into()
@ -465,6 +465,7 @@ fn icon(unicode: char) -> Text<'static> {
.font(Font::with_name("Iced-Todos-Icons"))
.width(20)
.align_x(Center)
.shaping(text::Shaping::Basic)
}
fn edit_icon() -> Text<'static> {
@ -475,6 +476,12 @@ fn delete_icon() -> Text<'static> {
icon('\u{F1F8}')
}
fn subtle(theme: &Theme) -> text::Style {
text::Style {
color: Some(theme.extended_palette().background.strongest.color),
}
}
// Persistence
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SavedState {
@ -625,6 +632,7 @@ mod tests {
}
#[test]
#[ignore]
fn it_creates_a_new_task() -> Result<(), Error> {
let (mut todos, _command) = Todos::new();
let _command = todos.update(Message::Loaded(Err(LoadError::File)));

View file

@ -1,11 +1,10 @@
use iced::border;
use iced::widget::{Button, Column, Container, Slider};
use iced::widget::{
button, center_x, center_y, checkbox, column, horizontal_space, image,
radio, rich_text, row, scrollable, slider, span, text, text_input, toggler,
vertical_space,
};
use iced::{Center, Color, Element, Fill, Font, Pixels, Theme};
use iced::{Center, Color, Element, Fill, Font, Pixels, color};
pub fn main() -> iced::Result {
#[cfg(target_arch = "wasm32")]
@ -201,7 +200,7 @@ impl Tour {
Self::container("Welcome!")
.push(
"This is a simple tour meant to showcase a bunch of \
widgets that can be easily implemented on top of Iced.",
widgets that come bundled in Iced.",
)
.push(
"Iced is a cross-platform GUI library for Rust focused on \
@ -216,28 +215,19 @@ impl Tour {
built on top of wgpu, a graphics library supporting Vulkan, \
Metal, DX11, and DX12.",
)
.push({
let theme = Theme::default();
let palette = theme.extended_palette();
.push(
rich_text![
"Additionally, this tour can also run on WebAssembly ",
"by leveraging ",
span("trunk")
.color(palette.primary.base.color)
.background(palette.background.weakest.color)
.border(
border::rounded(2)
.width(1)
.color(palette.background.weak.color)
)
.padding([0, 2])
.color(color!(0x7777FF))
.underline(true)
.font(Font::MONOSPACE)
.link(Message::OpenTrunk),
"."
]
.on_link_click(std::convert::identity)
})
.on_link_click(std::convert::identity),
)
.push(
"You will need to interact with the UI in order to reach \
the end!",

View file

@ -11,7 +11,7 @@ pub fn main() -> iced::Result {
VectorialText::update,
VectorialText::view,
)
.theme(|_| Theme::Dark)
.theme(Theme::Dark)
.run()
}

View file

@ -13,7 +13,7 @@ use iced::{
pub fn main() -> iced::Result {
iced::application(Example::default, Example::update, Example::view)
.subscription(Example::subscription)
.theme(|_| Theme::Dark)
.theme(Theme::Dark)
.run()
}