10 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
976d311f94 v0.1.3: fönster som nuddar docklinjen räknas inte som överlapp
All checks were successful
check / check (push) Successful in 1m34s
release / build-release (push) Successful in 2m8s
Maximerade fönster slutar exakt på den reserverade linjen — med >= i
overlap-testet gömde dodge docken ändå. Nu krävs att fönstret tränger
in i zonen (0.5 px tolerans). Bekräftat av Björn: reservera plats +
dodge + maximering beter sig nu som förväntat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:39:41 +02:00
bd3d62c11c v0.1.2: reservera plats som faktiskt reserverar + kantvänlig reveal
All checks were successful
check / check (push) Successful in 1m36s
release / build-release (push) Successful in 2m6s
- Exclusive zone sätts explicit (uppmätt storlek + marginal) — gtk4-
  layer-shells auto-zon räknar bara för ytor som spänner kanten, och
  zonen ignoreras per spec för hörn-förankrade ytor. Därför spänner
  ytan nu alltid kanten när avoid_windows är på (transparenta flanker);
  ön ritas via panel-alignment och ser ut precis som förut.
- Zonen fryses och ligger kvar när docken dodge-göms (fönstret hålls
  mappat) — maximerade fönster stannar alltid vid docklinjen.
- Dodge-djupet använder senaste synliga storlek (ingen flapp).
- Reveal via kant-sensorn: docken STANNAR medan pekaren är kvar vid
  kanten/i dockområdet; göm-fördröjningen börjar först när man lämnar
  området (sensor_hover räknas som hover).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:37:55 +02:00
