Consolidate desktop controls with editable holidays and a running-window tray
This commit is contained in:
1 parent
9908dde9f8
commit
326465952a
55 files changed
+4207
-450
No files matched your search
@@ -40,7 +40,7 @@ impl Default for Profile {
|
||||
expanded_width: 340.,
|
||||
workspaces_height: 176.,
|
||||
media_height: 170.,
|
||||
calendar_height: 230.,
|
||||
calendar_height: 650.,
|
||||
notifications_height: 176.,
|
||||
controls_height: 248.,
|
||||
expanded_radius: 26.,
|
||||
@@ -415,7 +415,7 @@ mod tests {
|
||||
assert!(!hub.active());
|
||||
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
|
||||
.unwrap();
|
||||
assert_eq!(hub.target_size(), [340., 230.]);
|
||||
assert_eq!(hub.target_size(), [340., 600.]); // clipped to the output
|
||||
hub.advance(Duration::from_millis(120), false);
|
||||
assert_eq!(hub.sample().opacity, 0.);
|
||||
assert_eq!(hub.sample().previous_opacity, 0.);
|
||||
@@ -469,7 +469,7 @@ mod tests {
|
||||
}
|
||||
let overshoot = (peak - 340.) / (340. - 150.);
|
||||
assert!((0.04..0.05).contains(&overshoot));
|
||||
assert_eq!(hub.sample().geometry.bounds, Rect::new(0., 0., 340., 230.));
|
||||
assert_eq!(hub.sample().geometry.bounds, Rect::new(0., 0., 340., 600.));
|
||||
assert!(!hub.active());
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ pub enum ItemKind {
|
||||
style: WorkspaceStyle,
|
||||
},
|
||||
FocusedWindow,
|
||||
WindowTray,
|
||||
Media,
|
||||
Activities,
|
||||
QuickSettings,
|
||||
@@ -152,6 +153,7 @@ impl ItemKind {
|
||||
pub fn minimum_width(self) -> Option<f32> {
|
||||
match self {
|
||||
Self::FocusedWindow => Some(48.),
|
||||
Self::WindowTray => Some(30.),
|
||||
Self::Workspaces { .. } => Some(24.),
|
||||
Self::Media => Some(112.),
|
||||
_ => None,
|
||||
@@ -164,6 +166,7 @@ impl ItemKind {
|
||||
Self::Overview => "Overview",
|
||||
Self::Workspaces { .. } => "Workspaces",
|
||||
Self::FocusedWindow => "Focused window",
|
||||
Self::WindowTray => "Window tray",
|
||||
Self::Media => "Media",
|
||||
Self::Activities => "Activities",
|
||||
Self::QuickSettings => "Quick Settings",
|
||||
@@ -188,7 +191,7 @@ impl ItemKind {
|
||||
&[Wide, Compact, Icon]
|
||||
}
|
||||
Self::Battery { percentage: true } => &[Wide, Icon],
|
||||
Self::Workspaces { .. } | Self::FocusedWindow => &[Wide],
|
||||
Self::Workspaces { .. } | Self::FocusedWindow | Self::WindowTray => &[Wide],
|
||||
_ => &[Icon],
|
||||
}
|
||||
}
|
||||
@@ -311,7 +314,10 @@ impl Item {
|
||||
metadata: BTreeMap::new(),
|
||||
overflow: if matches!(
|
||||
kind,
|
||||
ItemKind::Overview | ItemKind::Workspaces { .. } | ItemKind::FocusedWindow
|
||||
ItemKind::Overview
|
||||
| ItemKind::Workspaces { .. }
|
||||
| ItemKind::FocusedWindow
|
||||
| ItemKind::WindowTray
|
||||
) {
|
||||
OverflowPolicy::Never
|
||||
} else {
|
||||
@@ -698,6 +704,84 @@ impl Panel {
|
||||
.find(|item| &item.id == id)
|
||||
}
|
||||
|
||||
/// Project older panel files onto the consolidated time/control entry without
|
||||
/// rewriting their source or changing surviving item identities.
|
||||
pub fn consolidate_clock_controls(&mut self) {
|
||||
let kinds: Vec<_> = [&self.start, &self.center, &self.end]
|
||||
.into_iter()
|
||||
.flat_map(|z| &z.groups)
|
||||
.flat_map(|g| &g.items)
|
||||
.map(|i| i.kind)
|
||||
.collect();
|
||||
// Only migrate legacy presets. A deliberately removed or hidden tray in
|
||||
// a current composition must stay removed or hidden.
|
||||
if kinds.iter().any(|k| {
|
||||
matches!(
|
||||
k,
|
||||
ItemKind::QuickSettings | ItemKind::Network | ItemKind::NightLight
|
||||
)
|
||||
}) && !kinds.contains(&ItemKind::WindowTray)
|
||||
{
|
||||
let mut suffix = 0;
|
||||
let mut id = ItemId("window-tray".into());
|
||||
while self.item(&id).is_some() {
|
||||
suffix += 1;
|
||||
id = ItemId(format!("window-tray-{suffix}"));
|
||||
}
|
||||
if let Some(group) = self.end.groups.first_mut() {
|
||||
group
|
||||
.items
|
||||
.insert(0, Item::new(&id.0, ItemKind::WindowTray));
|
||||
}
|
||||
}
|
||||
let has_clock = [&self.start, &self.center, &self.end]
|
||||
.into_iter()
|
||||
.flat_map(|z| &z.groups)
|
||||
.flat_map(|g| &g.items)
|
||||
.any(|i| i.kind == ItemKind::Clock);
|
||||
let mut clock_exists = has_clock;
|
||||
for zone in [&mut self.start, &mut self.center, &mut self.end] {
|
||||
for group in &mut zone.groups {
|
||||
group.items.retain_mut(|item| match item.kind {
|
||||
ItemKind::QuickSettings if !clock_exists => {
|
||||
item.kind = ItemKind::Clock;
|
||||
clock_exists = true;
|
||||
true
|
||||
}
|
||||
ItemKind::QuickSettings | ItemKind::Network | ItemKind::NightLight => false,
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if !clock_exists {
|
||||
return;
|
||||
}
|
||||
let mut notices = Vec::new();
|
||||
for zone in [&mut self.start, &mut self.center, &mut self.end] {
|
||||
for group in &mut zone.groups {
|
||||
group.items.retain(|item| {
|
||||
if item.kind == ItemKind::Notifications {
|
||||
notices.push(item.clone());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
for zone in [&mut self.start, &mut self.center, &mut self.end] {
|
||||
for group in &mut zone.groups {
|
||||
if let Some(index) = group.items.iter().position(|i| i.kind == ItemKind::Clock) {
|
||||
for item in &mut notices {
|
||||
item.gap_before = Some(0.);
|
||||
}
|
||||
group.items.splice(index + 1..index + 1, notices);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a stable initial composition; presets are an editor convenience.
|
||||
#[must_use]
|
||||
pub fn from_preset(preset: PanelStyle) -> Self {
|
||||
@@ -714,18 +798,17 @@ impl Panel {
|
||||
);
|
||||
let title = item("focused-window", FocusedWindow);
|
||||
let controls = vec![
|
||||
item("window-tray", WindowTray),
|
||||
item("media", Media),
|
||||
item("quick-settings", QuickSettings),
|
||||
item("tray", Tray),
|
||||
item("night-light", NightLight),
|
||||
item("network", Network),
|
||||
item("audio", Audio),
|
||||
item("recording", Recording),
|
||||
item("capture-status", CaptureStatus),
|
||||
item("notifications", Notifications),
|
||||
item("battery", Battery { percentage: true }),
|
||||
];
|
||||
let clock = item("clock", Clock);
|
||||
let mut notifications = item("notifications", Notifications);
|
||||
notifications.gap_before = Some(0.);
|
||||
let display = item("display-mode", DisplayMode);
|
||||
|
||||
let (start, center, end) = match preset {
|
||||
@@ -737,13 +820,13 @@ impl Panel {
|
||||
Group::new("title", vec![title], [0, 0], padding),
|
||||
vec![
|
||||
Group::new("status", controls, [2, 3], padding),
|
||||
Group::new("time", vec![clock], [2, 3], padding),
|
||||
Group::new("time", vec![clock, notifications], [2, 3], padding),
|
||||
Group::new("display", vec![display], [0, 0], padding),
|
||||
],
|
||||
),
|
||||
_ => {
|
||||
let mut controls = controls;
|
||||
controls.extend([clock, display]);
|
||||
controls.extend([clock, notifications, display]);
|
||||
(
|
||||
vec![Group::new(
|
||||
"navigation",
|
||||
@@ -835,17 +918,15 @@ mod tests {
|
||||
"overview",
|
||||
"workspaces",
|
||||
"focused-window",
|
||||
"window-tray",
|
||||
"media",
|
||||
"quick-settings",
|
||||
"tray",
|
||||
"night-light",
|
||||
"network",
|
||||
"audio",
|
||||
"recording",
|
||||
"capture-status",
|
||||
"notifications",
|
||||
"battery",
|
||||
"clock",
|
||||
"notifications",
|
||||
"display-mode",
|
||||
];
|
||||
for panel in [&continuous, &islands] {
|
||||
@@ -876,8 +957,7 @@ mod tests {
|
||||
"overview",
|
||||
"workspaces",
|
||||
"focused-window",
|
||||
"quick-settings",
|
||||
"night-light",
|
||||
"window-tray",
|
||||
"recording",
|
||||
"clock"
|
||||
]
|
||||
@@ -898,7 +978,72 @@ mod tests {
|
||||
.map(|item| item.id.0.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(items(&panel).len(), 15);
|
||||
assert_eq!(items(&panel).len(), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_window_tray_is_seeded_once_without_resurrecting_current_removed_items() {
|
||||
let mut panel = Panel::default();
|
||||
panel.end.groups[0]
|
||||
.items
|
||||
.retain(|i| i.kind != ItemKind::WindowTray);
|
||||
panel.consolidate_clock_controls();
|
||||
assert!(!items(&panel).iter().any(|i| i.kind == ItemKind::WindowTray));
|
||||
panel.end.groups[0]
|
||||
.items
|
||||
.push(Item::new("old-controls", ItemKind::QuickSettings));
|
||||
// The instance name can already belong to a different authored item.
|
||||
panel.start.groups[0].items[0].id = ItemId("window-tray".into());
|
||||
panel.consolidate_clock_controls();
|
||||
let tray = items(&panel)
|
||||
.into_iter()
|
||||
.find(|i| i.kind == ItemKind::WindowTray)
|
||||
.unwrap();
|
||||
assert_eq!(tray.id.0, "window-tray-1");
|
||||
validate(std::slice::from_ref(&panel)).unwrap();
|
||||
let once = panel.clone();
|
||||
panel.consolidate_clock_controls();
|
||||
assert_eq!(panel, once);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_controls_consolidate_beside_the_clock_and_preserve_notification_state() {
|
||||
let mut panel = Panel::default();
|
||||
let mut notice = panel.end.groups[1].items.pop().unwrap();
|
||||
notice.visible = false;
|
||||
notice.metadata.insert("custom".into(), "retained".into());
|
||||
panel.start.groups[0].items.extend([
|
||||
Item::new("old-network", ItemKind::Network),
|
||||
Item::new("old-controls", ItemKind::QuickSettings),
|
||||
notice.clone(),
|
||||
]);
|
||||
panel.consolidate_clock_controls();
|
||||
let all = items(&panel);
|
||||
assert!(!all.iter().any(|i| matches!(
|
||||
i.kind,
|
||||
ItemKind::Network | ItemKind::QuickSettings | ItemKind::NightLight
|
||||
)));
|
||||
let clock = all.iter().position(|i| i.kind == ItemKind::Clock).unwrap();
|
||||
let moved = all[clock + 1];
|
||||
assert_eq!(moved.id, notice.id);
|
||||
assert!(!moved.visible);
|
||||
assert_eq!(moved.metadata, notice.metadata);
|
||||
let once = panel.clone();
|
||||
panel.consolidate_clock_controls();
|
||||
assert_eq!(panel, once);
|
||||
for zone in [&mut panel.start, &mut panel.center, &mut panel.end] {
|
||||
for group in &mut zone.groups {
|
||||
group.items.retain(|i| i.kind != ItemKind::Clock);
|
||||
}
|
||||
}
|
||||
panel.end.groups[0]
|
||||
.items
|
||||
.push(Item::new("controls-only", ItemKind::QuickSettings));
|
||||
panel.consolidate_clock_controls();
|
||||
assert_eq!(
|
||||
panel.item(&ItemId("controls-only".into())).unwrap().kind,
|
||||
ItemKind::Clock
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -840,14 +840,14 @@ mod tests {
|
||||
.groups
|
||||
.iter_mut()
|
||||
.flat_map(|group| &mut group.items)
|
||||
.find(|item| item.id.0 == "network")
|
||||
.find(|item| item.id.0 == "audio")
|
||||
.unwrap()
|
||||
.overflow = OverflowPolicy::Always;
|
||||
let mut measured = measurements(&panel);
|
||||
measured.retain(|item| item.id.0 != "media");
|
||||
let resolution = resolve(&panel, &measured, 2000., 32.);
|
||||
assert!(!resolution.items.contains_key(&ItemId("media".into())));
|
||||
assert_eq!(resolution.overflow, [ItemId("network".into())]);
|
||||
assert_eq!(resolution.overflow, [ItemId("audio".into())]);
|
||||
}
|
||||
#[test]
|
||||
fn priority_controls_displacement_and_expansion_restores_requested_sizes() {
|
||||
|
||||
Reference in new issue
Block a user