Fix panel review findings and polkit library lifetime
This commit is contained in:
1 parent
f7e5efb245
commit
305f49a62f
13 files changed
+436
-676
No files matched your search
@@ -178,9 +178,9 @@ impl Hub {
|
||||
pub fn pin_radii(&mut self, pins: Option<[f64; 4]>) -> Result<(), &'static str> {
|
||||
if pins.is_some_and(|pins| {
|
||||
pins.iter()
|
||||
.any(|value| !value.is_finite() || !(0. ..=128.).contains(value))
|
||||
.any(|value| !value.is_finite() || !(0. ..=f64::from(f32::MAX)).contains(value))
|
||||
}) {
|
||||
return Err("hub radius pins must be finite values between 0 and 128");
|
||||
return Err("hub radius pins must be finite, nonnegative pixel values representable as f32");
|
||||
}
|
||||
self.pins = pins;
|
||||
let mut target = self.motion.target();
|
||||
@@ -307,7 +307,7 @@ impl Hub {
|
||||
let mut geometry = self.motion.current();
|
||||
geometry.bounds.width = geometry.bounds.width.clamp(0., self.available[0]);
|
||||
geometry.bounds.height = geometry.bounds.height.clamp(0., self.available[1]);
|
||||
geometry.radii = geometry.radii.map(|radius| radius.max(0.));
|
||||
geometry.radii = geometry.radii.map(|radius| radius.clamp(0., f64::from(f32::MAX)));
|
||||
Sample {
|
||||
geometry,
|
||||
previous: self.previous,
|
||||
|
||||
@@ -136,6 +136,16 @@ pub enum ItemKind {
|
||||
}
|
||||
|
||||
impl ItemKind {
|
||||
/// Smallest useful allocation before a flexible control moves into overflow.
|
||||
pub fn minimum_width(self) -> Option<f32> {
|
||||
match self {
|
||||
Self::FocusedWindow => Some(48.),
|
||||
Self::Workspaces { .. } => Some(24.),
|
||||
Self::Media => Some(112.),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Overview => "Overview",
|
||||
|
||||
@@ -222,6 +222,41 @@ pub fn resolve_with_gap(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Removing the larger side may make an earlier eviction unnecessary. Give
|
||||
// mandatory items first refusal, then restore by descending priority.
|
||||
for mandatory in [true, false] {
|
||||
for (_, item) in candidates.iter().rev() {
|
||||
if item.overflow == OverflowPolicy::Always
|
||||
|| (item.overflow == OverflowPolicy::Never) != mandatory
|
||||
|| !matches!(result.items.get(&item.id), Some(Placement::Overflow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(measured) = measurements.get(&item.id) else {
|
||||
continue;
|
||||
};
|
||||
let Some(&(representation, width)) = measured.alternatives.iter().min_by(|a, b| a.1.total_cmp(&b.1)) else {
|
||||
continue;
|
||||
};
|
||||
result.items.insert(
|
||||
item.id.clone(),
|
||||
Placement::Visible {
|
||||
representation,
|
||||
width: measured.minimum.map_or(width, |minimum| minimum.min(width)),
|
||||
},
|
||||
);
|
||||
result.overflow.retain(|id| id != &item.id);
|
||||
result.update_widths(panel, overflow_width);
|
||||
if !fits(&result) {
|
||||
result.items.insert(item.id.clone(), Placement::Overflow);
|
||||
result.overflow.push(item.id.clone());
|
||||
result.update_widths(panel, overflow_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.forced_overflow = ordered.iter().any(|item| {
|
||||
item.overflow == OverflowPolicy::Never && matches!(result.items.get(&item.id), Some(Placement::Overflow))
|
||||
});
|
||||
// A reduction on the shorter side can leave unused room after the other
|
||||
// side shrinks. Restore preferred representations before expanding titles.
|
||||
for (_, item) in candidates.iter().rev() {
|
||||
@@ -294,6 +329,46 @@ pub fn resolve_with_gap(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn asymmetric_overflow_restores_items_that_fit_after_the_larger_side_yields() {
|
||||
use super::super::{Group, Item, ItemKind};
|
||||
for mirrored in [false, true] {
|
||||
for policy in [OverflowPolicy::Auto, OverflowPolicy::Never, OverflowPolicy::Always] {
|
||||
let group = |id: &str, priority, overflow| {
|
||||
let mut item = Item::new(id, ItemKind::Clock);
|
||||
item.priority = priority;
|
||||
item.overflow = overflow;
|
||||
let mut group = Group::new(id, vec![item], [0, 0], 0.);
|
||||
group.island_padding = 0.;
|
||||
group
|
||||
};
|
||||
let mut panel = Panel::default();
|
||||
panel.start.groups = vec![group("small", 0, policy)];
|
||||
panel.center.groups = vec![group("center", 100, OverflowPolicy::Never)];
|
||||
panel.end.groups = vec![group("large", 50, policy)];
|
||||
if mirrored {
|
||||
std::mem::swap(&mut panel.start, &mut panel.end);
|
||||
}
|
||||
let measurements: Vec<_> = [("small", 40.), ("center", 40.), ("large", 200.)]
|
||||
.into_iter()
|
||||
.map(|(id, width)| Measurement {
|
||||
id: ItemId(id.into()),
|
||||
alternatives: vec![(Representation::Wide, width)],
|
||||
minimum: None,
|
||||
})
|
||||
.collect();
|
||||
let resolution = resolve(&panel, &measurements, 300., 32.);
|
||||
assert!(resolution.fits(300.));
|
||||
assert_eq!(
|
||||
matches!(resolution.items[&ItemId("small".into())], Placement::Visible { .. }),
|
||||
policy != OverflowPolicy::Always
|
||||
);
|
||||
assert!(resolution.overflow.contains(&ItemId("large".into())));
|
||||
assert_eq!(resolution.forced_overflow, policy == OverflowPolicy::Never);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zone_width_ignores_empty_groups_and_gaps_before_hidden_items() {
|
||||
use super::super::{Group, Item, ItemKind};
|
||||
|
||||
@@ -349,6 +349,105 @@ mod tests {
|
||||
use cosmic::iced::{Font, Pixels};
|
||||
use ferese_panel::shape::Style;
|
||||
|
||||
#[test]
|
||||
fn shared_surface_publishes_layout_changes_and_leaves_native_fill_to_the_compositor() {
|
||||
use cosmic::iced::advanced::clipboard;
|
||||
use std::cell::RefCell;
|
||||
let mut renderer = cosmic::iced::futures::executor::block_on(<cosmic::Renderer as Headless>::new(
|
||||
Font::default(),
|
||||
Pixels(14.),
|
||||
Some("tiny-skia"),
|
||||
))
|
||||
.unwrap();
|
||||
let ids = [widget::Id::unique(), widget::Id::unique()];
|
||||
let mut tree = None;
|
||||
let viewport = Rectangle::with_size(Size::new(200., 32.));
|
||||
for (style, width, native) in [
|
||||
(Style::Floating, 40., false),
|
||||
(Style::Floating, 40., true),
|
||||
(Style::Floating, 60., true),
|
||||
(Style::Flat, 60., true),
|
||||
] {
|
||||
let content = cosmic::widget::row::with_children([
|
||||
cosmic::widget::container(cosmic::widget::Space::new().width(width).height(32))
|
||||
.id(ids[0].clone())
|
||||
.into(),
|
||||
cosmic::widget::container(cosmic::widget::Space::new().width(40).height(32))
|
||||
.id(ids[1].clone())
|
||||
.into(),
|
||||
])
|
||||
.spacing(20);
|
||||
let published = RefCell::new(Vec::new());
|
||||
let mut view = frame(
|
||||
cosmic::widget::container(content).width(200).height(32).into(),
|
||||
Options {
|
||||
parameters: Parameters {
|
||||
style,
|
||||
height: 32.,
|
||||
..Default::default()
|
||||
},
|
||||
bounds: None,
|
||||
groups: vec![
|
||||
(ids[0].clone(), ZonePosition::Start),
|
||||
(ids[1].clone(), ZonePosition::End),
|
||||
],
|
||||
scale: 1.25,
|
||||
high_contrast: false,
|
||||
background: Color::WHITE,
|
||||
border: Color::TRANSPARENT,
|
||||
divider: Color::TRANSPARENT,
|
||||
native,
|
||||
},
|
||||
|scene| published.borrow_mut().push(scene.clone()),
|
||||
Some(Box::new(Clone::clone)),
|
||||
);
|
||||
let tree = tree.get_or_insert_with(|| widget::Tree::new(&view));
|
||||
tree.diff(&mut view);
|
||||
let node = view
|
||||
.as_widget_mut()
|
||||
.layout(tree, &renderer, &layout::Limits::new(Size::ZERO, viewport.size()));
|
||||
let mut messages = Vec::new();
|
||||
for _ in 0..3 {
|
||||
view.as_widget_mut().update(
|
||||
tree,
|
||||
&Event::Window(cosmic::iced::window::Event::RedrawRequested(std::time::Instant::now())),
|
||||
Layout::new(&node),
|
||||
mouse::Cursor::Unavailable,
|
||||
&renderer,
|
||||
&mut clipboard::Null,
|
||||
&mut Shell::new(&mut messages),
|
||||
&viewport,
|
||||
);
|
||||
}
|
||||
assert_eq!(published.borrow().len(), 3);
|
||||
assert_eq!(
|
||||
messages.len(),
|
||||
usize::from(!(style == Style::Floating && width == 40. && native))
|
||||
);
|
||||
let scene = published.borrow()[0].clone();
|
||||
assert_eq!(scene.input.len(), if style == Style::Floating { 2 } else { 1 });
|
||||
if style == Style::Floating {
|
||||
assert!(scene.distance([width as f64 + 10., 16.]) > 0., "the gap stays empty");
|
||||
}
|
||||
renderer.reset(viewport);
|
||||
view.as_widget().draw(
|
||||
tree,
|
||||
&mut renderer,
|
||||
&Theme::default(),
|
||||
&renderer::Style {
|
||||
text_color: Color::WHITE,
|
||||
icon_color: Color::WHITE,
|
||||
scale_factor: 1.25,
|
||||
},
|
||||
Layout::new(&node),
|
||||
mouse::Cursor::Unavailable,
|
||||
&viewport,
|
||||
);
|
||||
let pixels = renderer.screenshot(Size::new(250, 40), 1.25, Color::TRANSPARENT);
|
||||
assert_eq!(pixels.chunks_exact(4).any(|pixel| pixel[3] > 0), !native);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn border_coverage_tracks_physical_scale_and_high_contrast() {
|
||||
let mut renderer = cosmic::iced::futures::executor::block_on(<cosmic::Renderer as Headless>::new(
|
||||
|
||||
Reference in new issue
Block a user