Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 976d311f94 | |||
| bd3d62c11c | |||
| bf279c5f2f | |||
| ae292eea52 | |||
| 81fa622963 | |||
| c0f9563997 | |||
| b8674ce5f0 | |||
| cfd1dbd4f4 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1042,7 +1042,7 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "waydock"
|
name = "waydock"
|
||||||
version = "0.1.0"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "waydock"
|
name = "waydock"
|
||||||
version = "0.1.0"
|
version = "0.1.3"
|
||||||
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"
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -6,6 +6,40 @@ 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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
| Dockan (pins, grupper, indikatorer) | Inställningar (per skärm, live) |
|
||||||
|
|---|---|
|
||||||
|
|  |  |
|
||||||
|
|
||||||
|
Samma dock, sex olika configar — bara JSON-filen ändrades mellan bilderna:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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
|
||||||
|
- **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),
|
||||||
|
|||||||
BIN
assets/screenshots/dock-closeup.png
Normal file
BIN
assets/screenshots/dock-closeup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/screenshots/dock-hero.png
Normal file
BIN
assets/screenshots/dock-hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 439 KiB |
BIN
assets/screenshots/gallery.png
Normal file
BIN
assets/screenshots/gallery.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/screenshots/settings.png
Normal file
BIN
assets/screenshots/settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
@@ -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)*
|
||||||
|
(M1–M7 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)
|
||||||
|
|
||||||
|
|||||||
14
doc/usage.md
14
doc/usage.md
@@ -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,10 @@ 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)
|
"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
6
install.sh
Normal file → Executable 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
0
release.sh
Normal file → Executable file
174
src/dock.rs
174
src/dock.rs
@@ -58,10 +58,15 @@ pub struct Dock {
|
|||||||
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 +118,17 @@ fn configure_layer(win: >k::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,10 +141,35 @@ fn configure_layer(win: >k::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)
|
||||||
|
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);
|
||||||
|
let size = if horizontal {
|
||||||
|
dock.window.height()
|
||||||
} else {
|
} else {
|
||||||
win.set_exclusive_zone(0);
|
dock.window.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);
|
||||||
|
debug!("{}: exclusive zone {}px", dock.connector, zone);
|
||||||
|
dock.window.set_exclusive_zone(zone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,9 +234,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);
|
||||||
|
|
||||||
@@ -276,8 +317,10 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
|
|||||||
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 +330,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,39 +398,61 @@ 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);
|
||||||
|
|
||||||
sensor.present();
|
sensor.present();
|
||||||
*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.window.height() } else { dock.window.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);
|
||||||
let dock2 = dock.clone();
|
if cfg.avoid_windows {
|
||||||
glib::timeout_add_local_once(Duration::from_millis(duration as u64 + 30), move || {
|
// reservera plats även gömd: håll fönstret mappat (kollapsat)
|
||||||
if dock2.hidden.get() {
|
// med frusen zon — maximerade fönster stannar vid docklinjen
|
||||||
dock2.window.set_visible(false);
|
dock.window
|
||||||
}
|
.set_exclusive_zone(dock.main_size.get().max(1) + cfg.margin.max(0));
|
||||||
});
|
} else {
|
||||||
|
let duration = dock.revealer.transition_duration();
|
||||||
|
let dock2 = dock.clone();
|
||||||
|
glib::timeout_add_local_once(
|
||||||
|
Duration::from_millis(duration as u64 + 30),
|
||||||
|
move || {
|
||||||
|
if dock2.hidden.get() {
|
||||||
|
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 +461,7 @@ 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() {
|
if !cfg.autohide.enabled || dock.hover.get() || dock.sensor_hover.get() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
match cfg.autohide.mode {
|
match cfg.autohide.mode {
|
||||||
@@ -414,7 +479,7 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +491,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 +524,18 @@ 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.window.height(),
|
||||||
|
_ => dock.window.width(),
|
||||||
|
};
|
||||||
|
if current > 1 {
|
||||||
|
dock.main_size.set(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let depth = (dock.main_size.get().max(40) + margin) 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 +555,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 +651,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
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@@ -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: >k::Application, cfg_path: PathBuf) {
|
fn activate(app: >k::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: >k::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();
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ 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));
|
||||||
|
|
||||||
heading(&list, "Marginaler");
|
heading(&list, "Marginaler");
|
||||||
list.append(&margin_illustration(ctx));
|
list.append(&margin_illustration(ctx));
|
||||||
|
|||||||
Reference in New Issue
Block a user