Format Rust sources at 100 columns

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 4661f595f8
commit 610478f8c7
336 files changed
+23497 -7892

No files matched your search

+10 -4
View File
@@ -21,8 +21,8 @@ pub(super) fn view<'a>(
opacity: p,
} = style;
if let Some(a) = &shell.status.audio {
let mut volume_heading =
row![shell.render.text("Volume").size(13).width(Length::Fill)].align_y(Alignment::Center);
let mut volume_heading = row![shell.render.text("Volume").size(13).width(Length::Fill)]
.align_y(Alignment::Center);
if combined {
volume_heading = volume_heading.push(shell_switch(
shell.render,
@@ -35,7 +35,11 @@ pub(super) fn view<'a>(
}
let audio = column![
volume_heading,
shell.render.text(&a.output).size(12).class(theme::Text::Color(muted)),
shell
.render
.text(&a.output)
.size(12)
.class(theme::Text::Color(muted)),
slider_row(
shell.render,
audio_icon(a.volume, a.muted),
@@ -62,7 +66,9 @@ pub(super) fn heading<'a>(
mut heading: super::MenuHeading<'a>,
style: MenuStyle,
) -> super::MenuHeading<'a> {
let MenuStyle { theme, opacity: p, .. } = style;
let MenuStyle {
theme, opacity: p, ..
} = style;
if let Some(audio) = &shell.status.audio {
heading = heading.push(shell_switch(
shell.render,
+47 -24
View File
@@ -5,7 +5,9 @@ use cosmic::{Element, theme};
use super::Menu;
use crate::status::Snapshot;
use crate::{BarMetrics, FereseShell, Message, accented_icon, bar_content, color, motion, recording};
use crate::{
BarMetrics, FereseShell, Message, accented_icon, bar_content, color, motion, recording,
};
impl FereseShell {
pub(crate) fn view_media_item(
@@ -48,8 +50,9 @@ impl FereseShell {
} else {
status_icon(kind, &self.status)
};
let selected =
selected || (kind == Menu::Recording && matches!(self.recorder.state, recording::State::Recording(_)));
let selected = selected
|| (kind == Menu::Recording
&& matches!(self.recorder.state, recording::State::Recording(_)));
let foreground = color(if selected {
theme.accent
} else if enabled {
@@ -82,20 +85,17 @@ impl FereseShell {
.as_ref()
.is_some_and(|n| n.count > 0 && !n.dnd)
{
content = content.push(
container(self.render.text(""))
.width(4)
.height(4)
.class(theme::Container::custom(move |_| container::Style {
background: Some(Background::Color(foreground)),
border: Border {
shape: BorderShape::Continuous,
radius: self.render.radius(2.0).into(),
..Default::default()
},
content = content.push(container(self.render.text("")).width(4).height(4).class(
theme::Container::custom(move |_| container::Style {
background: Some(Background::Color(foreground)),
border: Border {
shape: BorderShape::Continuous,
radius: self.render.radius(2.0).into(),
..Default::default()
})),
);
},
..Default::default()
}),
));
}
let content = bar_content(content, metrics.group_item_height);
let recording_busy = kind == Menu::Recording && self.recorder.busy();
@@ -107,7 +107,10 @@ impl FereseShell {
} else {
status_label(kind, &self.status)
})
.padding([0.0, ((metrics.height - f32::from(metrics.icon_size)) * 0.5).max(4.0)])
.padding([
0.0,
((metrics.height - f32::from(metrics.icon_size)) * 0.5).max(4.0),
])
.height(metrics.group_item_height)
.on_press_with_rectangle(move |offset, bounds| {
if kind == Menu::Recording {
@@ -134,7 +137,10 @@ impl FereseShell {
fn status_label(kind: Menu, s: &Snapshot) -> String {
match kind {
Menu::Network => match &s.network {
Some(n) if n.enabled => format!("Wi-Fi: {}", n.connection.as_deref().unwrap_or("not connected")),
Some(n) if n.enabled => format!(
"Wi-Fi: {}",
n.connection.as_deref().unwrap_or("not connected")
),
_ => "Wi-Fi: off".into(),
},
Menu::Bluetooth => match &s.bluetooth {
@@ -204,7 +210,9 @@ pub(super) fn status_icon(kind: Menu, s: &Snapshot) -> (&'static [u8], bool) {
}
}
Menu::Bluetooth => match &s.bluetooth {
Some(b) if b.enabled && !b.devices.is_empty() => (ferese_theme::icons::BLUETOOTH_CONNECTED, true),
Some(b) if b.enabled && !b.devices.is_empty() => {
(ferese_theme::icons::BLUETOOTH_CONNECTED, true)
}
Some(b) if b.enabled => (ferese_theme::icons::BLUETOOTH_ON, true),
_ => (ferese_theme::icons::BLUETOOTH_OFF, false),
},
@@ -261,26 +269,41 @@ mod tests {
}),
..Default::default()
};
assert_eq!(status_icon(Menu::Audio, &s), (ferese_theme::icons::VOLUME_MUTE, false));
assert_eq!(
status_icon(Menu::Audio, &s),
(ferese_theme::icons::VOLUME_MUTE, false)
);
s.battery = Some(status::Battery {
percent: 2,
status: "Charging".into(),
});
assert_eq!(status_icon(Menu::Battery, &s).0, ferese_theme::icons::BATTERY_CHARGING);
assert_eq!(
status_icon(Menu::Battery, &s).0,
ferese_theme::icons::BATTERY_CHARGING
);
s.notifications = Some(status::Notifications { count: 3, dnd: true });
s.notifications = Some(status::Notifications {
count: 3,
dnd: true,
});
assert_eq!(
status_icon(Menu::Notifications, &s).0,
ferese_theme::icons::NOTIFICATIONS_OFF
);
assert_eq!(status_label(Menu::Notifications, &s), "Notifications: do not disturb");
assert_eq!(
status_label(Menu::Notifications, &s),
"Notifications: do not disturb"
);
s.bluetooth = Some(status::Bluetooth {
enabled: false,
devices: vec!["Headphones".into()],
});
assert_eq!(status_icon(Menu::Bluetooth, &s).0, ferese_theme::icons::BLUETOOTH_OFF);
assert_eq!(
status_icon(Menu::Bluetooth, &s).0,
ferese_theme::icons::BLUETOOTH_OFF
);
}
#[test]
+44 -8
View File
@@ -7,7 +7,11 @@ use super::{MenuRows, MenuStyle};
use crate::status::Action;
use crate::{FereseShell, Message, ShellTheme, bar_icon, color_with_opacity, motion};
pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn view<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
let MenuStyle {
theme,
primary: _,
@@ -37,9 +41,17 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
if let Some(profiles) = &shell.status.power_profiles {
let mut modes = row::with_capacity(3).spacing(6).width(Length::Fill);
for (index, (label, icon, profile)) in [
("Power saver", ferese_theme::icons::POWER_SAVER, "power-saver"),
(
"Power saver",
ferese_theme::icons::POWER_SAVER,
"power-saver",
),
("Balanced", ferese_theme::icons::POWER_BALANCED, "balanced"),
("Performance", ferese_theme::icons::POWER_PERFORMANCE, "performance"),
(
"Performance",
ferese_theme::icons::POWER_PERFORMANCE,
"performance",
),
]
.into_iter()
.enumerate()
@@ -89,18 +101,33 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
),
] {
if enabled {
power = power.push(power_tile_action(shell.render, label, icon, action, theme, p));
power = power.push(power_tile_action(
shell.render,
label,
icon,
action,
theme,
p,
));
}
}
if shell.status.reboot || shell.status.poweroff || shell.status.suspend {
rows = rows.push(ferese_theme::menus::section_label("Power", shell.render.font, muted));
rows = rows.push(ferese_theme::menus::section_label(
"Power",
shell.render.font,
muted,
));
rows = rows.push(power);
}
rows
}
pub(super) fn brightness<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn brightness<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
let MenuStyle {
theme,
primary,
@@ -135,7 +162,14 @@ fn power_tile<'a>(
theme: ShellTheme,
opacity: f32,
) -> Element<'a, cosmic::Action<Message>> {
let foreground = color_with_opacity(if selected { theme.accent } else { theme.text_primary }, opacity);
let foreground = color_with_opacity(
if selected {
theme.accent
} else {
theme.text_primary
},
opacity,
);
let tile = container(
column![bar_icon(icon, 19, foreground), render.text(label).size(11)]
.spacing(3)
@@ -146,7 +180,9 @@ fn power_tile<'a>(
let btn = button::custom(tile)
.width(Length::FillPortion(1))
.padding([8, 2])
.on_press(cosmic::Action::App(Message::Control(Action::PowerProfile(profile))));
.on_press(cosmic::Action::App(Message::Control(Action::PowerProfile(
profile,
))));
motion::button(render, btn, foreground, selected, opacity)
}
+11 -2
View File
@@ -14,7 +14,12 @@ fn device_list_height(count: usize) -> Option<f32> {
(count > 4).then_some(DEVICE_LIST_HEIGHT)
}
pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle, in_hub: bool) -> MenuRows<'a> {
pub(super) fn view<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
in_hub: bool,
) -> MenuRows<'a> {
let MenuStyle { muted, .. } = style;
if let Some(b) = &shell.status.bluetooth {
rows = rows.push(status_summary(
@@ -43,7 +48,11 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
devices = devices.push(
column![
shell.render.text(device).size(14),
shell.render.text("Connected").size(12).class(theme::Text::Color(muted)),
shell
.render
.text("Connected")
.size(12)
.class(theme::Text::Color(muted)),
]
.spacing(3),
);
+10 -5
View File
@@ -35,11 +35,16 @@ pub(super) fn hub<'a>(
let navigation = |label: &'static str, message: Message| {
crate::motion::button(
render,
cosmic::widget::button::custom(render.text(label).size(11).class(cosmic::theme::Text::Color(primary)))
.name(label)
.padding([2, 4])
.height(22)
.on_press(cosmic::Action::App(message)),
cosmic::widget::button::custom(
render
.text(label)
.size(11)
.class(cosmic::theme::Text::Color(primary)),
)
.name(label)
.padding([2, 4])
.height(22)
.on_press(cosmic::Action::App(message)),
primary,
false,
opacity,
+20 -9
View File
@@ -30,7 +30,11 @@ pub(super) fn status_summary<'a>(
.center_y(32)
.class(theme::Container::custom(move |_| container::Style {
background: Some(Background::Color(Color {
a: if enabled { 0.22 * opacity } else { 0.06 * opacity },
a: if enabled {
0.22 * opacity
} else {
0.06 * opacity
},
..badge_color
})),
border: Border {
@@ -58,7 +62,10 @@ pub(super) fn status_summary<'a>(
badge,
column![
render.text(title).size(14),
render.text(subtitle).size(12).class(theme::Text::Color(muted)),
render
.text(subtitle)
.size(12)
.class(theme::Text::Color(muted)),
]
.spacing(4)
.width(Length::Fill)
@@ -176,13 +183,17 @@ pub(super) fn slider_row(
icon_accent: Color,
opacity: f32,
) -> Element<'static, cosmic::Action<Message>> {
let control = slider(if brightness { 1..=100 } else { 0..=100 }, value, move |value| {
cosmic::Action::App(Message::Control(if brightness {
Action::Brightness(value)
} else {
Action::Volume(value)
}))
})
let control = slider(
if brightness { 1..=100 } else { 0..=100 },
value,
move |value| {
cosmic::Action::App(Message::Control(if brightness {
Action::Brightness(value)
} else {
Action::Volume(value)
}))
},
)
.width(Length::Fill)
.height(24)
.class(ferese_theme::menus::slider(foreground, opacity));
+37 -10
View File
@@ -10,7 +10,11 @@ use super::{Menu, OpenMenu, PopoverAnchor};
use crate::status::Action;
use crate::{FereseShell, Message};
fn menu_positioner(rectangle: Rectangle<i32>, height_limit: f32, edge: ferese_panel::Edge) -> SctkPositioner {
fn menu_positioner(
rectangle: Rectangle<i32>,
height_limit: f32,
edge: ferese_panel::Edge,
) -> SctkPositioner {
// xdg-positioner: bottom/bottom-left for top bars, top/top-left for bottom bars.
let (anchor, gravity, gap) = match edge {
ferese_panel::Edge::Top => (2u32, 6u32, 8),
@@ -21,7 +25,9 @@ fn menu_positioner(rectangle: Rectangle<i32>, height_limit: f32, edge: ferese_pa
anchor: anchor.try_into().unwrap(),
gravity: gravity.try_into().unwrap(),
offset: (rectangle.width / 2, gap),
size_limits: Limits::NONE.max_width(368.0).max_height(720.0_f32.max(height_limit)),
size_limits: Limits::NONE
.max_width(368.0)
.max_height(720.0_f32.max(height_limit)),
constraint_adjustment: 3,
..Default::default()
}
@@ -41,7 +47,10 @@ impl FereseShell {
pub fn open_menu(&mut self, kind: Menu, anchor: PopoverAnchor) -> Task<Message> {
if (self.snapshot.binding_mode.is_some() && anchor.item.is_some())
|| !anchor.belongs_to(&self.config.panels[0])
|| !self.outputs.iter().any(|output| output.bar == anchor.parent)
|| !self
.outputs
.iter()
.any(|output| output.bar == anchor.parent)
|| self.bar_hidden(anchor.parent)
|| !kind.available(&self.status)
{
@@ -60,7 +69,9 @@ impl FereseShell {
return close.chain(open);
}
if self.menu.as_ref().is_some_and(|menu| {
menu.kind == kind && menu.anchor.same_item(&anchor) && menu.anchor.rectangle == anchor.rectangle
menu.kind == kind
&& menu.anchor.same_item(&anchor)
&& menu.anchor.rectangle == anchor.rectangle
}) {
if let Some(menu) = &mut self.menu
&& menu.motion.closing()
@@ -94,7 +105,11 @@ impl FereseShell {
if !kind.available(&self.status) {
return Task::none();
}
if self.menu.as_ref().is_some_and(|menu| menu.kind == Menu::Notifications) {
if self
.menu
.as_ref()
.is_some_and(|menu| menu.kind == Menu::Notifications)
{
self.notifications.history_open = false;
self.notifications.hovered = None;
}
@@ -120,7 +135,9 @@ impl FereseShell {
menu.switch_panel(kind, self.config.animations, Instant::now());
return Task::batch([
notifications,
cosmic::iced::platform_specific::shell::commands::popup::reposition(menu.id, positioner),
cosmic::iced::platform_specific::shell::commands::popup::reposition(
menu.id, positioner,
),
]);
}
let id = window::Id::unique();
@@ -166,7 +183,9 @@ impl FereseShell {
}
Task::batch([
tooltip,
cosmic::task::message(cosmic::Action::Surface(cosmic::surface::action::destroy_popup(menu.id))),
cosmic::task::message(cosmic::Action::Surface(
cosmic::surface::action::destroy_popup(menu.id),
)),
self.sync_notification_surface(),
])
}
@@ -302,7 +321,9 @@ mod tests {
width: 32,
height: 30,
};
for (edge, anchor, gravity, gap) in [(Edge::Top, 2u32, 6u32, 8), (Edge::Bottom, 1u32, 5u32, -8)] {
for (edge, anchor, gravity, gap) in
[(Edge::Top, 2u32, 6u32, 8), (Edge::Bottom, 1u32, 5u32, -8)]
{
let positioner = menu_positioner(rectangle, 620., edge);
assert_eq!(positioner.anchor_rect, rectangle);
assert_eq!(positioner.anchor, anchor.try_into().unwrap());
@@ -408,7 +429,10 @@ mod tests {
},
now,
);
assert_eq!(menu.motion.progress_at(now + Duration::from_millis(60)), normal);
assert_eq!(
menu.motion.progress_at(now + Duration::from_millis(60)),
normal
);
}
#[test]
fn popup_anchors_reject_removed_hidden_items_and_other_panel_definitions() {
@@ -426,7 +450,10 @@ mod tests {
};
assert!(anchor.belongs_to(&panel));
let items = &mut panel.end.groups[0].items;
let network = items.iter().position(|item| item.id.0 == "network").unwrap();
let network = items
.iter()
.position(|item| item.id.0 == "network")
.unwrap();
items[network].visible = false;
assert!(!anchor.belongs_to(&panel));
panel.end.groups[0].items.remove(network);
+177 -92
View File
@@ -8,7 +8,8 @@ use serde_json::json;
use super::{Menu, MenuRows, MenuStyle};
use crate::{
BarMetrics, FereseShell, Message, ShellTheme, accented_icon, bar_content, color, color_with_opacity, motion,
BarMetrics, FereseShell, Message, ShellTheme, accented_icon, bar_content, color,
color_with_opacity, motion,
};
const SEEK_THUMB_DIAMETER: f32 = 10.0;
@@ -80,30 +81,39 @@ pub(super) fn bar<'a>(
let Some(player) = &shell.media.snapshot.selected else {
return shell.render.text("").into();
};
let foreground = color(if selected { palette.accent } else { palette.text_primary });
let vertical_padding = ((metrics.group_item_height - f32::from(metrics.icon_size)) * 0.5).clamp(0.0, 2.0);
let foreground = color(if selected {
palette.accent
} else {
palette.text_primary
});
let vertical_padding =
((metrics.group_item_height - f32::from(metrics.icon_size)) * 0.5).clamp(0.0, 2.0);
let height = metrics.group_item_height - 2.0 * vertical_padding;
let side = height.min(24.0);
let cover: Element<'_, cosmic::Action<Message>> =
match shell.media.artwork.as_ref().and_then(|art| art.handle.as_ref()) {
Some(handle) => image(handle.clone())
.width(side)
.height(side)
.content_fit(cosmic::iced::ContentFit::Cover)
.border_radius(shell.render.radius(6.0))
.shape(BorderShape::Continuous)
.into(),
None => container(accented_icon(
icons::MEDIA,
side as u16,
foreground,
color(palette.accent),
))
.center_x(side)
.center_y(side)
let cover: Element<'_, cosmic::Action<Message>> = match shell
.media
.artwork
.as_ref()
.and_then(|art| art.handle.as_ref())
{
Some(handle) => image(handle.clone())
.width(side)
.height(side)
.content_fit(cosmic::iced::ContentFit::Cover)
.border_radius(shell.render.radius(6.0))
.shape(BorderShape::Continuous)
.into(),
};
None => container(accented_icon(
icons::MEDIA,
side as u16,
foreground,
color(palette.accent),
))
.center_x(side)
.center_y(side)
.width(side)
.into(),
};
let label = shell
.render
.text(player.label())
@@ -120,7 +130,8 @@ pub(super) fn bar<'a>(
let preferred_label_width: f32 = if compact { 112. } else { 320. };
// Outer padding, the transport button, details padding, and cover spacing
// share the allocation with the label.
let chrome = 4. + 2. + f32::from(metrics.icon_size) + 12. + 8. + if compact { 0. } else { side + 8. };
let chrome =
4. + 2. + f32::from(metrics.icon_size) + 12. + 8. + if compact { 0. } else { side + 8. };
let label_width = width.map_or(preferred_label_width, |width| {
(width - chrome).max(0.).min(preferred_label_width)
});
@@ -150,7 +161,11 @@ pub(super) fn bar<'a>(
let playing = player.status == Playback::Playing;
let mut toggle = button::custom(bar_content(
accented_icon(
if playing { icons::MEDIA_PAUSE } else { icons::MEDIA_PLAY },
if playing {
icons::MEDIA_PAUSE
} else {
icons::MEDIA_PLAY
},
metrics.icon_size,
foreground,
color(palette.accent),
@@ -186,7 +201,9 @@ pub(super) fn bar<'a>(
let mut area = mouse_area(controls);
if player.can_raise {
area = area.on_middle_release(cosmic::Action::App(Message::MediaAction(shell.media.action("raise"))));
area = area.on_middle_release(cosmic::Action::App(Message::MediaAction(
shell.media.action("raise"),
)));
}
if player.can_control && player.volume.is_some() {
let action = shell.media.action("volume");
@@ -205,7 +222,11 @@ fn volume_delta(delta: mouse::ScrollDelta) -> f64 {
mouse::ScrollDelta::Lines { y, .. } => f64::from(y) * 0.05,
mouse::ScrollDelta::Pixels { y, .. } => f64::from(y) * 0.001,
};
if delta.is_finite() { delta.clamp(-1.0, 1.0) } else { 0.0 }
if delta.is_finite() {
delta.clamp(-1.0, 1.0)
} else {
0.0
}
}
fn transport<'a>(
@@ -228,7 +249,8 @@ fn transport_sized<'a>(
style: MenuStyle,
size: f32,
) -> Element<'a, cosmic::Action<Message>> {
let fill = (action == "play-pause").then_some(if enabled { style.primary } else { style.muted });
let fill =
(action == "play-pause").then_some(if enabled { style.primary } else { style.muted });
let foreground = match fill {
Some(fill) => cosmic::iced::Color {
a: fill.a,
@@ -247,7 +269,10 @@ fn transport_sized<'a>(
accented_icon(
source,
(size - 20.) as u16,
cosmic::iced::Color { a: 1., ..foreground },
cosmic::iced::Color {
a: 1.,
..foreground
},
color(style.theme.accent),
)
.opacity(foreground.a),
@@ -260,13 +285,19 @@ fn transport_sized<'a>(
.height(size)
.name(label);
if enabled {
control = control.on_press(cosmic::Action::App(Message::MediaAction(shell.media.action(action))));
control = control.on_press(cosmic::Action::App(Message::MediaAction(
shell.media.action(action),
)));
}
motion::circular_button(shell.render, control, foreground, fill, style.opacity, size)
}
pub(super) fn hub<'a>(shell: &'a FereseShell, rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn hub<'a>(
shell: &'a FereseShell,
rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
let Some(player) = &shell.media.snapshot.selected else {
return rows.push(
shell
@@ -276,31 +307,35 @@ pub(super) fn hub<'a>(shell: &'a FereseShell, rows: MenuRows<'a>, style: MenuSty
.class(theme::Text::Color(style.muted)),
);
};
let cover: Element<'_, cosmic::Action<Message>> =
match shell.media.artwork.as_ref().and_then(|art| art.handle.as_ref()) {
Some(handle) => image(handle.clone())
.width(64)
.height(64)
.opacity(style.opacity)
.content_fit(cosmic::iced::ContentFit::Cover)
.border_radius(10.)
.into(),
None => container(
accented_icon(
icons::MEDIA,
26,
color(style.theme.text_primary),
color(style.theme.accent),
)
.opacity(style.opacity),
)
.center_x(64)
.center_y(64)
.class(theme::Container::custom(move |_| {
ferese_theme::controls::surface_appearance(style.primary.scale_alpha(0.06), 10.)
}))
let cover: Element<'_, cosmic::Action<Message>> = match shell
.media
.artwork
.as_ref()
.and_then(|art| art.handle.as_ref())
{
Some(handle) => image(handle.clone())
.width(64)
.height(64)
.opacity(style.opacity)
.content_fit(cosmic::iced::ContentFit::Cover)
.border_radius(10.)
.into(),
};
None => container(
accented_icon(
icons::MEDIA,
26,
color(style.theme.text_primary),
color(style.theme.accent),
)
.opacity(style.opacity),
)
.center_x(64)
.center_y(64)
.class(theme::Container::custom(move |_| {
ferese_theme::controls::surface_appearance(style.primary.scale_alpha(0.06), 10.)
}))
.into(),
};
let mut info = column![
shell
.render
@@ -327,8 +362,9 @@ pub(super) fn hub<'a>(shell: &'a FereseShell, rows: MenuRows<'a>, style: MenuSty
player.length_us.filter(|length| *length > 0),
player.position_at(shell.media.now_us),
) {
let value =
(shell.media.seeking.unwrap_or(position).min(length) as f64 / length as f64 * 10_000.).round() as u32;
let value = (shell.media.seeking.unwrap_or(position).min(length) as f64 / length as f64
* 10_000.)
.round() as u32;
if player.can_control && player.can_seek && player.track_id.is_some() {
info = info.push(
slider(0..=10_000_u32, value, move |value| {
@@ -358,7 +394,11 @@ pub(super) fn hub<'a>(shell: &'a FereseShell, rows: MenuRows<'a>, style: MenuSty
),
transport_sized(
shell,
if playing { icons::MEDIA_PAUSE } else { icons::MEDIA_PLAY },
if playing {
icons::MEDIA_PAUSE
} else {
icons::MEDIA_PLAY
},
if playing { "Pause" } else { "Play" },
"play-pause",
player.can_toggle(),
@@ -381,22 +421,35 @@ pub(super) fn hub<'a>(shell: &'a FereseShell, rows: MenuRows<'a>, style: MenuSty
)
}
pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn view<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
if let Some(player) = &shell.media.snapshot.selected {
let cover: Element<'_, cosmic::Action<Message>> =
match shell.media.artwork.as_ref().and_then(|art| art.handle.as_ref()) {
Some(handle) => image(handle.clone())
.width(64)
.height(64)
.content_fit(cosmic::iced::ContentFit::Contain)
.border_radius(shell.render.radius(12.0))
.shape(BorderShape::Continuous)
.into(),
None => container(accented_icon(icons::MEDIA, 30, style.muted, color(style.theme.accent)))
.center_x(64)
.center_y(64)
.into(),
};
let cover: Element<'_, cosmic::Action<Message>> = match shell
.media
.artwork
.as_ref()
.and_then(|art| art.handle.as_ref())
{
Some(handle) => image(handle.clone())
.width(64)
.height(64)
.content_fit(cosmic::iced::ContentFit::Contain)
.border_radius(shell.render.radius(12.0))
.shape(BorderShape::Continuous)
.into(),
None => container(accented_icon(
icons::MEDIA,
30,
style.muted,
color(style.theme.accent),
))
.center_x(64)
.center_y(64)
.into(),
};
let text_size = if player.label().len() >= 24 { 13 } else { 15 };
let mut info = column![
shell
@@ -470,20 +523,23 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
} else {
progress = progress.push(seek_pill(
container(
cosmic::iced::widget::ProgressBar::new(0.0..=1.0, (position as f64 / length as f64) as f32)
.girth(4)
.length(Length::Fill)
.class(cosmic::theme::iced::ProgressBar::custom(move |_| {
cosmic::iced::widget::progress_bar::Style {
background: style.muted.scale_alpha(0.35).into(),
bar: color_with_opacity(style.theme.accent, style.opacity).into(),
border: cosmic::iced::Border {
shape: BorderShape::Circular,
radius: 2.0.into(),
..Default::default()
},
}
})),
cosmic::iced::widget::ProgressBar::new(
0.0..=1.0,
(position as f64 / length as f64) as f32,
)
.girth(4)
.length(Length::Fill)
.class(cosmic::theme::iced::ProgressBar::custom(move |_| {
cosmic::iced::widget::progress_bar::Style {
background: style.muted.scale_alpha(0.35).into(),
bar: color_with_opacity(style.theme.accent, style.opacity).into(),
border: cosmic::iced::Border {
shape: BorderShape::Circular,
radius: 2.0.into(),
..Default::default()
},
}
})),
)
.center_y(20),
style,
@@ -525,7 +581,11 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
),
transport(
shell,
if playing { icons::MEDIA_PAUSE } else { icons::MEDIA_PLAY },
if playing {
icons::MEDIA_PAUSE
} else {
icons::MEDIA_PLAY
},
if playing { "Pause" } else { "Play" },
"play-pause",
player.can_toggle(),
@@ -597,7 +657,11 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
shell.render,
label,
Message::MediaAction(pin),
if player.ignored { style.muted } else { style.primary },
if player.ignored {
style.muted
} else {
style.primary
},
style.opacity
),
super::controls::menu_button(
@@ -616,7 +680,13 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
}
if let Some(error) = &shell.media.error {
rows = rows.push(shell.render.text(error).size(12).class(theme::Text::Color(style.muted)));
rows = rows.push(
shell
.render
.text(error)
.size(12)
.class(theme::Text::Color(style.muted)),
);
}
rows
@@ -645,9 +715,24 @@ mod tests {
#[test]
fn wheel_changes_are_bounded_and_pixel_scrolling_is_not_a_full_step() {
assert_eq!(volume_delta(mouse::ScrollDelta::Lines { x: 0.0, y: 1.0 }), 0.05);
assert_eq!(volume_delta(mouse::ScrollDelta::Pixels { x: 0.0, y: 1.0 }), 0.001);
assert_eq!(volume_delta(mouse::ScrollDelta::Lines { x: 0.0, y: f32::NAN }), 0.0);
assert_eq!(volume_delta(mouse::ScrollDelta::Lines { x: 0.0, y: 1000.0 }), 1.0);
assert_eq!(
volume_delta(mouse::ScrollDelta::Lines { x: 0.0, y: 1.0 }),
0.05
);
assert_eq!(
volume_delta(mouse::ScrollDelta::Pixels { x: 0.0, y: 1.0 }),
0.001
);
assert_eq!(
volume_delta(mouse::ScrollDelta::Lines {
x: 0.0,
y: f32::NAN
}),
0.0
);
assert_eq!(
volume_delta(mouse::ScrollDelta::Lines { x: 0.0, y: 1000.0 }),
1.0
);
}
}
+77 -27
View File
@@ -19,9 +19,11 @@ use status::{Action, Snapshot};
use super::*;
type MenuHeading<'a> = cosmic::widget::Row<'a, cosmic::Action<Message>, cosmic::Theme, cosmic::Renderer>;
type MenuHeading<'a> =
cosmic::widget::Row<'a, cosmic::Action<Message>, cosmic::Theme, cosmic::Renderer>;
type MenuRows<'a> = cosmic::widget::Column<'a, cosmic::Action<Message>, cosmic::Theme, cosmic::Renderer>;
type MenuRows<'a> =
cosmic::widget::Column<'a, cosmic::Action<Message>, cosmic::Theme, cosmic::Renderer>;
#[derive(Clone, Copy)]
struct MenuStyle {
@@ -59,7 +61,10 @@ impl Menu {
) | (Self::Network, Action::Wifi(_))
| (Self::Bluetooth, Action::Bluetooth(_))
| (Self::Audio, Action::Volume(_) | Action::Mute(_))
| (Self::Battery, Action::Brightness(_) | Action::PowerProfile(_))
| (
Self::Battery,
Action::Brightness(_) | Action::PowerProfile(_)
)
| (Self::Notifications, Action::Dnd(_) | Action::Notifications)
);
relevant.then_some(error.message.as_str())
@@ -132,10 +137,9 @@ pub(crate) struct PopoverAnchor {
impl PopoverAnchor {
pub(crate) fn belongs_to(&self, panel: &crate::panel::Panel) -> bool {
self.panel == panel.id
&& self
.item
.as_ref()
.is_none_or(|id| id.0 == "_overflow" || panel.item(id).is_some_and(|item| item.visible))
&& self.item.as_ref().is_none_or(|id| {
id.0 == "_overflow" || panel.item(id).is_some_and(|item| item.visible)
})
}
pub fn same_item(&self, other: &Self) -> bool {
@@ -176,7 +180,11 @@ impl OpenMenu {
}
impl FereseShell {
pub(crate) fn view_hub_menu(&self, kind: Menu, opacity: f32) -> Element<'_, cosmic::Action<Message>> {
pub(crate) fn view_hub_menu(
&self,
kind: Menu,
opacity: f32,
) -> Element<'_, cosmic::Action<Message>> {
let theme = self.config.theme;
let style = MenuStyle {
theme,
@@ -216,7 +224,12 @@ impl FereseShell {
Menu::Bluetooth => bluetooth::view(self, rows, style, true),
Menu::Audio => audio::view(self, rows, style, false),
Menu::Battery => battery::view(self, rows, style),
Menu::Calendar => rows.push(calendar::hub(self.render, self.calendar_offset, theme, opacity)),
Menu::Calendar => rows.push(calendar::hub(
self.render,
self.calendar_offset,
theme,
opacity,
)),
Menu::Media => media::hub(self, rows, style),
Menu::Notifications => {
let mut rows = notification_controls(self, rows, style, true);
@@ -305,8 +318,17 @@ impl FereseShell {
.class(theme::Text::Color(style.muted)),
);
}
if let Some(error) = self.status_error.as_ref().and_then(|error| kind.action_error(error)) {
rows = rows.push(self.render.text(error).size(12).class(theme::Text::Color(style.muted)));
if let Some(error) = self
.status_error
.as_ref()
.and_then(|error| kind.action_error(error))
{
rows = rows.push(
self.render
.text(error)
.size(12)
.class(theme::Text::Color(style.muted)),
);
}
container(rows)
.width(Length::Fill)
@@ -377,7 +399,12 @@ impl FereseShell {
.width(Length::Fixed(22.))
.into()
};
let badge = ferese_theme::menus::badge(heading_icon, color(theme.accent), p, self.render.radius(20.));
let badge = ferese_theme::menus::badge(
heading_icon,
color(theme.accent),
p,
self.render.radius(20.),
);
let mut heading = ferese_theme::menus::heading(badge, kind.title(), self.render.font);
let style = MenuStyle {
theme,
@@ -402,7 +429,9 @@ impl FereseShell {
Menu::Bluetooth => bluetooth::view(self, rows, style, false),
Menu::Audio => audio::view(self, rows, style, false),
Menu::Battery => battery::view(self, rows, style),
Menu::Calendar => rows.push(calendar::view(self.render, self.calendar_offset, theme, p)),
Menu::Calendar => {
rows.push(calendar::view(self.render, self.calendar_offset, theme, p))
}
Menu::Notifications => notification_controls(self, rows, style, false),
Menu::Activities => rows.push(self.activity_list(menu.anchor.parent, p)),
Menu::Recording | Menu::Workspaces => rows,
@@ -423,25 +452,34 @@ impl FereseShell {
.as_ref()
.and_then(|error| menu.kind.action_error(error))
{
rows = rows.push(self.render.text(error).size(12).class(theme::Text::Color(Color {
a: p,
..Color::from_rgb8(230, 172, 90)
})));
rows = rows.push(
self.render
.text(error)
.size(12)
.class(theme::Text::Color(Color {
a: p,
..Color::from_rgb8(230, 172, 90)
})),
);
}
let compositor_material = menu.effects.is_some();
let body: Element<'_, cosmic::Action<Message>> = if matches!(kind, Menu::Overflow | Menu::Activities) {
cosmic::widget::scrollable(rows).height(Length::Shrink).into()
} else {
rows.into()
};
let body: Element<'_, cosmic::Action<Message>> =
if matches!(kind, Menu::Overflow | Menu::Activities) {
cosmic::widget::scrollable(rows)
.height(Length::Shrink)
.into()
} else {
rows.into()
};
let panel = container(body)
.id("ferese-blur-card")
.width(kind.width())
.padding(16)
.class(theme::Container::custom(move |_| container::Style {
background: (!compositor_material)
.then_some(Background::Color(color_with_opacity(theme.surface_popover, p))),
background: (!compositor_material).then_some(Background::Color(
color_with_opacity(theme.surface_popover, p),
)),
text_color: Some(primary),
icon_color: Some(primary),
border: Border {
@@ -488,7 +526,14 @@ fn notification_controls<'a>(
opacity: p,
} = style;
if let Some(n) = &shell.status.notifications {
let toggle = toggle_row(shell.render, "Do Not Disturb", n.dnd, Action::Dnd(!n.dnd), theme, p);
let toggle = toggle_row(
shell.render,
"Do Not Disturb",
n.dnd,
Action::Dnd(!n.dnd),
theme,
p,
);
rows = rows.push(if combined {
control_card(shell.render, toggle, primary, p)
} else {
@@ -528,7 +573,9 @@ mod tests {
#[test]
fn activity_popups_scroll_to_the_last_row_in_regular_panels() {
use cosmic::iced::advanced::{Layout, Shell, clipboard, layout, renderer::Headless, widget};
use cosmic::iced::advanced::{
Layout, Shell, clipboard, layout, renderer::Headless, widget,
};
use cosmic::iced::{Event, Rectangle, Size, Vector, mouse};
use ferese_activities::{Data, Owner, Source, Store, Template, Value};
#[derive(Default)]
@@ -575,7 +622,10 @@ mod tests {
for n in 0..24 {
store
.start(
Owner { pid: n + 1, birth: 1 },
Owner {
pid: n + 1,
birth: 1,
},
n.to_string(),
Data {
source: Source::Publisher,
+16 -4
View File
@@ -7,9 +7,15 @@ use super::{Menu, MenuRows, MenuStyle};
use crate::FereseShell;
use crate::status::Action;
pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn view<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
let MenuStyle {
primary, opacity: p, ..
primary,
opacity: p,
..
} = style;
if let Some(n) = &shell.status.network {
let connected = n.enabled && n.connection.is_some();
@@ -42,7 +48,11 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
rows = rows.push(
column![
row![
shell.render.text("Signal strength").size(12).width(Length::Fill),
shell
.render
.text("Signal strength")
.size(12)
.width(Length::Fill),
shell.render.text(strength).size(12)
],
level_meter(shell.render, n.signal, primary, p),
@@ -59,7 +69,9 @@ pub(super) fn heading<'a>(
mut heading: super::MenuHeading<'a>,
style: MenuStyle,
) -> super::MenuHeading<'a> {
let MenuStyle { theme, opacity: p, .. } = style;
let MenuStyle {
theme, opacity: p, ..
} = style;
if let Some(network) = &shell.status.network {
heading = heading.push(shell_switch(
shell.render,
@@ -2,7 +2,11 @@ use cosmic::widget::{column, slider};
use super::*;
pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: MenuStyle) -> MenuRows<'a> {
pub(super) fn view<'a>(
shell: &'a FereseShell,
mut rows: MenuRows<'a>,
style: MenuStyle,
) -> MenuRows<'a> {
let Some(status) = &shell.status.night_light else {
return rows.push(shell.render.text("Night light is unavailable").size(13));
};
@@ -28,9 +32,19 @@ pub(super) fn view<'a>(shell: &'a FereseShell, mut rows: MenuRows<'a>, style: Me
shell.render.text("Lower temperatures are warmer").size(12),
]
.spacing(8);
rows = rows.push(control_card(shell.render, warmth.into(), style.primary, style.opacity));
rows = rows.push(control_card(
shell.render,
warmth.into(),
style.primary,
style.opacity,
));
if status.supported_outputs == 0 {
rows = rows.push(shell.render.text("No display supports night light").size(13));
rows = rows.push(
shell
.render
.text("No display supports night light")
.size(13),
);
} else if status.enabled && status.failed_outputs > 0 {
rows = rows.push(
shell
+35 -8
View File
@@ -22,7 +22,11 @@ pub(super) fn view<'a>(
} = style;
use ferese_config::theme::{Appearance, Mode};
let mut modes = row([]).spacing(6);
for (mode, label) in [(Mode::Light, "☀ Light"), (Mode::Dark, "☾ Dark"), (Mode::Auto, "◐ Auto")] {
for (mode, label) in [
(Mode::Light, "☀ Light"),
(Mode::Dark, "☾ Dark"),
(Mode::Auto, "◐ Auto"),
] {
modes = modes.push(motion::button(
shell.render,
button::custom(shell.render.text(label).font(shell.render.font).size(13))
@@ -45,7 +49,12 @@ pub(super) fn view<'a>(
let settings = motion::button(
shell.render,
button::custom(
ferese_theme::icons::tinted(ferese_theme::icons::SETTINGS, 18, color(theme.text_primary)).opacity(p),
ferese_theme::icons::tinted(
ferese_theme::icons::SETTINGS,
18,
color(theme.text_primary),
)
.opacity(p),
)
.name("Open Ferese Settings")
.width(30)
@@ -135,7 +144,10 @@ pub(super) fn view<'a>(
);
}
if shell.status.network.is_some() || shell.status.bluetooth.is_some() || shell.status.battery.is_some() {
if shell.status.network.is_some()
|| shell.status.bluetooth.is_some()
|| shell.status.battery.is_some()
{
rows = rows.push(control_card(shell.render, connections.into(), primary, p));
}
rows = audio::view(shell, rows, style, true);
@@ -195,7 +207,10 @@ fn connection_control<'a>(
.height(28)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center);
let content = column![icon].spacing(2).width(Length::Fill).align_x(Alignment::Center);
let content = column![icon]
.spacing(2)
.width(Length::Fill)
.align_x(Alignment::Center);
let content = content.push(
render
.text(connection_caption(detail))
@@ -210,12 +225,18 @@ fn connection_control<'a>(
.class(theme::Text::Color(muted)),
);
let Some(action) = action else {
return container(content).width(Length::FillPortion(1)).padding(4).into();
return container(content)
.width(Length::FillPortion(1))
.padding(4)
.into();
};
let button = button::custom(content)
.width(Length::Fill)
.padding(4)
.name(format!("{label}: {}, {detail}", if enabled { "on" } else { "off" }))
.name(format!(
"{label}: {}, {detail}",
if enabled { "on" } else { "off" }
))
.on_press(cosmic::Action::App(Message::Control(action)));
let tile: Element<'_, cosmic::Action<Message>> = if selected {
button
@@ -277,8 +298,14 @@ mod tests {
assert_eq!(super::connection_caption("88%"), "88%");
assert_eq!(super::connection_caption("Flow84@Lofree"), "Flow84@Lofr…");
assert_eq!(super::connection_caption("123456789012"), "123456789012");
assert_eq!(super::connection_caption("My very long headphones"), "My very lon…");
assert_eq!(super::connection_caption(" Device\n name "), "Device name");
assert_eq!(
super::connection_caption("My very long headphones"),
"My very lon…"
);
assert_eq!(
super::connection_caption(" Device\n name "),
"Device name"
);
let caption = super::connection_caption("耳機耳機耳機耳機耳機耳機耳機");
assert_eq!(caption.chars().count(), 12);
assert!(caption.ends_with('…'));