Skip to content

Commit a3b58f6

Browse files
committed
feat: applets shrinkable by padding and configurable
1 parent 6fe8e07 commit a3b58f6

File tree

4 files changed

+92
-44
lines changed

4 files changed

+92
-44
lines changed

cosmic-panel-bin/src/space/layout.rs

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ impl PanelSpace {
221221
mut center_overflow_button: Option<OverflowButtonElement>,
222222
) -> anyhow::Result<()> {
223223
self.space.refresh();
224-
let applet_padding = self.config.size.get_applet_shrinkable_padding(true) / 2;
224+
let padding_overlap = self.config.padding_overlap();
225+
let applet_padding =
226+
self.config.size.get_applet_shrinkable_padding(true) as f32 * padding_overlap;
225227

226228
let mut bg_color = self.bg_color();
227229
for c in 0..3 {
@@ -266,7 +268,7 @@ impl PanelSpace {
266268
(i, w, _, padding_shrinkable): &(usize, CosmicMappedInternal, Option<u32>, bool),
267269
anchor: PanelAnchor,
268270
alignment: Alignment,
269-
applet_padding: u16,
271+
applet_padding: f32,
270272
) -> (Alignment, usize, i32, i32, i32, i32) {
271273
let (mut size, mut suggested_bounds) = w
272274
.toplevel()
@@ -654,7 +656,7 @@ impl PanelSpace {
654656
);
655657
(x, y) = (cur.0 as i32, cur.1);
656658
prev += size.w + spacing_u32 as f64
657-
- if *padding_shrinkable { applet_padding } else { 0 } as f64;
659+
- if *padding_shrinkable { applet_padding } else { 0. } as f64;
658660
self.space.map_element(w.clone(), (x, y), false);
659661
} else {
660662
let cur = (
@@ -667,7 +669,7 @@ impl PanelSpace {
667669
);
668670
(x, y) = (cur.0, cur.1 as i32);
669671
prev += size.h + spacing_u32 as f64
670-
- if *padding_shrinkable { applet_padding } else { 0 } as f64;
672+
- if *padding_shrinkable { applet_padding } else { 0. } as f64;
671673
self.space.map_element(w.clone(), (x, y), false);
672674
}
673675
if minimize_priority.is_some() {
@@ -1004,6 +1006,16 @@ impl PanelSpace {
10041006
pos_a.cmp(&pos_b)
10051007
});
10061008

1009+
let (major_padding, cross_padding) = (
1010+
self.config.size.get_applet_shrinkable_padding(true),
1011+
self.config.size.get_applet_padding(true),
1012+
);
1013+
1014+
let (applet_size_unit_major, applet_size_unit_cross) = (
1015+
self.config.size.get_applet_icon_size(true) + 2 * major_padding as u32,
1016+
self.config.size.get_applet_icon_size(true) + 2 * cross_padding as u32,
1017+
);
1018+
10071019
for e in elements {
10081020
match &e {
10091021
PopupMappedInternal::Window(w) => {
@@ -1012,11 +1024,9 @@ impl PanelSpace {
10121024
} else {
10131025
let x_i = overflow_cnt % 8;
10141026
let mut x = BORDER_WIDTH as i32
1015-
+ padding
1016-
+ x_i as i32 * (applet_size_unit as i32 + spacing);
1027+
+ x_i as i32 * (applet_size_unit_major as i32 + spacing);
10171028
let mut y = BORDER_WIDTH as i32
1018-
+ padding
1019-
+ (overflow_cnt / 8) as i32 * (applet_size_unit as i32 + spacing);
1029+
+ (overflow_cnt / 8) as i32 * (applet_size_unit_cross as i32 + spacing);
10201030
if !self.config.is_horizontal() {
10211031
std::mem::swap(&mut x, &mut y);
10221032
}
@@ -1029,12 +1039,10 @@ impl PanelSpace {
10291039
if prev_cnt != cur_cnt {
10301040
let actual = cur_cnt.saturating_sub(1);
10311041
let mut popup_major = 2. * BORDER_WIDTH as f32
1032-
+ actual.min(8) as f32 * applet_size_unit as f32
1033-
+ 2. * padding as f32
1042+
+ actual.min(8) as f32 * applet_size_unit_major as f32
10341043
+ (actual.min(8).saturating_sub(1) as f32) * spacing as f32;
10351044
let mut popup_cross = 2. * BORDER_WIDTH as f32
1036-
+ (actual as f32 / 8.).ceil().min(1.0) * applet_size_unit as f32
1037-
+ 2. * padding as f32;
1045+
+ (actual as f32 / 8.).ceil().min(1.0) * applet_size_unit_cross as f32;
10381046
if !self.config.is_horizontal() {
10391047
std::mem::swap(&mut popup_major, &mut popup_cross);
10401048
}
@@ -1093,17 +1101,27 @@ impl PanelSpace {
10931101
continue;
10941102
};
10951103
if let Some(shrink_min_size) = c.shrink_min_size {
1096-
overflow_partition.shrinkable.push((w.0, w.1 as i32, shrink_min_size));
1104+
overflow_partition.shrinkable.push(ShrinkableClient {
1105+
window: w.0,
1106+
priority: w.1 as i32,
1107+
shrink_size: shrink_min_size,
1108+
padding_overlap: 0.0,
1109+
});
10971110
} else if c.shrink_priority.is_some() {
10981111
overflow_partition.movable.push(w);
10991112
} else {
11001113
// make shrinkable if no shrink priority with lowest priority so it is moved
11011114
// last
1102-
overflow_partition.shrinkable.push((w.0, -1, ClientShrinkSize::AppletUnit(1)));
1115+
overflow_partition.shrinkable.push(ShrinkableClient {
1116+
window: w.0,
1117+
priority: -1,
1118+
shrink_size: ClientShrinkSize::AppletUnit(1),
1119+
padding_overlap: 0.0,
1120+
});
11031121
}
11041122
}
11051123
// sort by priority
1106-
overflow_partition.shrinkable.sort_by(|(_, a, _), (_, b, _)| b.cmp(a));
1124+
overflow_partition.shrinkable.sort_by(|a, b| b.priority.cmp(&a.priority));
11071125
overflow_partition.movable.sort_by(|(_, a), (_, b)| b.cmp(a));
11081126
overflow_partition
11091127
}
@@ -1149,7 +1167,9 @@ impl PanelSpace {
11491167
let unit_size = self.config.size.get_applet_icon_size_with_padding(true);
11501168

11511169
let mut sum = 0.;
1152-
for (w, priority, min_units) in clients.shrinkable.iter_mut() {
1170+
for ShrinkableClient { window: w, priority, shrink_size: min_units, .. } in
1171+
clients.shrinkable.iter_mut()
1172+
{
11531173
if overflow == 0 {
11541174
break;
11551175
}
@@ -1248,19 +1268,35 @@ impl PanelSpace {
12481268
tracing::info!("Needs at least 2 movable clients to move to overflow space.");
12491269
return overflow;
12501270
}
1271+
let (major_padding, cross_padding) = (
1272+
self.config.size.get_applet_shrinkable_padding(true),
1273+
self.config.size.get_applet_padding(true),
1274+
);
1275+
let (h_padding, v_padding) = if self.config.is_horizontal() {
1276+
(major_padding as f32, cross_padding as f32)
1277+
} else {
1278+
(cross_padding as f32, major_padding as f32)
1279+
};
12511280
info!("Moving clients to overflow space {section:?} {overflow}");
12521281
let overflow_space = match section {
12531282
OverflowSection::Left => &mut self.overflow_left,
12541283
OverflowSection::Center => &mut self.overflow_center,
12551284
OverflowSection::Right => &mut self.overflow_right,
12561285
};
12571286
let mut overflow_cnt = overflow_space.elements().count();
1258-
let applet_size_unit = self.config.size.get_applet_icon_size(true)
1259-
+ 2 * self.config.size.get_applet_padding(true) as u32;
1287+
let (applet_size_unit_major, applet_size_unit_cross) = (
1288+
self.config.size.get_applet_icon_size(true) + 2 * major_padding as u32,
1289+
self.config.size.get_applet_icon_size(true) + 2 * cross_padding as u32,
1290+
);
1291+
let (applet_size_unit_h, applet_size_unit_v) = if is_horizontal {
1292+
(applet_size_unit_major, applet_size_unit_cross)
1293+
} else {
1294+
(applet_size_unit_cross, applet_size_unit_major)
1295+
};
12601296
let spacing = self.config.spacing;
12611297

12621298
if overflow_cnt == 0 {
1263-
overflow += applet_size_unit + spacing;
1299+
overflow += applet_size_unit_major + spacing;
12641300
}
12651301
let space = &mut self.space;
12661302

@@ -1280,20 +1316,18 @@ impl PanelSpace {
12801316
}
12811317
let diff = if is_horizontal { bbox.size.w as u32 } else { bbox.size.h as u32 };
12821318
overflow = overflow.saturating_sub(diff);
1283-
let padding = self.config.padding as i32;
1284-
// TODO spacing & padding
1319+
12851320
let x_i = overflow_cnt % 8;
1286-
let mut x = padding
1287-
+ x_i as i32 * (applet_size_unit as i32 + spacing as i32)
1288-
+ BORDER_WIDTH as i32;
1321+
let mut x =
1322+
x_i as i32 * (applet_size_unit_major as i32 + spacing as i32) + BORDER_WIDTH as i32;
12891323
let mut y = BORDER_WIDTH as i32
1290-
+ (overflow_cnt / 8) as i32 * (applet_size_unit + spacing) as i32;
1324+
+ (overflow_cnt / 8) as i32 * (applet_size_unit_cross + spacing) as i32;
12911325
if !self.config.is_horizontal() {
12921326
std::mem::swap(&mut x, &mut y);
12931327
}
12941328

12951329
space.unmap_elem(&CosmicMappedInternal::Window(w.0.clone()));
1296-
overflow_space.map_element(PopupMappedInternal::Window(w.0.clone()), (x, y), true);
1330+
overflow_space.map_element(PopupMappedInternal::Window(w.0.clone()), (x, y / 2), true);
12971331
// Rows of 8 with configured applet size
12981332
if let Some(t) = w.0.toplevel() {
12991333
with_states(t.wl_surface(), |states| {
@@ -1302,8 +1336,8 @@ impl PanelSpace {
13021336
});
13031337
});
13041338
t.with_pending_state(|s| {
1305-
s.size = Some((applet_size_unit as i32, applet_size_unit as i32).into());
1306-
s.bounds = Some((applet_size_unit as i32, applet_size_unit as i32).into());
1339+
s.size = Some((applet_size_unit_h as i32, applet_size_unit_v as i32).into());
1340+
s.bounds = Some((applet_size_unit_h as i32, applet_size_unit_v as i32).into());
13071341
});
13081342

13091343
t.send_pending_configure();
@@ -1317,14 +1351,12 @@ impl PanelSpace {
13171351
.count();
13181352

13191353
let space = self.config.spacing as f32;
1320-
let padding = self.config.padding as f32;
1354+
13211355
let mut popup_major = 2. * BORDER_WIDTH as f32
1322-
+ overflow_cnt.min(8) as f32 * applet_size_unit as f32
1323-
+ 2. * padding
1356+
+ overflow_cnt.min(8) as f32 * applet_size_unit_major as f32
13241357
+ (overflow_cnt.min(8).saturating_sub(1) as f32) * space;
13251358
let mut popup_cross = 2. * BORDER_WIDTH as f32
1326-
+ (overflow_cnt as f32 / 8.).ceil().min(1.0) * applet_size_unit as f32
1327-
+ 2. * padding;
1359+
+ (overflow_cnt as f32 / 8.).ceil().min(1.0) * applet_size_unit_cross as f32;
13281360
if !self.config.is_horizontal() {
13291361
std::mem::swap(&mut popup_major, &mut popup_cross);
13301362
}
@@ -1345,6 +1377,7 @@ impl PanelSpace {
13451377
}
13461378
})
13471379
.cloned();
1380+
13481381
let new_popup = |count| {
13491382
PopupMappedInternal::Popup(overflow_popup_element(
13501383
match section {
@@ -1388,7 +1421,6 @@ impl PanelSpace {
13881421
};
13891422

13901423
let icon_size = self.config.size.get_applet_icon_size(true);
1391-
let padding = self.config.size.get_applet_padding(true);
13921424
let icon = if self.config.is_horizontal() {
13931425
"view-more-horizontal-symbolic"
13941426
} else {
@@ -1398,7 +1430,7 @@ impl PanelSpace {
13981430
id,
13991431
(0, 0).into(),
14001432
u16::try_from(icon_size).unwrap_or(32),
1401-
(padding as f32).into(),
1433+
[v_padding, h_padding].into(),
14021434
Arc::new(AtomicBool::new(false)),
14031435
icon.into(),
14041436
self.shared.loop_handle.clone(),
@@ -1601,7 +1633,7 @@ impl PanelSpace {
16011633
if self.remap_attempts > 0 {
16021634
return;
16031635
}
1604-
for (w, ..) in
1636+
for ShrinkableClient { window: w, .. } in
16051637
clients.constrained_shrinkables(self.config.is_horizontal(), self.scale).drain(..).rev()
16061638
{
16071639
let expand = extra_space as i32;
@@ -1707,26 +1739,30 @@ pub enum OverflowSection {
17071739
Right,
17081740
}
17091741

1742+
#[derive(Debug, Clone)]
1743+
pub struct ShrinkableClient {
1744+
window: Window,
1745+
priority: i32,
1746+
shrink_size: ClientShrinkSize,
1747+
padding_overlap: f32,
1748+
}
1749+
17101750
#[derive(Debug, Default, Clone)]
17111751
pub struct OverflowClientPartition {
17121752
/// windows for clients that can be shrunk, but not moved to the overflow
17131753
/// popup
1714-
pub(crate) shrinkable: Vec<(Window, i32, ClientShrinkSize)>,
1754+
pub(crate) shrinkable: Vec<ShrinkableClient>,
17151755
/// windows for clients that can be moved to the overflow popup, but not
17161756
/// shrunk
17171757
pub(crate) movable: Vec<(Window, u32)>,
17181758
pub suggested_size: u32,
17191759
}
17201760

17211761
impl OverflowClientPartition {
1722-
fn constrained_shrinkables(
1723-
&self,
1724-
is_horizontal: bool,
1725-
scale: f64,
1726-
) -> Vec<(Window, i32, ClientShrinkSize)> {
1762+
fn constrained_shrinkables(&self, is_horizontal: bool, scale: f64) -> Vec<ShrinkableClient> {
17271763
self.shrinkable
17281764
.iter()
1729-
.filter(|(w, ..)| {
1765+
.filter(|ShrinkableClient { window: w, .. }| {
17301766
w.toplevel().is_some_and(|t| {
17311767
let state = t.current_state();
17321768
let cur_size = w.geometry().size;
@@ -1753,7 +1789,7 @@ impl OverflowClientPartition {
17531789

17541790
fn shrinkable_is_relaxed(&self, is_horizontal: bool, scale: f64) -> bool {
17551791
self.shrinkable.is_empty() || {
1756-
self.shrinkable.iter().all(|(w, ..)| {
1792+
self.shrinkable.iter().all(|ShrinkableClient { window: w, .. }| {
17571793
w.toplevel().is_some_and(|t| {
17581794
let state = t.current_state();
17591795
let cur_size = w.geometry().size;

cosmic-panel-bin/src/space/wrapper_space.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,16 @@ impl WrapperSpace for PanelSpace {
451451
let config_anchor = ron::ser::to_string(&self.config.anchor).unwrap_or_default();
452452
let config_bg = ron::ser::to_string(&self.config.background).unwrap_or_default();
453453
let config_spacing = ron::ser::to_string(&self.config.spacing).unwrap_or_default();
454+
let config_padding_overlap =
455+
ron::ser::to_string(&self.config.padding_overlap()).unwrap_or_default();
454456
let config_name = self.config.name.clone();
455457
let env_vars = vec![
456458
("COSMIC_PANEL_NAME".to_string(), config_name),
457459
("COSMIC_PANEL_OUTPUT".to_string(), active_output),
458460
("COSMIC_PANEL_SPACING".to_string(), config_spacing),
459461
("COSMIC_PANEL_ANCHOR".to_string(), config_anchor),
460462
("COSMIC_PANEL_BACKGROUND".to_string(), config_bg),
463+
("COSMIC_PANEL_PADDING_OVERLAP".to_string(), config_padding_overlap),
461464
("RUST_BACKTRACE".to_string(), "1".to_string()),
462465
];
463466
info!("{:?}", &desktop_ids);

cosmic-panel-config/src/container_config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ impl Default for CosmicPanelContainerConfig {
164164
margin: 0,
165165
opacity: 1.0,
166166
autohover_delay_ms: Some(500),
167+
padding_overlap: 0.5,
167168
},
168169
CosmicPanelConfig {
169170
name: "Dock".to_string(),
@@ -199,6 +200,7 @@ impl Default for CosmicPanelContainerConfig {
199200
margin: 0,
200201
opacity: 1.0,
201202
autohover_delay_ms: Some(500),
203+
padding_overlap: 0.5,
202204
},
203205
],
204206
}

cosmic-panel-config/src/panel_config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ pub struct CosmicPanelConfig {
410410
/// autohover popup delay duration in milliseconds
411411
/// If None, then it is disabled
412412
pub autohover_delay_ms: Option<u32>,
413+
/// padding overlap ratio
414+
pub padding_overlap: f32,
413415
}
414416

415417
impl PartialEq for CosmicPanelConfig {
@@ -464,6 +466,7 @@ impl Default for CosmicPanelConfig {
464466
margin: 4,
465467
opacity: 0.8,
466468
autohover_delay_ms: Some(500),
469+
padding_overlap: 0.5,
467470
}
468471
}
469472
}
@@ -477,6 +480,10 @@ pub enum Side {
477480

478481
#[cfg(feature = "wayland-rs")]
479482
impl CosmicPanelConfig {
483+
pub fn padding_overlap(&self) -> f32 {
484+
self.padding_overlap.clamp(0., 1.)
485+
}
486+
480487
/// get the applet size given its side
481488
pub fn get_effective_applet_size(&self, side: Side) -> PanelSize {
482489
match side {

0 commit comments

Comments
 (0)