Merge branch 'master' into feature/test-recorder

This commit is contained in:
Héctor Ramón Jiménez 2025-08-12 22:26:43 +02:00
commit 26c9dc1709
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
83 changed files with 2627 additions and 1208 deletions

View file

@ -369,6 +369,7 @@ impl Pipeline {
color_attachments: &[Some(
wgpu::RenderPassColorAttachment {
view: target,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
@ -568,6 +569,7 @@ impl DepthPipeline {
label: Some("cubes.pipeline.depth_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,

View file

@ -119,10 +119,10 @@ impl Editor {
let mut text = self.content.text();
if let Some(ending) = self.content.line_ending() {
if !text.ends_with(ending.as_str()) {
text.push_str(ending.as_str());
}
if let Some(ending) = self.content.line_ending()
&& !text.ends_with(ending.as_str())
{
text.push_str(ending.as_str());
}
Task::perform(

View file

@ -29,7 +29,7 @@ impl Image {
.get("https://civitai.com/api/v1/images")
.query(&[
("sort", "Most Reactions"),
("period", "Week"),
("period", "Month"),
("nsfw", "None"),
("limit", &Image::LIMIT.to_string()),
])

View file

@ -10,7 +10,7 @@ use iced::animation;
use iced::time::{Instant, milliseconds};
use iced::widget::{
button, container, float, grid, horizontal_space, image, mouse_area,
opaque, pop, scrollable, stack,
opaque, scrollable, sensor, stack,
};
use iced::window;
use iced::{
@ -257,7 +257,7 @@ fn card<'a>(
.style(button::text)
.into()
} else {
pop(card)
sensor(card)
.on_show(|_| Message::ImagePoppedIn(metadata.id))
.into()
}

View file

@ -96,17 +96,14 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
let capabilities = surface.get_capabilities(&adapter);
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: adapter_features
& wgpu::Features::default(),
required_limits: wgpu::Limits::default(),
memory_hints:
wgpu::MemoryHints::MemoryUsage,
},
None,
)
.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features: adapter_features
& wgpu::Features::default(),
required_limits: wgpu::Limits::default(),
memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: wgpu::Trace::Off,
})
.await
.expect("Request device");

View file

@ -24,6 +24,7 @@ impl Scene {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({

View file

@ -6,7 +6,7 @@ use iced::highlighter;
use iced::time::{self, Instant, milliseconds};
use iced::widget::{
self, button, center_x, container, horizontal_space, hover, image,
markdown, pop, right, row, scrollable, text_editor, toggler,
markdown, right, row, scrollable, sensor, text_editor, toggler,
};
use iced::window;
use iced::{
@ -267,7 +267,7 @@ impl<'a> markdown::Viewer<'a, Message> for CustomViewer<'a> {
)
.into()
} else {
pop(horizontal_space())
sensor(horizontal_space())
.key_ref(url.as_str())
.delay(milliseconds(500))
.on_show(|_size| Message::ImageShown(url.clone()))

View file

@ -71,11 +71,10 @@ impl Example {
}
}
Message::FocusAdjacent(direction) => {
if let Some(pane) = self.focus {
if let Some(adjacent) = self.panes.adjacent(pane, direction)
{
self.focus = Some(adjacent);
}
if let Some(pane) = self.focus
&& let Some(adjacent) = self.panes.adjacent(pane, direction)
{
self.focus = Some(adjacent);
}
}
Message::Clicked(pane) => {
@ -106,14 +105,12 @@ impl Example {
}
}
Message::CloseFocused => {
if let Some(pane) = self.focus {
if let Some(Pane { is_pinned, .. }) = self.panes.get(pane) {
if !is_pinned {
if let Some((_, sibling)) = self.panes.close(pane) {
self.focus = Some(sibling);
}
}
}
if let Some(pane) = self.focus
&& let Some(Pane { is_pinned, .. }) = self.panes.get(pane)
&& !is_pinned
&& let Some((_, sibling)) = self.panes.close(pane)
{
self.focus = Some(sibling);
}
}
}
@ -276,13 +273,13 @@ fn view_content<'a>(
button(
"Split vertically",
Message::Split(pane_grid::Axis::Vertical, pane),
)
),
if total_panes > 1 && !is_pinned {
Some(button("Close", Message::Close(pane)).style(button::danger))
} else {
None
}
]
.push_maybe(if total_panes > 1 && !is_pinned {
Some(button("Close", Message::Close(pane)).style(button::danger))
} else {
None
})
.spacing(5)
.max_width(160);
@ -300,7 +297,7 @@ fn view_controls<'a>(
is_pinned: bool,
is_maximized: bool,
) -> Element<'a, Message> {
let row = row![].spacing(5).push_maybe(if total_panes > 1 {
let maximize = if total_panes > 1 {
let (content, message) = if is_maximized {
("Restore", Message::Restore)
} else {
@ -315,7 +312,7 @@ fn view_controls<'a>(
)
} else {
None
});
};
let close = button(text("Close").size(14))
.style(button::danger)
@ -326,7 +323,7 @@ fn view_controls<'a>(
None
});
row.push(close).into()
row![maximize, close].spacing(5).into()
}
mod style {

View file

@ -88,18 +88,18 @@ impl QRGenerator {
input,
row![toggle_total_size, choose_theme]
.spacing(20)
.align_y(Center)
.align_y(Center),
self.total_size.map(|total_size| {
slider(Self::SIZE_RANGE, total_size, Message::TotalSizeChanged)
}),
self.qr_code.as_ref().map(|data| {
if let Some(total_size) = self.total_size {
qr_code(data).total_size(total_size)
} else {
qr_code(data).cell_size(10.0)
}
})
]
.push_maybe(self.total_size.map(|total_size| {
slider(Self::SIZE_RANGE, total_size, Message::TotalSizeChanged)
}))
.push_maybe(self.qr_code.as_ref().map(|data| {
if let Some(total_size) = self.total_size {
qr_code(data).total_size(total_size)
} else {
qr_code(data).cell_size(10.0)
}
}))
.width(700)
.spacing(20)
.align_x(Center);

View file

@ -158,15 +158,15 @@ impl Example {
.spacing(10)
.align_y(Center);
let crop_controls =
column![crop_origin_controls, crop_dimension_controls]
.push_maybe(
self.crop_error
.as_ref()
.map(|error| text!("Crop error! \n{error}")),
)
.spacing(10)
.align_x(Center);
let crop_controls = column![
crop_origin_controls,
crop_dimension_controls,
self.crop_error
.as_ref()
.map(|error| text!("Crop error! \n{error}")),
]
.spacing(10)
.align_x(Center);
let controls = {
let save_result =
@ -208,8 +208,8 @@ impl Example {
]
.spacing(10)
.align_x(Center),
save_result.map(text)
]
.push_maybe(save_result.map(text))
.spacing(40)
};

View file

@ -1 +1 @@
30570747bb062e9f7730cdd58be961c84bcf4711a6983185bff6d903e8d29e9c
0650eb2c27c21c5d48e1e00031a52d8471d8a3b4e827ad502c4628914f5c1c13

View file

@ -1 +1 @@
d5a086a08544f98087189bd4ece8815e5290722a07cd580b933f1bf77a040c52
ae1da92064373838152ac163072ee68135f530e0fef8146a01aea1df5cfdb494

View file

@ -1 +1 @@
30e523961db89a3ee97ad1eac09e727ecb3dec485faa362534a9f5ad083b32dd
8466dc0975c0bc7c06ed3c45df51e99b9d384394f8c3689b15231a872ba1262f

View file

@ -1 +1 @@
bce5427d5105f68e1d7fa18a34fcc551cb78c2fefd9a583ba44686331133436d
1c8f13cfb5d0bbbeb24b80ed35671e8a93d208d79ac4dbd069fe65c4a53c50c2

View file

@ -1 +1 @@
c8a7edbd5a8bbf559134b84253e14e65340f4ffe3e22c272b21c8438e47ffaf7
a1d30652db2cce98b5b86e8e29d776e2fc9091056aff8861cd54fa061161ed47

View file

@ -1 +1 @@
63d646b22d3dffbb56dac2e3f345090bd26625a388dd6cc142359f2a7ac9c8df
8c01615169803510f1cd4d051721b415adc7147672238aff1275fa3741edb507

View file

@ -1 +1 @@
d26f55674cbd96bc3b534ffdd098a13199718ef9c5ffe8ece0882ddab714b776
0b10823a1d218c145214ff2dcf751584669a3ca1d3e777a2cd618479a809523e

View file

@ -1 +1 @@
482c44c13d4ff3de19e71f3dddf93bbee170e54e2d353e818811069de28e18ed
26bc668c55650c6c25a14f76feeb1d1f78a96835aaac7a5f57b48b838cb28b14

View file

@ -1 +1 @@
6738cc4fc6eb8a5d406c613a4b0f08c0e8dcd2c1a5444445eebd3888f9303841
228eb8d64eed2f3726d27490b88a4519e36979a0ccfb0db8e164c5e5296b0739

View file

@ -1 +1 @@
0a918c52538fc4848aa0c68d8f2d6f4c981ed68971dd9c725f0093a39ef7f353
d579b14db1650e907f925302f23c53ebaba370aef6410cfa48fef70ab3138d1f

View file

@ -1 +1 @@
de3e1a2c21e1a86d76ca99989c73e8a2596ef627bba95d246fab8f02d56bd0af
896072b46221f83e1edaa37574436af6474969625f5c1a41cc5ddc2e20823cee

View file

@ -1 +1 @@
3418ea4eb0f7786607ef02e7db4bc97309530f2f7c08f8aea15c768a13a09ca4
60e1c95159caddb8bd7b8360e32ffd75472be37c4fcbd8ad23dabd0d000a4ec1

View file

@ -1 +1 @@
c8474e02a9df23f123816a489c1ea7ae6cb994a0eca429592dfe6d933de1beee
2e3c4ea86b5bd968b8ec77a7ec7b5b7ae29d5ee8e4b68a216c1fa11d92c015bc

View file

@ -1 +1 @@
02095fd09c078be02dc41e29e55de25e8a79e6ad4293aa7e430257a9016dfb3d
79ffced2a78689bac1a40ab154a478b4fff87154ee4a8bbf023d922c86b7d53b

View file

@ -1 +1 @@
d82588a2aba3e7211f25b85ebb812a42dfa59137dd4b59d26f5f60d5b28e537f
17c632cbef607502ed2c438f409a1c9bee382d9084c38772021f1f2a4ad3908c

View file

@ -1 +1 @@
d6b73545929cc7794c1a918f069b5326ef129bed8f9ad2cd001be7d078a2b6a0
359f7f2c1d7f87e6e0eb80a9a28f70f033d6321ba028d32bc372030b718ed481

View file

@ -1 +1 @@
0ec7251c69755becd678b7aec398a275edf31cc077960723cd6b9364e8678548
a908d8f154f2baf67455380b5d8b39003c08ba0c80f39e71d4bcd2377bc784fc

View file

@ -1 +1 @@
4a15c475d45cf8eb0ccd6727cf6e493bd8c22454610b167a632a2328308faed1
8d6c2bab1f6e9a8db1e2acc8eb76334170e046b709a36dd4ad4d86f8d47346a4

View file

@ -1 +1 @@
49a41af93e89aab0a4e352e9cedfba3c6e18caf4267955c9d362bad40264a165
2010df2e80bfc72e7e9274de07b77dc4843485f6be38266fdfb7a4f129d75da1

View file

@ -1 +1 @@
8fcd80d4569dafdac4b4452b8ca8ab0cdceeb755f3c83d374ccd5ed4d0e8d43d
74812d50467787ce39a33ad6bc89411d7b8bc0b13e1bbd45838fcc27c75aee98

View file

@ -1 +1 @@
c37a32784c769c046f3aa881914b121af373b8c6e175ced89304d15b626a653a
b04218ee65cd446b142596a2cd9ff69d5267969af86026a4ff394f3c13a4d842

View file

@ -1 +1 @@
533d25575e8bf1111036fb082b424d0d0e60947a7da8428ab8c71e0bda01469e
e1cbe8742f000921c86924056e9a45f95ee2a2a973743bf9f37fee65baccfb9b

View file

@ -1,10 +1,10 @@
use iced::keyboard;
use iced::widget::{
button, center, checkbox, column, container, horizontal_rule, pick_list,
progress_bar, row, scrollable, slider, text, text_input, toggler,
vertical_rule, vertical_space,
button, center_x, center_y, checkbox, column, container, horizontal_rule,
pick_list, progress_bar, row, scrollable, slider, text, text_input,
toggler, vertical_rule, vertical_space,
};
use iced::{Center, Element, Fill, Subscription, Theme};
use iced::{Center, Element, Fill, Shrink, Subscription, Theme};
pub fn main() -> iced::Result {
iced::application(Styling::default, Styling::update, Styling::view)
@ -78,38 +78,64 @@ impl Styling {
.padding(10)
.size(20);
let styled_button = |label| {
button(text(label).width(Fill).center())
.padding(10)
.on_press(Message::ButtonPressed)
};
let buttons = {
let styles = [
("Primary", button::primary as fn(&Theme, _) -> _),
("Secondary", button::secondary),
("Success", button::success),
("Warning", button::warning),
("Danger", button::danger),
];
let primary = styled_button("Primary");
let success = styled_button("Success").style(button::success);
let warning = styled_button("Warning").style(button::warning);
let danger = styled_button("Danger").style(button::danger);
let styled_button =
|label| button(text(label).width(Fill).center()).padding(10);
column![
row(styles.into_iter().map(|(name, style)| styled_button(
name
)
.on_press(Message::ButtonPressed)
.style(style)
.into()))
.spacing(10)
.align_y(Center),
row(styles.into_iter().map(|(name, style)| styled_button(
name
)
.style(style)
.into()))
.spacing(10)
.align_y(Center),
]
.spacing(10)
};
let slider =
|| slider(0.0..=100.0, self.slider_value, Message::SliderChanged);
let progress_bar = || progress_bar(0.0..=100.0, self.slider_value);
let scrollable = scrollable(column![
let scroll_me = scrollable(column![
"Scroll me!",
vertical_space().height(800),
"You did it!"
])
.width(Fill)
.height(100);
.height(Fill);
let checkbox = checkbox("Check me!", self.checkbox_value)
let check = checkbox("Check me!", self.checkbox_value)
.on_toggle(Message::CheckboxToggled);
let toggler = toggler(self.toggler_value)
let check_disabled = checkbox("Disabled", self.checkbox_value);
let toggle = toggler(self.toggler_value)
.label("Toggle me!")
.on_toggle(Message::TogglerToggled)
.spacing(10);
let disabled_toggle =
toggler(self.toggler_value).label("Disabled").spacing(10);
let card = {
container(
column![
@ -128,18 +154,17 @@ impl Styling {
choose_theme,
horizontal_rule(1),
text_input,
row![primary, success, warning, danger]
.spacing(10)
.align_y(Center),
buttons,
slider(),
progress_bar(),
row![
scrollable,
row![vertical_rule(1), column![checkbox, toggler].spacing(20)]
.spacing(20)
scroll_me,
vertical_rule(1),
column![check, check_disabled, toggle, disabled_toggle]
.spacing(10)
]
.spacing(10)
.height(100)
.height(Shrink)
.align_y(Center),
card
]
@ -147,7 +172,9 @@ impl Styling {
.padding(20)
.max_width(600);
center(content).into()
center_y(scrollable(center_x(content)).spacing(10))
.padding(10)
.into()
}
fn subscription(&self) -> Subscription<Message> {

10
examples/table/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "table"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2024"
publish = false
[dependencies]
iced.workspace = true
iced.features = ["debug"]

252
examples/table/src/main.rs Normal file
View file

@ -0,0 +1,252 @@
use iced::font;
use iced::time::{Duration, hours, minutes};
use iced::widget::{
center_x, center_y, column, container, row, scrollable, slider, table,
text, tooltip,
};
use iced::{Center, Element, Fill, Font, Right, Theme};
pub fn main() -> iced::Result {
iced::application(Table::new, Table::update, Table::view)
.theme(|_| Theme::CatppuccinMocha)
.run()
}
struct Table {
events: Vec<Event>,
padding: (f32, f32),
separator: (f32, f32),
}
#[derive(Debug, Clone)]
enum Message {
PaddingChanged(f32, f32),
SeparatorChanged(f32, f32),
}
impl Table {
fn new() -> Self {
Self {
events: Event::list(),
padding: (10.0, 5.0),
separator: (1.0, 1.0),
}
}
fn update(&mut self, message: Message) {
match message {
Message::PaddingChanged(x, y) => self.padding = (x, y),
Message::SeparatorChanged(x, y) => self.separator = (x, y),
}
}
fn view(&self) -> Element<'_, Message> {
let table = {
let bold = |header| {
text(header).font(Font {
weight: font::Weight::Bold,
..Font::DEFAULT
})
};
let columns = [
table::column(bold("Name"), |event: &Event| text(&event.name)),
table::column(bold("Time"), |event: &Event| {
let minutes = event.duration.as_secs() / 60;
text!("{minutes} min").style(if minutes > 90 {
text::warning
} else {
text::default
})
})
.align_x(Right)
.align_y(Center),
table::column(bold("Price"), |event: &Event| {
if event.price > 0.0 {
text!("${:.2}", event.price).style(
if event.price > 100.0 {
text::warning
} else {
text::default
},
)
} else {
text("Free").style(text::success).width(Fill).center()
}
})
.align_x(Right)
.align_y(Center),
table::column(bold("Rating"), |event: &Event| {
text!("{:.2}", event.rating).style(if event.rating > 4.7 {
text::success
} else if event.rating < 2.0 {
text::danger
} else {
text::default
})
})
.align_x(Right)
.align_y(Center),
];
table(columns, &self.events)
.padding_x(self.padding.0)
.padding_y(self.padding.1)
.separator_x(self.separator.0)
.separator_y(self.separator.1)
};
let controls = {
let labeled_slider =
|label,
range: std::ops::RangeInclusive<f32>,
(x, y),
on_change: fn(f32, f32) -> Message| {
row![
text(label).font(Font::MONOSPACE).size(14).width(100),
tooltip(
slider(range.clone(), x, move |x| on_change(x, y)),
text!("{x:.0}px").font(Font::MONOSPACE).size(10),
tooltip::Position::Left
),
tooltip(
slider(range, y, move |y| on_change(x, y)),
text!("{y:.0}px").font(Font::MONOSPACE).size(10),
tooltip::Position::Right
),
]
.spacing(10)
.align_y(Center)
};
column![
labeled_slider(
"Padding",
0.0..=30.0,
self.padding,
Message::PaddingChanged
),
labeled_slider(
"Separator",
0.0..=5.0,
self.separator,
Message::SeparatorChanged
)
]
.spacing(10)
.width(400)
};
column![
center_y(scrollable(center_x(table)).spacing(10)).padding(10),
center_x(controls).padding(10).style(container::dark)
]
.into()
}
}
struct Event {
name: String,
duration: Duration,
price: f32,
rating: f32,
}
impl Event {
fn list() -> Vec<Self> {
vec![
Event {
name: "Get lost in a hacker bookstore".to_owned(),
duration: hours(2),
price: 0.0,
rating: 4.9,
},
Event {
name: "Buy vintage synth at Noisebridge flea market".to_owned(),
duration: hours(1),
price: 150.0,
rating: 4.8,
},
Event {
name: "Eat a questionable hot dog at 2AM".to_owned(),
duration: minutes(20),
price: 5.0,
rating: 1.7,
},
Event {
name: "Ride the MUNI for the story".to_owned(),
duration: minutes(60),
price: 3.0,
rating: 4.1,
},
Event {
name: "Scream into the void from Twin Peaks".to_owned(),
duration: minutes(40),
price: 0.0,
rating: 4.9,
},
Event {
name: "Buy overpriced coffee and feel things".to_owned(),
duration: minutes(25),
price: 6.5,
rating: 4.5,
},
Event {
name: "Attend an underground robot poetry slam".to_owned(),
duration: hours(1),
price: 12.0,
rating: 4.8,
},
Event {
name: "Browse cursed tech at a retro computer fair".to_owned(),
duration: hours(2),
price: 10.0,
rating: 4.7,
},
Event {
name: "Try to order at a secret ramen place with no sign"
.to_owned(),
duration: minutes(50),
price: 14.0,
rating: 4.6,
},
Event {
name: "Join a spontaneous rooftop drone rave".to_owned(),
duration: hours(3),
price: 0.0,
rating: 4.9,
},
Event {
name: "Sketch a stranger at Dolores Park".to_owned(),
duration: minutes(45),
price: 0.0,
rating: 4.4,
},
Event {
name: "Visit the Museum of Obsolete APIs".to_owned(),
duration: hours(1),
price: 9.99,
rating: 4.2,
},
Event {
name: "Chase the last working payphone".to_owned(),
duration: minutes(35),
price: 0.25,
rating: 4.0,
},
Event {
name: "Trade zines with a punk on BART".to_owned(),
duration: minutes(30),
price: 3.5,
rating: 4.7,
},
Event {
name: "Get a tattoo of the Git logo".to_owned(),
duration: hours(1),
price: 200.0,
rating: 4.6,
},
]
}
}

View file

@ -1 +1 @@
0e355b080ad33905145e9f70a3b29e2481197c8fc8f42491acd5358238ebbd5f
99f418007af163f172e163565f166da31015521e1bf7de95fa55cda2fb5a7db5

View file

@ -1 +0,0 @@
804a1bb6d49e3b3158463202960447d9e7820b967280f41dd0c34c00d3edf2c3

View file

@ -142,17 +142,17 @@ impl Tour {
}
fn view(&self) -> Element<'_, Message> {
let controls =
row![]
.push_maybe(self.screen.previous().is_some().then(|| {
padded_button("Back")
.on_press(Message::BackPressed)
.style(button::secondary)
}))
.push(horizontal_space())
.push_maybe(self.can_continue().then(|| {
padded_button("Next").on_press(Message::NextPressed)
}));
let controls = row![
self.screen.previous().is_some().then(|| {
padded_button("Back")
.on_press(Message::BackPressed)
.style(button::secondary)
}),
horizontal_space(),
self.can_continue().then(|| {
padded_button("Next").on_press(Message::NextPressed)
})
];
let screen = match self.screen {
Screen::Welcome => self.welcome(),

View file

@ -119,11 +119,11 @@ impl WebSocket {
let mut button = button(text("Send").height(40).align_y(Center))
.padding([0, 20]);
if matches!(self.state, State::Connected(_)) {
if let Some(message) = echo::Message::new(&self.new_message) {
input = input.on_submit(Message::Send(message.clone()));
button = button.on_press(Message::Send(message));
}
if matches!(self.state, State::Connected(_))
&& let Some(message) = echo::Message::new(&self.new_message)
{
input = input.on_submit(Message::Send(message.clone()));
button = button.on_press(Message::Send(message));
}
row![input, button].spacing(10).align_y(Center)