Per-skärm-inställningar med skärmväljare, kant-i-kant-marginaler
All checks were successful
check / check (push) Successful in 1m39s
All checks were successful
check / check (push) Successful in 1m39s
- outputs{} är nu generiska JSON-överridar: vilket configfält som helst
kan sättas per skärm och deep-mergas över den globala configen
(Config::for_output). Docka, autohide, dodge, beteende, hotspots och
CSS (scopat på window.o-<connector>) läser skärmens effektiva config.
- Settings har skärmväljare överst: klickbar förhandsgranskning av
skärmarna + deras barer, 'Alla skärmar' (standard) eller enskild
skärm. Bara ändrade fält sparas som override (json-diff);
återställ-knapp tar bort skärmens överridar. * = har egna.
- Marginaler: skuggutrymmet läggs inte längre på förankrade sidor så
0 = kant i kant; minusvärden tillåts (A ner till -64, B/C -256).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
112
src/dock.rs
112
src/dock.rs
@@ -16,8 +16,8 @@ use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::config::{
|
||||
Alignment, AutohideMode, ClickAction, GroupPopup, Layer as CfgLayer, MiddleClick,
|
||||
Position, ScrollAction, SharedConfig,
|
||||
Alignment, AutohideMode, ClickAction, Config, GroupPopup, Layer as CfgLayer,
|
||||
MiddleClick, Position, ScrollAction, SharedConfig,
|
||||
};
|
||||
use crate::icons::{self, AppMeta};
|
||||
use crate::menu;
|
||||
@@ -90,8 +90,7 @@ fn side_edges(pos: &Position) -> (Edge, Edge) {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_layer(win: >k::ApplicationWindow, ctx: &Ctx, monitor: &gdk::Monitor) {
|
||||
let cfg = ctx.cfg.borrow();
|
||||
fn configure_layer(win: >k::ApplicationWindow, cfg: &Config, monitor: &gdk::Monitor) {
|
||||
win.init_layer_shell();
|
||||
win.set_namespace(Some("waydock"));
|
||||
win.set_monitor(Some(monitor));
|
||||
@@ -167,9 +166,10 @@ pub fn rebuild_all(ctx: &Rc<Ctx>) {
|
||||
}
|
||||
|
||||
fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Dock> {
|
||||
let cfg = ctx.cfg.borrow();
|
||||
let cfg = ctx.cfg.borrow().for_output(&connector);
|
||||
let win = gtk::ApplicationWindow::new(&ctx.app);
|
||||
win.add_css_class("waydock");
|
||||
win.add_css_class(&crate::style::output_class(&connector));
|
||||
|
||||
let orient = orientation(&cfg.position);
|
||||
let panel = gtk::Box::new(orient, 0);
|
||||
@@ -199,17 +199,46 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
|
||||
}
|
||||
panel.append(&items_box);
|
||||
|
||||
// yttre marginal = plats för skuggan att rita
|
||||
// yttre marginal = plats för skuggan att rita — men INTE på sidor som
|
||||
// är förankrade mot skärmkanten, så att margin = 0 verkligen når kanten
|
||||
let outer = gtk::Box::new(orient, 0);
|
||||
let pad = if cfg.appearance.shadow.enabled {
|
||||
cfg.appearance.shadow.blur as i32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
outer.set_margin_top(pad);
|
||||
outer.set_margin_bottom(pad);
|
||||
outer.set_margin_start(pad);
|
||||
outer.set_margin_end(pad);
|
||||
let anchored_start = cfg.expand || cfg.alignment == Alignment::Start;
|
||||
let anchored_end = cfg.expand || cfg.alignment == Alignment::End;
|
||||
let (top, bottom, start, end) = match cfg.position {
|
||||
Position::Bottom => (
|
||||
pad,
|
||||
0,
|
||||
if anchored_start { 0 } else { pad },
|
||||
if anchored_end { 0 } else { pad },
|
||||
),
|
||||
Position::Top => (
|
||||
0,
|
||||
pad,
|
||||
if anchored_start { 0 } else { pad },
|
||||
if anchored_end { 0 } else { pad },
|
||||
),
|
||||
Position::Left => (
|
||||
if anchored_start { 0 } else { pad },
|
||||
if anchored_end { 0 } else { pad },
|
||||
0,
|
||||
pad,
|
||||
),
|
||||
Position::Right => (
|
||||
if anchored_start { 0 } else { pad },
|
||||
if anchored_end { 0 } else { pad },
|
||||
pad,
|
||||
0,
|
||||
),
|
||||
};
|
||||
outer.set_margin_top(top);
|
||||
outer.set_margin_bottom(bottom);
|
||||
outer.set_margin_start(start);
|
||||
outer.set_margin_end(end);
|
||||
outer.append(&panel);
|
||||
|
||||
// revealern ger autohide-animationen
|
||||
@@ -229,9 +258,8 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
|
||||
revealer.set_child(Some(&outer));
|
||||
win.set_child(Some(&revealer));
|
||||
let autohide_enabled = cfg.autohide.enabled;
|
||||
drop(cfg);
|
||||
|
||||
configure_layer(&win, ctx, monitor);
|
||||
configure_layer(&win, &cfg, monitor);
|
||||
menu::setup_dock_actions(ctx, &win);
|
||||
|
||||
let dock = Rc::new(Dock {
|
||||
@@ -289,7 +317,7 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
|
||||
|
||||
/// Den tunna remsan vid skärmkanten som väcker en gömd dock.
|
||||
fn build_sensor(ctx: &Rc<Ctx>, dock: &Rc<Dock>, monitor: &gdk::Monitor) {
|
||||
let cfg = ctx.cfg.borrow();
|
||||
let cfg = ctx.cfg.borrow().for_output(&dock.connector);
|
||||
let sensor = gtk::ApplicationWindow::new(&ctx.app);
|
||||
sensor.add_css_class("waydock-sensor");
|
||||
sensor.init_layer_shell();
|
||||
@@ -359,7 +387,7 @@ fn set_dock_hidden(dock: &Rc<Dock>, hidden: bool) {
|
||||
}
|
||||
|
||||
fn should_hide(ctx: &Ctx, dock: &Dock) -> bool {
|
||||
let cfg = ctx.cfg.borrow();
|
||||
let cfg = ctx.cfg.borrow().for_output(&dock.connector);
|
||||
if !cfg.autohide.enabled || dock.hover.get() {
|
||||
return false;
|
||||
}
|
||||
@@ -383,7 +411,7 @@ pub fn update_autohide(ctx: &Rc<Ctx>) {
|
||||
}
|
||||
|
||||
fn schedule_hide(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
|
||||
let delay = ctx.cfg.borrow().autohide.hide_delay_ms;
|
||||
let delay = ctx.cfg.borrow().for_output(&dock.connector).autohide.hide_delay_ms;
|
||||
let generation = dock.hide_generation.get() + 1;
|
||||
dock.hide_generation.set(generation);
|
||||
let ctx = ctx.clone();
|
||||
@@ -397,18 +425,24 @@ fn schedule_hide(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
|
||||
|
||||
/// Räkna om dodge-överlapp för alla dockor via Wayfire-IPC-geometri.
|
||||
pub fn recompute_dodge(ctx: &Rc<Ctx>) {
|
||||
{
|
||||
let cfg = ctx.cfg.borrow();
|
||||
if !cfg.autohide.enabled || cfg.autohide.mode != AutohideMode::Dodge {
|
||||
return;
|
||||
}
|
||||
// hämta bara geometri om någon dock alls kör dodge
|
||||
let any_dodge = ctx.docks.borrow().iter().any(|d| {
|
||||
let c = ctx.cfg.borrow().for_output(&d.connector);
|
||||
c.autohide.enabled && c.autohide.mode == AutohideMode::Dodge
|
||||
});
|
||||
if !any_dodge {
|
||||
return;
|
||||
}
|
||||
let Some(views) = wayfire_ipc::list_views() else { return };
|
||||
let Some(outputs) = wayfire_ipc::list_outputs() else { return };
|
||||
let position = ctx.cfg.borrow().position.clone();
|
||||
let margin = ctx.cfg.borrow().margin;
|
||||
|
||||
for dock in ctx.docks.borrow().iter() {
|
||||
let eff = ctx.cfg.borrow().for_output(&dock.connector);
|
||||
if !eff.autohide.enabled || eff.autohide.mode != AutohideMode::Dodge {
|
||||
dock.dodge_overlap.set(false);
|
||||
continue;
|
||||
}
|
||||
let (position, margin) = (eff.position.clone(), eff.margin);
|
||||
let Some(output) = outputs
|
||||
.iter()
|
||||
.find(|o| o["name"].as_str() == Some(dock.connector.as_str()))
|
||||
@@ -542,7 +576,11 @@ fn special_button(icon_name: &str, tooltip: &str) -> gtk::Button {
|
||||
}
|
||||
|
||||
fn add_special_buttons(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
|
||||
let specials = ctx.cfg.borrow().special_buttons.clone();
|
||||
let specials = ctx
|
||||
.cfg
|
||||
.borrow()
|
||||
.for_output(&dock.connector)
|
||||
.special_buttons;
|
||||
|
||||
if specials.overview && wayfire_ipc::available() {
|
||||
let btn = special_button("view-grid-symbolic", "Översikt (scale)");
|
||||
@@ -562,12 +600,11 @@ fn add_special_buttons(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
|
||||
}
|
||||
|
||||
fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::Button {
|
||||
let cfg = ctx.cfg.borrow();
|
||||
let cfg = ctx.cfg.borrow().for_output(connector);
|
||||
let icon_size = cfg.appearance.icon_size;
|
||||
let max_dots = cfg.appearance.indicator.max as usize;
|
||||
let tooltips = cfg.behavior.tooltips;
|
||||
let urgent_pulse = cfg.behavior.urgent_pulse;
|
||||
drop(cfg);
|
||||
|
||||
let btn = gtk::Button::new();
|
||||
btn.add_css_class("dock-item");
|
||||
@@ -639,8 +676,8 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
|
||||
gesture.set_state(gtk::EventSequenceState::Claimed);
|
||||
let live = live_item(&ctx, &conn2, &item);
|
||||
match gesture.current_button() {
|
||||
1 => primary_action(&ctx, &live, &btn2),
|
||||
2 => middle_action(&ctx, &live),
|
||||
1 => primary_action(&ctx, &conn2, &live, &btn2),
|
||||
2 => middle_action(&ctx, &conn2, &live),
|
||||
3 => menu::popup_item_menu(&ctx, &live, &btn2),
|
||||
_ => {}
|
||||
}
|
||||
@@ -649,9 +686,7 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
|
||||
btn.add_controller(click);
|
||||
|
||||
// hover-popup för grupperade appar (om valt i configen)
|
||||
if item.windows.len() > 1
|
||||
&& ctx.cfg.borrow().behavior.group_popup == GroupPopup::Hover
|
||||
{
|
||||
if item.windows.len() > 1 && cfg.behavior.group_popup == GroupPopup::Hover {
|
||||
let hover_ctl = gtk::EventControllerMotion::new();
|
||||
let ctx2 = ctx.clone();
|
||||
let item2 = item.clone();
|
||||
@@ -682,7 +717,7 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
|
||||
let item = item.clone();
|
||||
let conn2 = connector.to_string();
|
||||
scroll.connect_scroll(move |_, _, dy| {
|
||||
if ctx.cfg.borrow().behavior.scroll == ScrollAction::CycleWindows {
|
||||
if ctx.cfg.borrow().for_output(&conn2).behavior.scroll == ScrollAction::CycleWindows {
|
||||
let live = live_item(&ctx, &conn2, &item);
|
||||
cycle_windows(&ctx, &live, dy > 0.0);
|
||||
glib::Propagation::Stop
|
||||
@@ -696,28 +731,25 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
|
||||
btn
|
||||
}
|
||||
|
||||
fn primary_action(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
||||
fn primary_action(ctx: &Rc<Ctx>, connector: &str, item: &Rc<Item>, anchor: >k::Button) {
|
||||
let service = ctx.service.borrow();
|
||||
let Some(service) = service.as_ref() else {
|
||||
icons::launch(&item.meta);
|
||||
return;
|
||||
};
|
||||
let behavior = ctx.cfg.borrow().for_output(connector).behavior;
|
||||
match item.windows.len() {
|
||||
0 => icons::launch(&item.meta),
|
||||
1 => {
|
||||
let w = &item.windows[0];
|
||||
let click = ctx.cfg.borrow().behavior.click.clone();
|
||||
if w.activated && !w.minimized && click == ClickAction::FocusOrMinimize {
|
||||
if w.activated && !w.minimized && behavior.click == ClickAction::FocusOrMinimize {
|
||||
service.set_minimized(w, true);
|
||||
} else {
|
||||
service.activate(w);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (click, popup) = {
|
||||
let b = &ctx.cfg.borrow().behavior;
|
||||
(b.click.clone(), b.group_popup.clone())
|
||||
};
|
||||
let (click, popup) = (behavior.click.clone(), behavior.group_popup.clone());
|
||||
if click == ClickAction::Cycle || popup == GroupPopup::Hover {
|
||||
cycle_windows(ctx, item, true);
|
||||
} else {
|
||||
@@ -727,8 +759,8 @@ fn primary_action(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
||||
}
|
||||
}
|
||||
|
||||
fn middle_action(ctx: &Rc<Ctx>, item: &Rc<Item>) {
|
||||
let action = ctx.cfg.borrow().behavior.middle_click.clone();
|
||||
fn middle_action(ctx: &Rc<Ctx>, connector: &str, item: &Rc<Item>) {
|
||||
let action = ctx.cfg.borrow().for_output(connector).behavior.middle_click.clone();
|
||||
match action {
|
||||
MiddleClick::LaunchNew => icons::launch(&item.meta),
|
||||
MiddleClick::Close => {
|
||||
|
||||
Reference in New Issue
Block a user