Justering/expand/marginaler, hover-popup med peek, appikon
All checks were successful
check / check (push) Successful in 1m37s

- Ny layoutmodell: alignment (center/start/end längs kanten), expand
  (ö eller täck hela kanten) och tre marginaler — A från kanten,
  B/C i början/slutet av kanten. Funkar för alla fyra positionerna.
- Settings → Layout har en ritad illustration (cairo) som visar vad
  A/B/C betyder för vald position och uppdateras live.
- Fönsterlistan för grupper kan öppnas vid hover (group_popup) och
  stängs när pekaren lämnar den; hover på en rad kan peeka (alla andra
  fönster tonas ner via Wayfires alpha-plugin) eller fokusera fönstret
  (list_hover: peek/focus/none).
- Appikon (assets/waydock.svg) + .desktop-fil; installeras av
  install.sh och används av settings-fönstret.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 00:03:15 +02:00
parent c9cdf2edc3
commit 04d999642c
10 changed files with 490 additions and 32 deletions

View File

@@ -16,8 +16,8 @@ use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
use log::{debug, info};
use crate::config::{
AutohideMode, ClickAction, Layer as CfgLayer, MiddleClick, Position, ScrollAction,
SharedConfig,
Alignment, AutohideMode, ClickAction, GroupPopup, Layer as CfgLayer, MiddleClick,
Position, ScrollAction, SharedConfig,
};
use crate::icons::{self, AppMeta};
use crate::menu;
@@ -45,7 +45,7 @@ pub struct Ctx {
pub struct Dock {
pub window: gtk::ApplicationWindow,
pub panel: gtk::Box,
pub items_box: gtk::Box,
pub revealer: gtk::Revealer,
pub connector: String,
/// pekaren är över dockan just nu
@@ -103,12 +103,28 @@ fn configure_layer(win: &gtk::ApplicationWindow, ctx: &Ctx, monitor: &gdk::Monit
win.set_keyboard_mode(KeyboardMode::None);
let edge = main_edge(&cfg.position);
let (side_a, side_b) = side_edges(&cfg.position);
let (side_start, side_end) = side_edges(&cfg.position);
win.set_anchor(edge, true);
win.set_margin(edge, cfg.margin);
if !cfg.centered {
win.set_anchor(side_a, true);
win.set_anchor(side_b, true);
if cfg.expand {
// täck hela kanten; start/slut-marginalerna drar in baren
win.set_anchor(side_start, true);
win.set_anchor(side_end, true);
win.set_margin(side_start, cfg.margin_start);
win.set_margin(side_end, cfg.margin_end);
} else {
// ö: förankra bara i vald ände (ingen sidoförankring = centrerad)
match cfg.alignment {
Alignment::Center => {}
Alignment::Start => {
win.set_anchor(side_start, true);
win.set_margin(side_start, cfg.margin_start);
}
Alignment::End => {
win.set_anchor(side_end, true);
win.set_margin(side_end, cfg.margin_end);
}
}
}
if cfg.avoid_windows && !cfg.autohide.enabled {
win.auto_exclusive_zone_enable();
@@ -158,8 +174,30 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
let orient = orientation(&cfg.position);
let panel = gtk::Box::new(orient, 0);
panel.add_css_class("dock-panel");
panel.set_halign(gtk::Align::Center);
panel.set_valign(gtk::Align::Center);
let items_align = match cfg.alignment {
Alignment::Center => gtk::Align::Center,
Alignment::Start => gtk::Align::Start,
Alignment::End => gtk::Align::End,
};
let items_box = gtk::Box::new(orient, 0);
if cfg.expand {
// panelbakgrunden täcker kanten; ikonerna läggs enligt alignment
panel.set_halign(gtk::Align::Fill);
panel.set_valign(gtk::Align::Fill);
if orient == gtk::Orientation::Horizontal {
panel.set_hexpand(true);
items_box.set_hexpand(true);
items_box.set_halign(items_align);
} else {
panel.set_vexpand(true);
items_box.set_vexpand(true);
items_box.set_valign(items_align);
}
} else {
panel.set_halign(gtk::Align::Center);
panel.set_valign(gtk::Align::Center);
}
panel.append(&items_box);
// yttre marginal = plats för skuggan att rita
let outer = gtk::Box::new(orient, 0);
@@ -198,7 +236,7 @@ fn build_dock(ctx: &Rc<Ctx>, monitor: &gdk::Monitor, connector: String) -> Rc<Do
let dock = Rc::new(Dock {
window: win.clone(),
panel: panel.clone(),
items_box: items_box.clone(),
revealer,
connector,
hover: Cell::new(false),
@@ -481,13 +519,13 @@ pub fn refresh_items(ctx: &Rc<Ctx>) {
.unwrap_or_default();
for dock in ctx.docks.borrow().iter() {
while let Some(child) = dock.panel.first_child() {
dock.panel.remove(&child);
while let Some(child) = dock.items_box.first_child() {
dock.items_box.remove(&child);
}
add_special_buttons(ctx, dock);
for item in build_items(ctx, &dock.connector, &snapshot) {
let widget = build_item_widget(ctx, &dock.connector, Rc::new(item));
dock.panel.append(&widget);
dock.items_box.append(&widget);
}
}
}
@@ -509,7 +547,7 @@ fn add_special_buttons(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
if specials.overview && wayfire_ipc::available() {
let btn = special_button("view-grid-symbolic", "Översikt (scale)");
btn.connect_clicked(|_| wayfire_ipc::toggle_scale());
dock.panel.append(&btn);
dock.items_box.append(&btn);
}
if specials.show_desktop {
let btn = special_button("user-desktop-symbolic", "Visa skrivbordet");
@@ -519,7 +557,7 @@ fn add_special_buttons(ctx: &Rc<Ctx>, dock: &Rc<Dock>) {
service.toggle_show_desktop();
}
});
dock.panel.append(&btn);
dock.items_box.append(&btn);
}
}
@@ -610,6 +648,33 @@ 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
{
let hover_ctl = gtk::EventControllerMotion::new();
let ctx2 = ctx.clone();
let item2 = item.clone();
let btn2 = btn.clone();
let conn2 = connector.to_string();
hover_ctl.connect_enter(move |ctl, _, _| {
let ctx3 = ctx2.clone();
let item3 = item2.clone();
let btn3 = btn2.clone();
let conn3 = conn2.clone();
let ctl2 = ctl.clone();
glib::timeout_add_local_once(Duration::from_millis(350), move || {
if ctl2.contains_pointer() {
let live = live_item(&ctx3, &conn3, &item3);
if live.windows.len() > 1 {
menu::popup_group_list(&ctx3, &live, &btn3);
}
}
});
});
btn.add_controller(hover_ctl);
}
// skroll: växla mellan gruppens fönster
let scroll = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
{
@@ -649,8 +714,11 @@ fn primary_action(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: &gtk::Button) {
}
}
_ => {
let click = ctx.cfg.borrow().behavior.click.clone();
if click == ClickAction::Cycle {
let (click, popup) = {
let b = &ctx.cfg.borrow().behavior;
(b.click.clone(), b.group_popup.clone())
};
if click == ClickAction::Cycle || popup == GroupPopup::Hover {
cycle_windows(ctx, item, true);
} else {
menu::popup_group_list(ctx, item, anchor);