Reduce Settings page and config reload overhead
Mount nearby shortcut and theme rows, preserve gallery keyboard navigation, reset page scrolling, and reuse native themes during palette transitions. Prepare and deduplicate live config reloads on a worker and apply only changed runtime sections. Align pointer focus and overview selection on overlapping outputs, add regression coverage, and clean up workspace Clippy warnings.
This commit is contained in:
1 parent
629414e0fb
commit
647eaf4ad9
23 files changed
+1098
-220
No files matched your search
@@ -235,15 +235,7 @@ impl Ferese {
|
||||
}
|
||||
|
||||
pub(crate) fn focus_output_at(&mut self, position: Point<f64, Logical>) {
|
||||
let output = self.space.outputs().find_map(|output| {
|
||||
let geometry = self.space.output_geometry(output)?;
|
||||
(position.x >= f64::from(geometry.loc.x)
|
||||
&& position.y >= f64::from(geometry.loc.y)
|
||||
&& position.x < f64::from(geometry.loc.x + geometry.size.w)
|
||||
&& position.y < f64::from(geometry.loc.y + geometry.size.h))
|
||||
.then(|| output.clone())
|
||||
});
|
||||
let Some(output) = output else {
|
||||
let Some(output) = self.space.output_under(position).next().cloned() else {
|
||||
return;
|
||||
};
|
||||
let Some(output_id) = self.output_ids.get(&output).copied() else {
|
||||
@@ -260,3 +252,182 @@ impl Ferese {
|
||||
self.relayout();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use smithay::backend::input::{ButtonState, Event, InputBackend, InputEvent, PointerButtonEvent, UnusedEvent};
|
||||
use smithay::backend::winit::WinitVirtualDevice;
|
||||
use smithay::input::pointer::MotionEvent;
|
||||
use smithay::utils::SERIAL_COUNTER;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestInput;
|
||||
|
||||
impl InputBackend for TestInput {
|
||||
type Device = WinitVirtualDevice;
|
||||
type PointerButtonEvent = TestButton;
|
||||
type KeyboardKeyEvent = UnusedEvent;
|
||||
type PointerAxisEvent = UnusedEvent;
|
||||
type PointerMotionEvent = UnusedEvent;
|
||||
type PointerMotionAbsoluteEvent = UnusedEvent;
|
||||
type GestureSwipeBeginEvent = UnusedEvent;
|
||||
type GestureSwipeUpdateEvent = UnusedEvent;
|
||||
type GestureSwipeEndEvent = UnusedEvent;
|
||||
type GesturePinchBeginEvent = UnusedEvent;
|
||||
type GesturePinchUpdateEvent = UnusedEvent;
|
||||
type GesturePinchEndEvent = UnusedEvent;
|
||||
type GestureHoldBeginEvent = UnusedEvent;
|
||||
type GestureHoldEndEvent = UnusedEvent;
|
||||
type TouchDownEvent = UnusedEvent;
|
||||
type TouchUpEvent = UnusedEvent;
|
||||
type TouchMotionEvent = UnusedEvent;
|
||||
type TouchCancelEvent = UnusedEvent;
|
||||
type TouchFrameEvent = UnusedEvent;
|
||||
type TabletToolAxisEvent = UnusedEvent;
|
||||
type TabletToolProximityEvent = UnusedEvent;
|
||||
type TabletToolTipEvent = UnusedEvent;
|
||||
type TabletToolButtonEvent = UnusedEvent;
|
||||
type SwitchToggleEvent = UnusedEvent;
|
||||
type SpecialEvent = UnusedEvent;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestButton(ButtonState);
|
||||
|
||||
impl Event<TestInput> for TestButton {
|
||||
fn time(&self) -> u64 {
|
||||
1_000
|
||||
}
|
||||
fn device(&self) -> WinitVirtualDevice {
|
||||
WinitVirtualDevice
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerButtonEvent<TestInput> for TestButton {
|
||||
fn button_code(&self) -> u32 {
|
||||
0x110
|
||||
}
|
||||
fn state(&self) -> ButtonState {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn click(state: &mut Ferese, position: Point<f64, Logical>) {
|
||||
let pointer = state.seat.get_pointer().unwrap();
|
||||
pointer.motion(
|
||||
state,
|
||||
state.surface_under(position),
|
||||
&MotionEvent {
|
||||
location: position,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: 1,
|
||||
},
|
||||
);
|
||||
pointer.frame(state);
|
||||
|
||||
for button in [ButtonState::Pressed, ButtonState::Released] {
|
||||
state.process_input_event::<TestInput>(InputEvent::PointerButton {
|
||||
event: TestButton(button),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires a private XDG_RUNTIME_DIR and permission to bind test sockets"]
|
||||
fn overlapping_outputs_agree_on_pointer_focus_and_overview_selection() {
|
||||
let runtime = std::path::PathBuf::from(std::env::var_os("XDG_RUNTIME_DIR").unwrap());
|
||||
assert!(runtime.starts_with(std::env::temp_dir()));
|
||||
|
||||
let mut event_loop = EventLoop::try_new().unwrap();
|
||||
let config = crate::config::Config::default().runtime_config().unwrap();
|
||||
let mut state = Ferese::new(&mut event_loop, Display::new().unwrap(), config).unwrap();
|
||||
let mut outputs = Vec::new();
|
||||
|
||||
for name in ["first", "second"] {
|
||||
let output = Output::new(
|
||||
name.into(),
|
||||
smithay::output::PhysicalProperties {
|
||||
size: (0, 0).into(),
|
||||
subpixel: smithay::output::Subpixel::Unknown,
|
||||
make: "test".into(),
|
||||
model: "test".into(),
|
||||
},
|
||||
);
|
||||
output.change_current_state(
|
||||
Some(smithay::output::Mode {
|
||||
size: (1920, 1080).into(),
|
||||
refresh: 60_000,
|
||||
}),
|
||||
Some(smithay::utils::Transform::Normal),
|
||||
Some(smithay::output::Scale::Fractional(1.75)),
|
||||
Some((0, 0).into()),
|
||||
);
|
||||
state.space.map_output(&output, (0, 0));
|
||||
state.register_output(&output, name.into());
|
||||
outputs.push(output);
|
||||
}
|
||||
|
||||
let position = Point::from((300.0, 300.0));
|
||||
let expected_output = state.space.output_under(position).next().unwrap().clone();
|
||||
assert_eq!(expected_output, outputs[1]);
|
||||
let expected_workspace = state.workspace_under_pointer(position).unwrap();
|
||||
|
||||
state.focus_output_at(position);
|
||||
assert_eq!(state.workspaces.active_id(), expected_workspace);
|
||||
assert_eq!(
|
||||
state.output_workspaces.focused_output(),
|
||||
state.output_id(&expected_output)
|
||||
);
|
||||
|
||||
state
|
||||
.workspaces
|
||||
.insert_window(WindowId(1), Axis::Horizontal, 0.5)
|
||||
.unwrap();
|
||||
state.relayout();
|
||||
state.set_overview_active(true);
|
||||
let card = state
|
||||
.overview_workspace_cards(&expected_output)
|
||||
.into_iter()
|
||||
.find(|card| card.workspace != expected_workspace)
|
||||
.expect("occupied workspace has an empty successor");
|
||||
let position = Point::from((
|
||||
card.rect.x + card.rect.width / 2.0,
|
||||
card.rect.y + card.rect.height / 2.0,
|
||||
));
|
||||
assert_eq!(state.overview_strip_at(position), Some(expected_output.clone()));
|
||||
click(&mut state, position);
|
||||
assert_eq!(state.workspaces.active_id(), card.workspace);
|
||||
state.windows.records.insert(
|
||||
WindowId(1),
|
||||
super::super::window_registry::WindowRecord {
|
||||
geometry: Some(WindowGeometry::new(Rect::new(0.0, 0.0, 800.0, 600.0), None)),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let occupied = state
|
||||
.overview_workspace_cards(&expected_output)
|
||||
.into_iter()
|
||||
.find(|card| card.workspace == expected_workspace)
|
||||
.unwrap();
|
||||
let preview = occupied.windows[0].1;
|
||||
let image_point = Point::from((preview.x + preview.width / 2.0, preview.y + preview.height / 2.0));
|
||||
let label_point = Point::from((
|
||||
occupied.rect.x + occupied.rect.width / 2.0,
|
||||
occupied.rect.y + occupied.rect.height - 8.0,
|
||||
));
|
||||
|
||||
for position in [image_point, label_point] {
|
||||
state.activate_managed_workspace(card.workspace);
|
||||
click(&mut state, position);
|
||||
assert_eq!(state.workspaces.active_id(), expected_workspace);
|
||||
assert!(state.overview.is_active());
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
state.output_workspaces.focused_output(),
|
||||
state.output_id(&expected_output)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user