bf279c5f2f v0.1.1: reservera plats fungerar ihop med autohide
All checks were successful
check / check (push) Successful in 1m32s
release / build-release (push) Successful in 1m58s
Tidigare stängdes exclusive zone av så fort autohide var aktiv —
maximerade fönster täckte då hela skärmen och dodge gömde docken,
vilket motsäger vad 'reservera plats' lovar. Nu reserveras platsen
alltid när avoid_windows är på: maximerade fönster stannar vid dockan
(och triggar aldrig dodge), medan fönster man själv drar in i zonen
fortfarande gömmer den. Tydligare etikett i settings + docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 14:22:47 +02:00
ae292eea52 Ta om alla skärmbilder utan privat innehåll
Dolphin visade privata sökvägar i förra omgången — nya bilder med
neutrala fönster (konsole/fastfetch, Kwrite, kcalc; Dolphin är kvar
som pin i docken men körs inte). Hero, närbild, settings och hela
sexbilds-galleriet omtagna i nästlad headless-session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:33:56 +02:00
81fa622963 README: configgalleri + kortfattad vad-kan-konfigureras-lista
Galleri-montaget (sex configvarianter, live-omladdade i nästlad
headless-session) + kompakt beskrivning av alla configområden i README
och i configreferensens ingress (doc/usage.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 13:03:07 +02:00
c0f9563997 plan: M8 klar — waydock är Wayfire-DE:s dock
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 12:49:45 +02:00
b8674ce5f0 Skärmbilder i README + --settings-flagga
All checks were successful
check / check (push) Successful in 1m44s
Nya assets/screenshots (tagna i nästlad headless-Wayfire): hero-bild,
dock-närbild och settings-fönstret. Ny CLI-flagga --settings öppnar
inställningsfönstret direkt vid start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 01:55:04 +02:00
cfd1dbd4f4 plan: M1–M7 klara, v0.1.0 släppt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 01:43:32 +02:00
17 changed files with 243 additions and 55 deletions

2
Cargo.lock generated
View File

@@ -1042,7 +1042,7 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "waydock" name = "waydock"
version = "0.1.0" 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.0" 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

@@ -6,6 +6,46 @@ Rust/GTK4 som **en enda binär**. Pinnade appar, körande fönster med
gruppering, per-skärm-kontext, konfigurerbara menyer, settings-GUI och gruppering, per-skärm-kontext, konfigurerbara menyer, settings-GUI och
live-omladdning av JSON-configen. live-omladdning av JSON-configen.
## Skärmbilder
![waydock](assets/screenshots/dock-hero.png)
| Dockan (pins, grupper, indikatorer) | Inställningar (per skärm, live) |
|---|---|
| ![dock](assets/screenshots/dock-closeup.png) | ![settings](assets/screenshots/settings.png) |
Samma dock, sex olika configar — bara JSON-filen ändrades mellan bilderna:
![galleri](assets/screenshots/gallery.png)
## Vad kan konfigureras?
- **Placering**: nere/uppe/vänster/höger; centrerad/vänster-/högerjusterad ö
eller expanderad över hela kanten; tre marginaler (från kanten + start/slut,
minusvärden ok) — med ritad illustration i settings som visar måtten
- **Utseende**: panelfärg + transparens, accentfärg, hörnradie, kant, skugga,
ikonstorlek/-avstånd, hover-zoom, animationer, indikatorstil
- **Beteende**: klick (fokus/minimera/växla), mittklick, skroll (växla fönster
eller starta stoppad app), gruppering, fönsterlista vid klick eller hover,
peek/fokus vid hover i listan, tooltips, urgency-puls
- **Menyer**: välj exakt vilka val högerklicksmenyn ska ha
(minimera/maximera/flytta till skärm/pinna/stäng)
- **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
- **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
sensor vid kanten och animation
- **Hotspots**: hot corners/edges som kör kommandon eller Wayfire-IPC
(t.ex. scale-översikten)
Allt via `~/.config/waydock/config.json` (live-omladdning) **eller**
settings-GUI:t — högerklicka på tom dockyta → Inställningar…
- Config: `~/.config/waydock/config.json` (skapas automatiskt första starten) - Config: `~/.config/waydock/config.json` (skapas automatiskt första starten)
- Djupare docs: [`doc/plan.md`](doc/plan.md), - Djupare docs: [`doc/plan.md`](doc/plan.md),
[`doc/architecture.md`](doc/architecture.md), [`doc/architecture.md`](doc/architecture.md),

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -42,6 +42,8 @@ skriver samma fil. Schema dokumenteras i `doc/usage.md`.
## Milestones ## Milestones
1. **M0 Scaffold** — repo, docs, CI, VS Code-tasks, release-flöde *(klar)* 1. **M0 Scaffold** — repo, docs, CI, VS Code-tasks, release-flöde *(klar)*
(M1M7 klara 2026-08-30 — v0.1.0 släppt: binär 1,6 MB, en-raders-install
verifierad, repot publikt. M8 klar — waydock är Wayfire-DE:s dock.)
2. **M1 Panelskelett** — layer-shell-fönster per skärm; position 2. **M1 Panelskelett** — layer-shell-fönster per skärm; position
(top/bottom/left/right), centrering, marginal, layer, exclusive zone; (top/bottom/left/right), centrering, marginal, layer, exclusive zone;
CSS-pipeline från config (bakgrund/opacity/radie/skugga/kant); CSS-pipeline från config (bakgrund/opacity/radie/skugga/kant);
@@ -64,8 +66,8 @@ skriver samma fil. Schema dokumenteras i `doc/usage.md`.
hot corners/edges med exec- eller IPC-actions hot corners/edges med exec- eller IPC-actions
8. **M7 Polish & släpp** — profilering (mål: 0 % CPU i vila, < 70 MB RAM), 8. **M7 Polish & släpp** — profilering (mål: 0 % CPU i vila, < 70 MB RAM),
release v0.1.0, wiki-docs release v0.1.0, wiki-docs
9. **M8 Wayfire-DE-integration** ersätt `autostart/11-dock.sh` 9. **M8 Wayfire-DE-integration** *(klar 2026-08-30)* `autostart/11-dock.sh`
(sfwbar) med waydock, pensionera sfwbar-configen kör waydock, sfwbar-configen pensionerad
## Roadmap / expansions (överenskomna) ## Roadmap / expansions (överenskomna)

View File

@@ -7,6 +7,7 @@ waydock # normal start
waydock --log debug # pratigare logg till stderr waydock --log debug # pratigare logg till stderr
waydock --log-file ~/.cache/waydock.log waydock --log-file ~/.cache/waydock.log
waydock --config /annan/väg.json waydock --config /annan/väg.json
waydock --settings # öppna inställningsfönstret direkt
``` ```
Vid första start skapas `~/.config/waydock/config.json` med defaults. Vid första start skapas `~/.config/waydock/config.json` med defaults.
@@ -44,6 +45,14 @@ inställningar; "Återställ skärmen till globalt" tar bort dem.
## Configreferens (`config.json`) ## Configreferens (`config.json`)
I korthet kan du styra: **placering** (kant, justering, ö/expanderad,
marginaler), **utseende** (färger, transparens, radie, skugga, ikoner,
indikatorer, animationer), **beteende** (klick/mittklick/skroll,
gruppering, fönsterlistans öppning och hover-effekt), **menyinnehåll**,
**specialknappar**, **per-skärm-överridar**, **autohide/dodge**,
**hotspots** och **loggning**. Exempelgalleri med färdiga teman finns i
[wikin](https://gitea.brasse-pc.eu/brasse/waydock/wiki/Konfiguration).
```jsonc ```jsonc
{ {
"position": "bottom", // bottom | top | left | right "position": "bottom", // bottom | top | left | right
@@ -55,7 +64,13 @@ inställningar; "Återställ skärmen till globalt" tar bort dem.
// 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)
"avoid_windows": false, // true = reservera plats (exclusive zone) "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
// stannar vid dockan. Funkar IHOP med autohide —
// då gömmer dodge bara för fönster du själv
// drar in över dockan
"appearance": { "appearance": {
"background": "#222222", // panelfärg "background": "#222222", // panelfärg

6
install.sh Normal file → Executable file
View File

@@ -13,9 +13,11 @@ command -v gtk4-launch >/dev/null 2>&1 || \
mkdir -p "$DEST" mkdir -p "$DEST"
echo "Hämtar waydock (latest)…" echo "Hämtar waydock (latest)…"
curl -fSL -o "$DEST/waydock" \ # temp + mv: atomiskt byte som funkar även när waydock kör (ETXTBSY)
curl -fSL -o "$DEST/waydock.new" \
"$BASE/releases/download/latest/waydock-latest-linux-x64" "$BASE/releases/download/latest/waydock-latest-linux-x64"
chmod +x "$DEST/waydock" chmod +x "$DEST/waydock.new"
mv -f "$DEST/waydock.new" "$DEST/waydock"
# ikon + .desktop-fil # ikon + .desktop-fil
mkdir -p "$HOME/.local/share/icons/hicolor/scalable/apps" "$HOME/.local/share/applications" mkdir -p "$HOME/.local/share/icons/hicolor/scalable/apps" "$HOME/.local/share/applications"

0
release.sh Normal file → Executable file
View File

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,15 +53,21 @@ 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,
/// pekaren är över dockan just nu /// pekaren är över dockan just nu
pub hover: Cell<bool>, pub hover: Cell<bool>,
/// pekaren är på väcknings-sensorn vid kanten
pub sensor_hover: Cell<bool>,
/// dockan är undan-gömd av autohide /// dockan är undan-gömd av autohide
pub hidden: Cell<bool>, pub hidden: Cell<bool>,
/// något fönster överlappar dockans kantzon (dodge) /// något fönster överlappar dockans kantzon (dodge)
pub dodge_overlap: Cell<bool>, pub dodge_overlap: Cell<bool>,
/// senaste synliga storlek längs huvudaxeln (px) — används för
/// exclusive zone och dodge-djup även när docken är gömd
pub main_size: Cell<i32>,
/// generationsräknare för fördröjd göm-timer /// generationsräknare för fördröjd göm-timer
hide_generation: Cell<u64>, hide_generation: Cell<u64>,
/// sensorremsan som väcker dockan när den är gömd /// sensorremsan som väcker dockan när den är gömd
@@ -113,14 +119,17 @@ fn configure_layer(win: &gtk::ApplicationWindow, cfg: &Config, monitor: &gdk::Mo
let (side_start, side_end) = side_edges(&cfg.position); let (side_start, side_end) = side_edges(&cfg.position);
win.set_anchor(edge, true); win.set_anchor(edge, true);
win.set_margin(edge, cfg.margin); win.set_margin(edge, cfg.margin);
if cfg.expand { // exclusive zone honoreras bara för ytor som är förankrade i EN kant
// täck hela kanten; start/slut-marginalerna drar in baren // eller spänner kanten (spec + Wayfires tolkning) — så med reservera
// plats spänner ytan alltid kanten; ön ritas ändå som en ö via
// panelens alignment (flankerna är transparenta)
if cfg.expand || cfg.avoid_windows {
win.set_anchor(side_start, true); win.set_anchor(side_start, true);
win.set_anchor(side_end, true); win.set_anchor(side_end, true);
win.set_margin(side_start, cfg.margin_start); win.set_margin(side_start, cfg.margin_start.max(0));
win.set_margin(side_end, cfg.margin_end); win.set_margin(side_end, cfg.margin_end.max(0));
} else { } else {
// ö: förankra bara i vald ände (ingen sidoförankring = centrerad) // ö utan reservation: förankra bara i vald ände
match cfg.alignment { match cfg.alignment {
Alignment::Center => {} Alignment::Center => {}
Alignment::Start => { Alignment::Start => {
@@ -133,11 +142,39 @@ fn configure_layer(win: &gtk::ApplicationWindow, cfg: &Config, monitor: &gdk::Mo
} }
} }
} }
if cfg.avoid_windows && !cfg.autohide.enabled { // exclusive zone sätts explicit efter map (auto-zonen i
win.auto_exclusive_zone_enable(); // gtk4-layer-shell fungerar bara för ytor som spänner hela kanten)
} else {
win.set_exclusive_zone(0); win.set_exclusive_zone(0);
} }
/// Reservera plats: zon = dockans uppmätta storlek + marginal. Zonen
/// ligger kvar även när docken är dodge-gömd (fönstret hålls mappat),
/// så maximerade fönster alltid stannar vid docklinjen.
pub fn update_exclusive_zone(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
let cfg = ctx.cfg.borrow().for_output(&dock.connector);
if !cfg.avoid_windows {
if !dock.hidden.get() {
dock.window.set_exclusive_zone(0);
}
return;
}
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 {
dock.panel.height()
} else {
dock.panel.width()
};
if size > 1 && !dock.hidden.get() {
dock.main_size.set(size);
}
if dock.main_size.get() > 1 {
let zone =
(dock.main_size.get() + cfg.margin.max(0) + cfg.reserve_margin).max(1);
debug!("{}: exclusive zone {}px", dock.connector, zone);
dock.window.set_exclusive_zone(zone);
}
} }
/// Bygg om alla dockfönster från grunden (vid start, configbyte, skärmbyte). /// Bygg om alla dockfönster från grunden (vid start, configbyte, skärmbyte).
@@ -201,9 +238,17 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
items_box.set_vexpand(true); items_box.set_vexpand(true);
items_box.set_valign(items_align); items_box.set_valign(items_align);
} }
} else { } else if orient == gtk::Orientation::Horizontal {
panel.set_halign(gtk::Align::Center); // ö: när ytan spänner kanten (reservera plats) placerar
// panel-alignmenten ön; expand krävs för att alignment ska
// verka i hela ytans bredd (annars packas panelen först)
panel.set_hexpand(true);
panel.set_halign(items_align);
panel.set_valign(gtk::Align::Center); panel.set_valign(gtk::Align::Center);
} else {
panel.set_vexpand(true);
panel.set_halign(gtk::Align::Center);
panel.set_valign(items_align);
} }
panel.append(&items_box); panel.append(&items_box);
@@ -272,12 +317,15 @@ 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,
hover: Cell::new(false), hover: Cell::new(false),
sensor_hover: Cell::new(false),
hidden: Cell::new(false), hidden: Cell::new(false),
dodge_overlap: Cell::new(false), dodge_overlap: Cell::new(false),
main_size: Cell::new(0),
hide_generation: Cell::new(0), hide_generation: Cell::new(0),
sensor: RefCell::new(None), sensor: RefCell::new(None),
}); });
@@ -287,13 +335,13 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
empty_click.set_button(3); empty_click.set_button(3);
{ {
let ctx = ctx.clone(); let ctx = ctx.clone();
let panel2 = panel.clone(); let outer2 = outer.clone();
empty_click.connect_pressed(move |gesture, _, x, y| { empty_click.connect_pressed(move |gesture, _, x, y| {
gesture.set_state(gtk::EventSequenceState::Claimed); gesture.set_state(gtk::EventSequenceState::Claimed);
menu::popup_panel_menu(&ctx, &panel2, x, y); menu::popup_panel_menu(&ctx, &outer2, x, y);
}); });
} }
panel.add_controller(empty_click); outer.add_controller(empty_click);
// hover-spårning för autohide // hover-spårning för autohide
let motion = gtk::EventControllerMotion::new(); let motion = gtk::EventControllerMotion::new();
@@ -355,17 +403,20 @@ fn build_sensor(ctx: &Rc<Ctx>, dock: &Rc<Dock>, monitor: &gdk::Monitor) {
let ctx = ctx.clone(); let ctx = ctx.clone();
let dock2 = dock.clone(); let dock2 = dock.clone();
motion.connect_enter(move |_, _, _| { motion.connect_enter(move |_, _, _| {
dock2.sensor_hover.set(true);
dock2.hide_generation.set(dock2.hide_generation.get() + 1);
if dock2.hidden.get() { if dock2.hidden.get() {
set_dock_hidden(&dock2, false); set_dock_hidden(&ctx, &dock2, false);
// om pekaren aldrig når dockan: göm igen efter en stund
let ctx2 = ctx.clone();
let dock3 = dock2.clone();
glib::timeout_add_local_once(Duration::from_millis(1500), move || {
if !dock3.hover.get() {
update_autohide(&ctx2);
} }
}); });
} }
{
let ctx = ctx.clone();
let dock2 = dock.clone();
motion.connect_leave(move |_| {
dock2.sensor_hover.set(false);
// göm-fördröjningen börjar först NÄR man lämnar kantområdet
schedule_hide(&ctx, &dock2);
}); });
} }
strip.add_controller(motion); strip.add_controller(motion);
@@ -374,20 +425,41 @@ fn build_sensor(ctx: &Rc<Ctx>, dock: &Rc<Dock>, monitor: &gdk::Monitor) {
*dock.sensor.borrow_mut() = Some(sensor); *dock.sensor.borrow_mut() = Some(sensor);
} }
fn set_dock_hidden(dock: &Rc<Dock>, hidden: bool) { fn set_dock_hidden(ctx: &Ctx, dock: &Rc<Dock>, hidden: bool) {
if dock.hidden.get() == hidden { if dock.hidden.get() == hidden {
return; return;
} }
let cfg = ctx.cfg.borrow().for_output(&dock.connector);
let horizontal = matches!(cfg.position, Position::Bottom | Position::Top);
// kom ihåg synlig storlek innan vi gömmer
if hidden {
let size = if horizontal { dock.panel.height() } else { dock.panel.width() };
if size > 1 {
dock.main_size.set(size);
}
}
dock.hidden.set(hidden); dock.hidden.set(hidden);
if hidden { if hidden {
let duration = dock.revealer.transition_duration();
dock.revealer.set_reveal_child(false); dock.revealer.set_reveal_child(false);
if cfg.avoid_windows {
// reservera plats även gömd: håll fönstret mappat (kollapsat)
// med frusen zon — maximerade fönster stannar vid docklinjen
dock.window.set_exclusive_zone(
(dock.main_size.get().max(1) + cfg.margin.max(0) + cfg.reserve_margin)
.max(1),
);
} else {
let duration = dock.revealer.transition_duration();
let dock2 = dock.clone(); let dock2 = dock.clone();
glib::timeout_add_local_once(Duration::from_millis(duration as u64 + 30), move || { glib::timeout_add_local_once(
Duration::from_millis(duration as u64 + 30),
move || {
if dock2.hidden.get() { if dock2.hidden.get() {
dock2.window.set_visible(false); dock2.window.set_visible(false);
} }
}); },
);
}
} else { } else {
dock.window.set_visible(true); dock.window.set_visible(true);
dock.revealer.set_reveal_child(true); dock.revealer.set_reveal_child(true);
@@ -396,7 +468,13 @@ fn set_dock_hidden(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() { // 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 {
@@ -414,7 +492,14 @@ fn should_hide(ctx: &Ctx, dock: &Dock) -> bool {
/// Applicera göm/visa-läget på alla dockor. /// Applicera göm/visa-läget på alla dockor.
pub fn update_autohide(ctx: &Rc<Ctx>) { pub fn update_autohide(ctx: &Rc<Ctx>) {
for dock in ctx.docks.borrow().iter() { for dock in ctx.docks.borrow().iter() {
set_dock_hidden(dock, should_hide(ctx, dock)); set_dock_hidden(ctx, dock, should_hide(ctx, dock));
}
}
/// 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);
} }
} }
@@ -426,7 +511,7 @@ fn schedule_hide(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
let dock = dock.clone(); let dock = dock.clone();
glib::timeout_add_local_once(Duration::from_millis(delay as u64), move || { glib::timeout_add_local_once(Duration::from_millis(delay as u64), move || {
if dock.hide_generation.get() == generation && should_hide(&ctx, &dock) { if dock.hide_generation.get() == generation && should_hide(&ctx, &dock) {
set_dock_hidden(&dock, true); set_dock_hidden(&ctx, &dock, true);
} }
}); });
} }
@@ -459,11 +544,19 @@ pub fn recompute_dodge(ctx: &Rc<Ctx>) {
}; };
let ow = output["geometry"]["width"].as_f64().unwrap_or(0.0); let ow = output["geometry"]["width"].as_f64().unwrap_or(0.0);
let oh = output["geometry"]["height"].as_f64().unwrap_or(0.0); let oh = output["geometry"]["height"].as_f64().unwrap_or(0.0);
// zonen dockan upptar vid sin kant (fönstrets storlek + marginal) // zonen dockan upptar vid sin kant: senaste SYNLIGA storlek —
let depth = match position { // den kollapsade storleken skulle få hidden/synlig att flappa
Position::Bottom | Position::Top => dock.window.height().max(40) + margin, if !dock.hidden.get() {
_ => dock.window.width().max(40) + margin, let current = match position {
} as f64; Position::Bottom | Position::Top => dock.panel.height(),
_ => dock.panel.width(),
};
if current > 1 {
dock.main_size.set(current);
}
}
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")
@@ -483,11 +576,14 @@ pub fn recompute_dodge(ctx: &Rc<Ctx>) {
if x + w <= 0.0 || y + h <= 0.0 || x >= ow || y >= oh { if x + w <= 0.0 || y + h <= 0.0 || x >= ow || y >= oh {
return false; return false;
} }
// fönstret måste tränga IN i zonen — ett maximerat fönster
// som slutar exakt på docklinjen (reserverad plats) räknas
// inte som överlapp (0.5 px tolerans för avrundning)
match position { match position {
Position::Bottom => y + h >= oh - depth, Position::Bottom => y + h > oh - depth + 0.5,
Position::Top => y <= depth, Position::Top => y < depth - 0.5,
Position::Left => x <= depth, Position::Left => x < depth - 0.5,
Position::Right => x + w >= ow - depth, Position::Right => x + w > ow - depth + 0.5,
} }
}); });
dock.dodge_overlap.set(overlap); dock.dodge_overlap.set(overlap);
@@ -576,6 +672,21 @@ pub fn refresh_items(ctx: &Rc<Ctx>) {
let widget = build_item_widget(ctx, &dock.connector, Rc::new(item)); let widget = build_item_widget(ctx, &dock.connector, Rc::new(item));
dock.items_box.append(&widget); dock.items_box.append(&widget);
} }
let ctx2 = ctx.clone();
let dock2 = dock.clone();
// första configure-rundan är asynkron — mät tills storleken finns
let mut attempts = 0;
glib::timeout_add_local(Duration::from_millis(120), move |
| {
attempts += 1;
update_exclusive_zone(&ctx2, &dock2);
let done = dock2.main_size.get() > 1 || attempts > 20;
if done {
glib::ControlFlow::Break
} else {
glib::ControlFlow::Continue
}
});
} }
} }

