2 Commits

Author SHA1 Message Date
c6b69dcec2 v0.1.5: öppna menyer håller dockan uppe
All checks were successful
check / check (push) Successful in 1m36s
release / build-release (push) Successful in 2m9s
Pekaren i en fönsterlista/meny (egen Wayland-yta) räknades som att den
lämnat dockan, så dodge gömde dockan under en öppen popup. Nu blockerar
öppna popovers autohide helt; när sista menyn stängs startar
göm-fördröjningen om. Bekräftat av Björn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:38:14 +02:00
5b5a802fda v0.1.4: reserve_margin + panelbaserad zon + reservationsdemo i docs
All checks were successful
check / check (push) Successful in 1m35s
release / build-release (push) Successful in 2m10s
- Zonen mäts på synliga panelen (inte fönstret med skuggutrymme) —
  maximerade fönster kommer ~20px närmare docken direkt
- Ny inställning reserve_margin (även i settings → Layout): finjustera
  avståndet mellan panel och maximerade fönster; minus tillåtet
- README + wiki: före/efter-bild som demonstrerar reservationen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:02:10 +02:00
9 changed files with 54 additions and 14 deletions

2
Cargo.lock generated
View File

@@ -1042,7 +1042,7 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "waydock" name = "waydock"
version = "0.1.3" version = "0.1.5"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"gtk4", "gtk4",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "waydock" name = "waydock"
version = "0.1.3" version = "0.1.5"
edition = "2021" edition = "2021"
description = "Modern, resurssnål dock för Wayfire — transparent, JSON-konfigurerad, per-skärm" description = "Modern, resurssnål dock för Wayfire — transparent, JSON-konfigurerad, per-skärm"
license = "MIT" license = "MIT"

View File

@@ -32,6 +32,12 @@ Samma dock, sex olika configar — bara JSON-filen ändrades mellan bilderna:
(minimera/maximera/flytta till skärm/pinna/stäng) (minimera/maximera/flytta till skärm/pinna/stäng)
- **Per skärm**: vilken inställning som helst kan överridas per skärm — - **Per skärm**: vilken inställning som helst kan överridas per skärm —
egna pins, position, färger, filter för bara-den-skärmens-fönster egna pins, position, färger, filter för bara-den-skärmens-fönster
- **Reservera plats**: maximerade fönster stannar vid docklinjen i stället
för att täcka dockan — finjustera avståndet med `reserve_margin`
(0 = kant i kant, minus = ännu närmare). Fungerar ihop med autohide:
![reservera plats](assets/screenshots/reserve-demo.png)
- **Autohide**: dodge (göm vid fönsteröverlapp) eller göm alltid, med - **Autohide**: dodge (göm vid fönsteröverlapp) eller göm alltid, med
sensor vid kanten och animation sensor vid kanten och animation
- **Hotspots**: hot corners/edges som kör kommandon eller Wayfire-IPC - **Hotspots**: hot corners/edges som kör kommandon eller Wayfire-IPC

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -64,6 +64,9 @@ gruppering, fönsterlistans öppning och hover-effekt), **menyinnehåll**,
// skuggutrymme läggs aldrig på förankrade sidor, // skuggutrymme läggs aldrig på förankrade sidor,
// så 0 = verkligen kant i kant // så 0 = verkligen kant i kant
"layer": "top", // top | overlay | bottom (always-on-top = overlay) "layer": "top", // top | overlay | bottom (always-on-top = overlay)
"reserve_margin": 0, // extra px ovanpå reservationen: 0 = fönster ända
// till panelkanten, minus = ännu närmare (in i
// skuggan), plus = luft mellan panel och fönster
"avoid_windows": false, // true = reservera plats: maximerade fönster "avoid_windows": false, // true = reservera plats: maximerade fönster
// stannar vid dockan. Funkar IHOP med autohide — // stannar vid dockan. Funkar IHOP med autohide —
// då gömmer dodge bara för fönster du själv // då gömmer dodge bara för fönster du själv

View File