View File

@@ -28,14 +28,16 @@ struct Cli {
config: Option<PathBuf>, config: Option<PathBuf>,
log: Option<String>, log: Option<String>,
log_file: Option<String>, log_file: Option<String>,
open_settings: bool,
} }
fn parse_cli() -> Cli { fn parse_cli() -> Cli {
let mut cli = Cli { config: None, log: None, log_file: None }; let mut cli = Cli { config: None, log: None, log_file: None, open_settings: false };
let mut args = std::env::args().skip(1); let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
match arg.as_str() { match arg.as_str() {
"--config" => cli.config = args.next().map(PathBuf::from), "--config" => cli.config = args.next().map(PathBuf::from),
"--settings" => cli.open_settings = true,
"--log" => cli.log = args.next(), "--log" => cli.log = args.next(),
"--log-file" => cli.log_file = args.next(), "--log-file" => cli.log_file = args.next(),
"--version" | "-V" => { "--version" | "-V" => {
@@ -49,6 +51,7 @@ fn parse_cli() -> Cli {
--config <fil> annan configfil (default ~/.config/waydock/config.json)\n \ --config <fil> annan configfil (default ~/.config/waydock/config.json)\n \
--log <nivå> error|warn|info|debug|trace (även env WAYDOCK_LOG)\n \ --log <nivå> error|warn|info|debug|trace (även env WAYDOCK_LOG)\n \
--log-file <fil> skriv logg till fil\n \ --log-file <fil> skriv logg till fil\n \
--settings öppna inställningsfönstret vid start\n \
--version visa version" --version visa version"
); );
std::process::exit(0); std::process::exit(0);
@@ -128,20 +131,21 @@ fn main() -> glib::ExitCode {
let started = Rc::new(Cell::new(false)); let started = Rc::new(Cell::new(false));
let cfg_path2 = cfg_path.clone(); let cfg_path2 = cfg_path.clone();
let open_settings = cli.open_settings;
app.connect_activate(move |app| { app.connect_activate(move |app| {
if started.get() { if started.get() {
info!("waydock kör redan — ignorerar andra instansen"); info!("waydock kör redan — ignorerar andra instansen");
return; return;
} }
started.set(true); started.set(true);
activate(app, cfg_path2.clone()); activate(app, cfg_path2.clone(), open_settings);
}); });
// ge inte GTK våra egna flaggor // ge inte GTK våra egna flaggor
app.run_with_args::<&str>(&[]) app.run_with_args::<&str>(&[])
} }
fn activate(app: &gtk::Application, cfg_path: PathBuf) { fn activate(app: &gtk::Application, cfg_path: PathBuf, open_settings: bool) {
let cfg: config::SharedConfig = Rc::new(RefCell::new(config::load_or_create(&cfg_path))); let cfg: config::SharedConfig = Rc::new(RefCell::new(config::load_or_create(&cfg_path)));
let ctx = Rc::new(dock::Ctx { let ctx = Rc::new(dock::Ctx {
@@ -224,6 +228,10 @@ fn activate(app: &gtk::Application, cfg_path: PathBuf) {
); );
} }
if open_settings {
settings::open(&ctx);
}
// skärmar in/ut → bygg om (debouncat, skärmar kommer i klungor) // skärmar in/ut → bygg om (debouncat, skärmar kommer i klungor)
{ {
let ctx2 = ctx.clone(); let ctx2 = ctx.clone();

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

@@ -467,7 +467,8 @@ fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
_ => Layer::Top, _ => Layer::Top,
}; };
})); }));
row(&list, "Reservera plats (undvik fönster)", &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));