@@ -312,6 +312,10 @@ pub struct Config {
pub expand: bool, pub expand: bool,
/// avstånd från kanten baren ligger mot (px) /// avstånd från kanten baren ligger mot (px)
pub margin: i32, pub margin: i32,
/// extra marginal ovanpå det reserverade området (px). 0 = fönster
/// maximeras ända till panelens kant; minus låter dem gå närmare
/// (in i skuggan), plus ger luft mellan panel och fönster
pub reserve_margin: i32,
/// marginal i början av kanten: vänster (topp/botten) resp. topp (vänster/höger) /// marginal i början av kanten: vänster (topp/botten) resp. topp (vänster/höger)
pub margin_start: i32, pub margin_start: i32,
/// marginal i slutet av kanten: höger resp. botten /// marginal i slutet av kanten: höger resp. botten
@@ -336,6 +340,7 @@ impl Default for Config {
alignment: Alignment::Center, alignment: Alignment::Center,
expand: false, expand: false,
margin: 8, margin: 8,
reserve_margin: 0,
margin_start: 0, margin_start: 0,
margin_end: 0, margin_end: 0,
layer: Layer::Top, layer: Layer::Top,

View File

@@ -53,6 +53,7 @@ pub struct Ctx {
pub struct Dock { pub struct Dock {
pub window: gtk::ApplicationWindow, pub window: gtk::ApplicationWindow,
pub panel: gtk::Box,
pub items_box: gtk::Box, pub items_box: gtk::Box,
pub revealer: gtk::Revealer, pub revealer: gtk::Revealer,
pub connector: String, pub connector: String,
@@ -158,16 +159,19 @@ pub fn update_exclusive_zone(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
return; return;
} }
let horizontal = matches!(cfg.position, Position::Bottom | Position::Top); let horizontal = matches!(cfg.position, Position::Bottom | Position::Top);
// mät den SYNLIGA panelen — fönstret innehåller även skuggutrymmet,
// som inte ska räknas in i reservationen
let size = if horizontal { let size = if horizontal {
dock.window.height() dock.panel.height()
} else { } else {
dock.window.width() dock.panel.width()
}; };
if size > 1 && !dock.hidden.get() { if size > 1 && !dock.hidden.get() {
dock.main_size.set(size); dock.main_size.set(size);
} }
if dock.main_size.get() > 1 { if dock.main_size.get() > 1 {
let zone = dock.main_size.get() + cfg.margin.max(0); let zone =
(dock.main_size.get() + cfg.margin.max(0) + cfg.reserve_margin).max(1);
debug!("{}: exclusive zone {}px", dock.connector, zone); debug!("{}: exclusive zone {}px", dock.connector, zone);
dock.window.set_exclusive_zone(zone); dock.window.set_exclusive_zone(zone);
} }
@@ -313,6 +317,7 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
let dock = Rc::new(Dock { let dock = Rc::new(Dock {
window: win.clone(), window: win.clone(),
panel: panel.clone(),
items_box: items_box.clone(), items_box: items_box.clone(),
revealer, revealer,
connector, connector,
@@ -428,7 +433,7 @@ fn set_dock_hidden(ctx: &Ctx, dock: &Rc<Dock>, hidden: bool) {
let horizontal = matches!(cfg.position, Position::Bottom | Position::Top); let horizontal = matches!(cfg.position, Position::Bottom | Position::Top);
// kom ihåg synlig storlek innan vi gömmer // kom ihåg synlig storlek innan vi gömmer
if hidden { if hidden {
let size = if horizontal { dock.window.height() } else { dock.window.width() }; let size = if horizontal { dock.panel.height() } else { dock.panel.width() };
if size > 1 { if size > 1 {
dock.main_size.set(size); dock.main_size.set(size);
} }
@@ -439,8 +444,10 @@ fn set_dock_hidden(ctx: &Ctx, dock: &Rc<Dock>, hidden: bool) {
if cfg.avoid_windows { if cfg.avoid_windows {
// reservera plats även gömd: håll fönstret mappat (kollapsat) // reservera plats även gömd: håll fönstret mappat (kollapsat)
// med frusen zon — maximerade fönster stannar vid docklinjen // med frusen zon — maximerade fönster stannar vid docklinjen
dock.window dock.window.set_exclusive_zone(
.set_exclusive_zone(dock.main_size.get().max(1) + cfg.margin.max(0)); (dock.main_size.get().max(1) + cfg.margin.max(0) + cfg.reserve_margin)
.max(1),
);
} else { } else {
let duration = dock.revealer.transition_duration(); let duration = dock.revealer.transition_duration();
let dock2 = dock.clone(); let dock2 = dock.clone();
@@ -461,7 +468,13 @@ fn set_dock_hidden(ctx: &Ctx, dock: &Rc<Dock>, hidden: bool) {
fn should_hide(ctx: &Ctx, dock: &Dock) -> bool { fn should_hide(ctx: &Ctx, dock: &Dock) -> bool {
let cfg = ctx.cfg.borrow().for_output(&dock.connector); let cfg = ctx.cfg.borrow().for_output(&dock.connector);
if !cfg.autohide.enabled || dock.hover.get() || dock.sensor_hover.get() { // en öppen meny/fönsterlista håller dockan uppe — pekaren i en popup
// (egen yta) räknas annars som "lämnat dockan"
if !cfg.autohide.enabled
|| dock.hover.get()
|| dock.sensor_hover.get()
|| ctx.open_popovers.get() > 0
{
return false; return false;
} }
match cfg.autohide.mode { match cfg.autohide.mode {
@@ -483,6 +496,13 @@ pub fn update_autohide(ctx: &Rc<Ctx>) {
} }
} }
/// Starta göm-fördröjningen för alla dockor (körs när sista menyn stängts).
pub fn schedule_hide_all(ctx: &Rc<Ctx>) {
for dock in ctx.docks.borrow().iter() {
schedule_hide(ctx, dock);
}
}
fn schedule_hide(ctx: &Rc<Ctx>, dock: &Rc<Dock>) { fn schedule_hide(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
let delay = ctx.cfg.borrow().for_output(&dock.connector).autohide.hide_delay_ms; let delay = ctx.cfg.borrow().for_output(&dock.connector).autohide.hide_delay_ms;
let generation = dock.hide_generation.get() + 1; let generation = dock.hide_generation.get() + 1;
@@ -528,14 +548,15 @@ pub fn recompute_dodge(ctx: &Rc<Ctx>) {
// den kollapsade storleken skulle få hidden/synlig att flappa // den kollapsade storleken skulle få hidden/synlig att flappa
if !dock.hidden.get() { if !dock.hidden.get() {
let current = match position { let current = match position {
Position::Bottom | Position::Top => dock.window.height(), Position::Bottom | Position::Top => dock.panel.height(),
_ => dock.window.width(), _ => dock.panel.width(),
}; };
if current > 1 { if current > 1 {
dock.main_size.set(current); dock.main_size.set(current);
} }
} }
let depth = (dock.main_size.get().max(40) + margin) as f64; let extra = if eff.avoid_windows { eff.reserve_margin } else { 0 };
let depth = (dock.main_size.get().max(24) + margin + extra).max(1) as f64;
let overlap = views.iter().any(|v| { let overlap = views.iter().any(|v| {
if v["role"].as_str() != Some("toplevel") if v["role"].as_str() != Some("toplevel")

View File

@@ -198,9 +198,13 @@ fn register_popover(ctx: &Rc<Ctx>, popover: &impl IsA<gtk::Popover>) {
let ctx2 = ctx.clone(); let ctx2 = ctx.clone();
glib::idle_add_local_once(move || { glib::idle_add_local_once(move || {
p.unparent(); p.unparent();
if ctx2.open_popovers.get() == 0 && ctx2.refresh_pending.get() { if ctx2.open_popovers.get() == 0 {
if ctx2.refresh_pending.get() {
crate::dock::refresh_items(&ctx2); crate::dock::refresh_items(&ctx2);
} }
// sista menyn stängd: autohide får ta vid igen (med delay)
crate::dock::schedule_hide_all(&ctx2);
}
}); });
}); });
} }

View File

@@ -468,6 +468,7 @@ fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
}; };
})); }));
row(&list, "Reservera plats (maximerade fönster stannar vid dockan)", &switch(ctx, cfg.avoid_windows, |c, v| c.avoid_windows = v)); row(&list, "Reservera plats (maximerade fönster stannar vid dockan)", &switch(ctx, cfg.avoid_windows, |c, v| c.avoid_windows = v));
row(&list, "Extra reservationsmarginal (px)", &spin(ctx, cfg.reserve_margin as f64, (-64.0, 128.0), 1.0, |c, v| c.reserve_margin = v as i32));
heading(&list, "Marginaler"); heading(&list, "Marginaler");
list.append(&margin_illustration(ctx)); list.append(&margin_illustration(ctx));