Justering/expand/marginaler, hover-popup med peek, appikon
All checks were successful
check / check (push) Successful in 1m37s
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:
10
assets/waydock.desktop
Normal file
10
assets/waydock.desktop
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=waydock
|
||||||
|
Comment=Dock för Wayfire — transparent, JSON-konfigurerad, per skärm
|
||||||
|
Exec=waydock
|
||||||
|
Icon=waydock
|
||||||
|
Terminal=false
|
||||||
|
Categories=Utility;
|
||||||
|
Keywords=dock;panel;wayfire;taskbar;
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
36
assets/waydock.svg
Normal file
36
assets/waydock.svg
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#2e2e33"/>
|
||||||
|
<stop offset="1" stop-color="#1c1c20"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="bar" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#3a3a41" stop-opacity="0.95"/>
|
||||||
|
<stop offset="1" stop-color="#26262b" stop-opacity="0.95"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="soft" x="-20%" y="-20%" width="140%" height="140%">
|
||||||
|
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000000" flood-opacity="0.45"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- skärmen -->
|
||||||
|
<rect x="8" y="8" width="112" height="112" rx="24" fill="url(#bg)"/>
|
||||||
|
<rect x="8" y="8" width="112" height="112" rx="24" fill="none" stroke="#ffffff" stroke-opacity="0.08" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- antydda fönster -->
|
||||||
|
<rect x="26" y="26" width="42" height="30" rx="6" fill="#ffffff" fill-opacity="0.10"/>
|
||||||
|
<rect x="58" y="38" width="44" height="32" rx="6" fill="#ffffff" fill-opacity="0.16"/>
|
||||||
|
|
||||||
|
<!-- dockan -->
|
||||||
|
<rect x="22" y="84" width="84" height="24" rx="12" fill="url(#bar)" filter="url(#soft)"/>
|
||||||
|
<rect x="22" y="84" width="84" height="24" rx="12" fill="none" stroke="#ffffff" stroke-opacity="0.12" stroke-width="1.5"/>
|
||||||
|
|
||||||
|
<!-- appar i dockan -->
|
||||||
|
<circle cx="40" cy="96" r="7" fill="#c9545d"/>
|
||||||
|
<circle cx="64" cy="96" r="7" fill="#5a8dd6"/>
|
||||||
|
<circle cx="88" cy="96" r="7" fill="#5fb878"/>
|
||||||
|
|
||||||
|
<!-- indikatorprick under aktiv app -->
|
||||||
|
<circle cx="40" cy="112" r="2.2" fill="#c9545d"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
19
doc/usage.md
19
doc/usage.md
@@ -23,7 +23,12 @@ Alla ändringar i filen slår igenom **live** — ingen omstart behövs.
|
|||||||
- **Högerklick på tom yta**: Inställningar…, Ladda om config, Avsluta
|
- **Högerklick på tom yta**: Inställningar…, Ladda om config, Avsluta
|
||||||
- **Visa skrivbord-knappen** minimerar allt; klick igen återställer
|
- **Visa skrivbord-knappen** minimerar allt; klick igen återställer
|
||||||
- **Översikt-knappen** togglar Wayfires scale-vy (kräver ipc-pluginen)
|
- **Översikt-knappen** togglar Wayfires scale-vy (kräver ipc-pluginen)
|
||||||
- Grupp-ikoner får en siffra och accentprickar; hovra för fönsterlistan
|
- Grupp-ikoner får en siffra och accentprickar. Fönsterlistan öppnas
|
||||||
|
vid klick eller hover (`behavior.group_popup`); hovra en rad i listan
|
||||||
|
för peek (bara det fönstret syns, andra tonas ner — kräver Wayfires
|
||||||
|
alpha-plugin) eller fokus, enligt `behavior.list_hover`
|
||||||
|
- Marginalillustrationen i Inställningar → Layout visar vad A/B/C-måtten
|
||||||
|
styr för aktuell position
|
||||||
|
|
||||||
## Settings-GUI
|
## Settings-GUI
|
||||||
|
|
||||||
@@ -35,8 +40,11 @@ JSON-filen och appliceras live. GUI:t och handredigering kan blandas fritt.
|
|||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
"position": "bottom", // bottom | top | left | right
|
"position": "bottom", // bottom | top | left | right
|
||||||
"centered": true, // ö i mitten i stället för hela kanten
|
"alignment": "center", // center | start | end — var längs kanten ön ligger
|
||||||
"margin": 8, // px från skärmkant (0 = kant i kant)
|
"expand": false, // true = täck hela kanten (panelen fyller bredden/höjden)
|
||||||
|
"margin": 8, // A: px från kanten baren ligger mot
|
||||||
|
"margin_start": 0, // B: marginal i början av kanten (vänster/topp)
|
||||||
|
"margin_end": 0, // C: marginal i slutet av kanten (höger/botten)
|
||||||
"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 (exclusive zone)
|
||||||
|
|
||||||
@@ -61,7 +69,10 @@ JSON-filen och appliceras live. GUI:t och handredigering kan blandas fritt.
|
|||||||
"scroll": "cycle_windows", // cycle_windows | none
|
"scroll": "cycle_windows", // cycle_windows | none
|
||||||
"group_apps": true,
|
"group_apps": true,
|
||||||
"tooltips": true,
|
"tooltips": true,
|
||||||
"urgent_pulse": true
|
"urgent_pulse": true,
|
||||||
|
"group_popup": "click", // click | hover — hur fönsterlistan öppnas
|
||||||
|
"list_hover": "peek" // peek (visa bara det fönstret, tona ner andra)
|
||||||
|
// | focus (fokusera vid hover) | none
|
||||||
},
|
},
|
||||||
|
|
||||||
"context_menu": ["minimize", "maximize", "move_to_output",
|
"context_menu": ["minimize", "maximize", "move_to_output",
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ curl -fSL -o "$DEST/waydock" \
|
|||||||
"$BASE/releases/download/latest/waydock-latest-linux-x64"
|
"$BASE/releases/download/latest/waydock-latest-linux-x64"
|
||||||
chmod +x "$DEST/waydock"
|
chmod +x "$DEST/waydock"
|
||||||
|
|
||||||
|
# ikon + .desktop-fil
|
||||||
|
mkdir -p "$HOME/.local/share/icons/hicolor/scalable/apps" "$HOME/.local/share/applications"
|
||||||
|
curl -fsSL -o "$HOME/.local/share/icons/hicolor/scalable/apps/waydock.svg" \
|
||||||
|
"$BASE/raw/branch/master/assets/waydock.svg" || true
|
||||||
|
curl -fsSL -o "$HOME/.local/share/applications/waydock.desktop" \
|
||||||
|
"$BASE/raw/branch/master/assets/waydock.desktop" || true
|
||||||
|
|
||||||
echo "Installerad: $DEST/waydock"
|
echo "Installerad: $DEST/waydock"
|
||||||
case ":$PATH:" in
|
case ":$PATH:" in
|
||||||
*":$DEST:"*) ;;
|
*":$DEST:"*) ;;
|
||||||
|
|||||||
@@ -28,6 +28,34 @@ pub enum Layer {
|
|||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Var längs kanten ön ligger (start = vänster/topp, end = höger/botten).
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Alignment {
|
||||||
|
Center,
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hur fönsterlistan för en grupperad app öppnas.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum GroupPopup {
|
||||||
|
Click,
|
||||||
|
Hover,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vad som händer när man hovrar en rad i fönsterlistan.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ListHover {
|
||||||
|
/// tona ner alla andra fönster så bara det hovrade syns tydligt
|
||||||
|
Peek,
|
||||||
|
/// fokusera/lyft fönstret direkt vid hover
|
||||||
|
Focus,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ClickAction {
|
pub enum ClickAction {
|
||||||
@@ -151,6 +179,8 @@ pub struct Behavior {
|
|||||||
pub group_apps: bool,
|
pub group_apps: bool,
|
||||||
pub tooltips: bool,
|
pub tooltips: bool,
|
||||||
pub urgent_pulse: bool,
|
pub urgent_pulse: bool,
|
||||||
|
pub group_popup: GroupPopup,
|
||||||
|
pub list_hover: ListHover,
|
||||||
}
|
}
|
||||||
impl Default for Behavior {
|
impl Default for Behavior {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -161,6 +191,8 @@ impl Default for Behavior {
|
|||||||
group_apps: true,
|
group_apps: true,
|
||||||
tooltips: true,
|
tooltips: true,
|
||||||
urgent_pulse: true,
|
urgent_pulse: true,
|
||||||
|
group_popup: GroupPopup::Click,
|
||||||
|
list_hover: ListHover::Peek,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,8 +300,15 @@ impl Default for LogConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub centered: bool,
|
pub alignment: Alignment,
|
||||||
|
/// false = ö som är så stor som innehållet; true = täck hela kanten
|
||||||
|
pub expand: bool,
|
||||||
|
/// avstånd från kanten baren ligger mot (px)
|
||||||
pub margin: i32,
|
pub margin: i32,
|
||||||
|
/// marginal i början av kanten: vänster (topp/botten) resp. topp (vänster/höger)
|
||||||
|
pub margin_start: i32,
|
||||||
|
/// marginal i slutet av kanten: höger resp. botten
|
||||||
|
pub margin_end: i32,
|
||||||
pub layer: Layer,
|
pub layer: Layer,
|
||||||
pub avoid_windows: bool,
|
pub avoid_windows: bool,
|
||||||
pub appearance: Appearance,
|
pub appearance: Appearance,
|
||||||
@@ -287,8 +326,11 @@ impl Default for Config {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
position: Position::Bottom,
|
position: Position::Bottom,
|
||||||
centered: true,
|
alignment: Alignment::Center,
|
||||||
|
expand: false,
|
||||||
margin: 8,
|
margin: 8,
|
||||||
|
margin_start: 0,
|
||||||
|
margin_end: 0,
|
||||||
layer: Layer::Top,
|
layer: Layer::Top,
|
||||||
avoid_windows: false,
|
avoid_windows: false,
|
||||||
appearance: Appearance::default(),
|
appearance: Appearance::default(),
|
||||||
|
|||||||
102
src/dock.rs
102
src/dock.rs
@@ -16,8 +16,8 @@ use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
|
|||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
|
|
||||||
use crate::config::{
|
use crate::config::{
|
||||||
AutohideMode, ClickAction, Layer as CfgLayer, MiddleClick, Position, ScrollAction,
|
Alignment, AutohideMode, ClickAction, GroupPopup, Layer as CfgLayer, MiddleClick,
|
||||||
SharedConfig,
|
Position, ScrollAction, SharedConfig,
|
||||||
};
|
};
|
||||||
use crate::icons::{self, AppMeta};
|
use crate::icons::{self, AppMeta};
|
||||||
use crate::menu;
|
use crate::menu;
|
||||||
@@ -45,7 +45,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 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
|
||||||
@@ -103,12 +103,28 @@ fn configure_layer(win: >k::ApplicationWindow, ctx: &Ctx, monitor: &gdk::Monit
|
|||||||
win.set_keyboard_mode(KeyboardMode::None);
|
win.set_keyboard_mode(KeyboardMode::None);
|
||||||
|
|
||||||
let edge = main_edge(&cfg.position);
|
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_anchor(edge, true);
|
||||||
win.set_margin(edge, cfg.margin);
|
win.set_margin(edge, cfg.margin);
|
||||||
if !cfg.centered {
|
if cfg.expand {
|
||||||
win.set_anchor(side_a, true);
|
// täck hela kanten; start/slut-marginalerna drar in baren
|
||||||
win.set_anchor(side_b, true);
|
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 {
|
if cfg.avoid_windows && !cfg.autohide.enabled {
|
||||||
win.auto_exclusive_zone_enable();
|
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 orient = orientation(&cfg.position);
|
||||||
let panel = gtk::Box::new(orient, 0);
|
let panel = gtk::Box::new(orient, 0);
|
||||||
panel.add_css_class("dock-panel");
|
panel.add_css_class("dock-panel");
|
||||||
panel.set_halign(gtk::Align::Center);
|
let items_align = match cfg.alignment {
|
||||||
panel.set_valign(gtk::Align::Center);
|
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
|
// yttre marginal = plats för skuggan att rita
|
||||||
let outer = gtk::Box::new(orient, 0);
|
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 {
|
let dock = Rc::new(Dock {
|
||||||
window: win.clone(),
|
window: win.clone(),
|
||||||
panel: panel.clone(),
|
items_box: items_box.clone(),
|
||||||
revealer,
|
revealer,
|
||||||
connector,
|
connector,
|
||||||
hover: Cell::new(false),
|
hover: Cell::new(false),
|
||||||
@@ -481,13 +519,13 @@ pub fn refresh_items(ctx: &Rc<Ctx>) {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
for dock in ctx.docks.borrow().iter() {
|
for dock in ctx.docks.borrow().iter() {
|
||||||
while let Some(child) = dock.panel.first_child() {
|
while let Some(child) = dock.items_box.first_child() {
|
||||||
dock.panel.remove(&child);
|
dock.items_box.remove(&child);
|
||||||
}
|
}
|
||||||
add_special_buttons(ctx, dock);
|
add_special_buttons(ctx, dock);
|
||||||
for item in build_items(ctx, &dock.connector, &snapshot) {
|
for item in build_items(ctx, &dock.connector, &snapshot) {
|
||||||
let widget = build_item_widget(ctx, &dock.connector, Rc::new(item));
|
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() {
|
if specials.overview && wayfire_ipc::available() {
|
||||||
let btn = special_button("view-grid-symbolic", "Översikt (scale)");
|
let btn = special_button("view-grid-symbolic", "Översikt (scale)");
|
||||||
btn.connect_clicked(|_| wayfire_ipc::toggle_scale());
|
btn.connect_clicked(|_| wayfire_ipc::toggle_scale());
|
||||||
dock.panel.append(&btn);
|
dock.items_box.append(&btn);
|
||||||
}
|
}
|
||||||
if specials.show_desktop {
|
if specials.show_desktop {
|
||||||
let btn = special_button("user-desktop-symbolic", "Visa skrivbordet");
|
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();
|
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);
|
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
|
// skroll: växla mellan gruppens fönster
|
||||||
let scroll = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
|
let scroll = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
|
||||||
{
|
{
|
||||||
@@ -649,8 +714,11 @@ fn primary_action(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let click = ctx.cfg.borrow().behavior.click.clone();
|
let (click, popup) = {
|
||||||
if click == ClickAction::Cycle {
|
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);
|
cycle_windows(ctx, item, true);
|
||||||
} else {
|
} else {
|
||||||
menu::popup_group_list(ctx, item, anchor);
|
menu::popup_group_list(ctx, item, anchor);
|
||||||
|
|||||||
@@ -102,10 +102,13 @@ fn init_logging(cli: &Cli, cfg: &config::Config) {
|
|||||||
/// Delar av configen som kräver att fönstren byggs om (inte bara CSS).
|
/// Delar av configen som kräver att fönstren byggs om (inte bara CSS).
|
||||||
fn layout_signature(cfg: &config::Config) -> String {
|
fn layout_signature(cfg: &config::Config) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{:?}|{}|{}|{:?}|{}|{}|{}|{:?}",
|
"{:?}|{:?}|{}|{}|{}|{}|{:?}|{}|{}|{}|{:?}",
|
||||||
cfg.position,
|
cfg.position,
|
||||||
cfg.centered,
|
cfg.alignment,
|
||||||
|
cfg.expand,
|
||||||
cfg.margin,
|
cfg.margin,
|
||||||
|
cfg.margin_start,
|
||||||
|
cfg.margin_end,
|
||||||
cfg.layer,
|
cfg.layer,
|
||||||
cfg.avoid_windows,
|
cfg.avoid_windows,
|
||||||
cfg.appearance.shadow.enabled,
|
cfg.appearance.shadow.enabled,
|
||||||
|
|||||||
108
src/menu.rs
108
src/menu.rs
@@ -11,7 +11,7 @@ use gtk4::glib;
|
|||||||
use gtk4::prelude::*;
|
use gtk4::prelude::*;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use crate::config::{self, MenuItem};
|
use crate::config::{self, GroupPopup, ListHover, MenuItem};
|
||||||
use crate::dock::{Ctx, Item};
|
use crate::dock::{Ctx, Item};
|
||||||
use crate::icons;
|
use crate::icons;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
@@ -232,12 +232,56 @@ pub fn popup_item_menu(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
|||||||
popover.popup();
|
popover.popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vänsterklick på grupp med flera fönster: lista fönstren.
|
/// Tona ner alla andra fönster än det hovrade (peek). Returnerar id:n
|
||||||
|
/// som dimmats så de kan återställas.
|
||||||
|
fn peek_apply(hovered: &crate::toplevel::ToplevelInfo) -> Vec<i64> {
|
||||||
|
let Some(views) = wayfire_ipc::list_views() else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let hovered_id = views
|
||||||
|
.iter()
|
||||||
|
.find(|v| {
|
||||||
|
v["app-id"].as_str() == Some(hovered.app_id.as_str())
|
||||||
|
&& v["title"].as_str() == Some(hovered.title.as_str())
|
||||||
|
})
|
||||||
|
.and_then(|v| v["id"].as_i64());
|
||||||
|
let mut dimmed = Vec::new();
|
||||||
|
for v in &views {
|
||||||
|
if v["role"].as_str() != Some("toplevel")
|
||||||
|
|| !v["mapped"].as_bool().unwrap_or(false)
|
||||||
|
|| v["minimized"].as_bool().unwrap_or(false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(id) = v["id"].as_i64() else { continue };
|
||||||
|
if Some(id) != hovered_id {
|
||||||
|
wayfire_ipc::set_view_alpha(id, 0.15);
|
||||||
|
dimmed.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_restore(dimmed: &[i64]) {
|
||||||
|
for id in dimmed {
|
||||||
|
wayfire_ipc::set_view_alpha(*id, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vänsterklick (eller hover) på grupp med flera fönster: lista fönstren.
|
||||||
pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
||||||
let popover = gtk::Popover::new();
|
let popover = gtk::Popover::new();
|
||||||
popover.add_css_class("waydock-menu");
|
popover.add_css_class("waydock-menu");
|
||||||
let list = gtk::Box::new(gtk::Orientation::Vertical, 2);
|
let list = gtk::Box::new(gtk::Orientation::Vertical, 2);
|
||||||
|
|
||||||
|
let (list_hover, hover_mode) = {
|
||||||
|
let b = &ctx.cfg.borrow().behavior;
|
||||||
|
(b.list_hover.clone(), b.group_popup == GroupPopup::Hover)
|
||||||
|
};
|
||||||
|
let peek_possible = list_hover == ListHover::Peek && wayfire_ipc::available();
|
||||||
|
// fönster som tonats ner av peek — återställs vid radbyte/stängning
|
||||||
|
let dimmed: Rc<std::cell::RefCell<Vec<i64>>> = Rc::new(std::cell::RefCell::new(Vec::new()));
|
||||||
|
|
||||||
for w in &item.windows {
|
for w in &item.windows {
|
||||||
let row = gtk::Button::new();
|
let row = gtk::Button::new();
|
||||||
row.add_css_class("flat");
|
row.add_css_class("flat");
|
||||||
@@ -250,16 +294,76 @@ pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: >k::Button) {
|
|||||||
let ctx2 = ctx.clone();
|
let ctx2 = ctx.clone();
|
||||||
let w2 = w.clone();
|
let w2 = w.clone();
|
||||||
let pop2 = popover.clone();
|
let pop2 = popover.clone();
|
||||||
|
let dimmed2 = dimmed.clone();
|
||||||
row.connect_clicked(move |_| {
|
row.connect_clicked(move |_| {
|
||||||
|
peek_restore(&dimmed2.borrow());
|
||||||
|
dimmed2.borrow_mut().clear();
|
||||||
if let Some(s) = ctx2.service.borrow().as_ref() {
|
if let Some(s) = ctx2.service.borrow().as_ref() {
|
||||||
s.activate(&w2);
|
s.activate(&w2);
|
||||||
}
|
}
|
||||||
pop2.popdown();
|
pop2.popdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// hover-effekt per rad: peek eller fokus
|
||||||
|
match list_hover {
|
||||||
|
ListHover::Peek if peek_possible => {
|
||||||
|
let mc = gtk::EventControllerMotion::new();
|
||||||
|
let w3 = w.clone();
|
||||||
|
let dimmed3 = dimmed.clone();
|
||||||
|
mc.connect_enter(move |_, _, _| {
|
||||||
|
peek_restore(&dimmed3.borrow());
|
||||||
|
*dimmed3.borrow_mut() = peek_apply(&w3);
|
||||||
|
});
|
||||||
|
let dimmed4 = dimmed.clone();
|
||||||
|
mc.connect_leave(move |_| {
|
||||||
|
peek_restore(&dimmed4.borrow());
|
||||||
|
dimmed4.borrow_mut().clear();
|
||||||
|
});
|
||||||
|
row.add_controller(mc);
|
||||||
|
}
|
||||||
|
ListHover::Focus => {
|
||||||
|
let mc = gtk::EventControllerMotion::new();
|
||||||
|
let ctx3 = ctx.clone();
|
||||||
|
let w3 = w.clone();
|
||||||
|
mc.connect_enter(move |_, _, _| {
|
||||||
|
if let Some(s) = ctx3.service.borrow().as_ref() {
|
||||||
|
s.activate(&w3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
row.add_controller(mc);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
list.append(&row);
|
list.append(&row);
|
||||||
}
|
}
|
||||||
popover.set_child(Some(&list));
|
popover.set_child(Some(&list));
|
||||||
popover.set_parent(anchor);
|
popover.set_parent(anchor);
|
||||||
|
|
||||||
|
// säkerhetsnät: återställ peek när popovern stängs
|
||||||
|
{
|
||||||
|
let dimmed5 = dimmed.clone();
|
||||||
|
popover.connect_closed(move |_| {
|
||||||
|
peek_restore(&dimmed5.borrow());
|
||||||
|
dimmed5.borrow_mut().clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// i hover-läge: stäng listan när pekaren lämnar den
|
||||||
|
if hover_mode {
|
||||||
|
let mc = gtk::EventControllerMotion::new();
|
||||||
|
let pop3 = popover.clone();
|
||||||
|
mc.connect_leave(move |ctl| {
|
||||||
|
let pop4 = pop3.clone();
|
||||||
|
let ctl2 = ctl.clone();
|
||||||
|
glib::timeout_add_local_once(std::time::Duration::from_millis(400), move || {
|
||||||
|
if !ctl2.contains_pointer() {
|
||||||
|
pop4.popdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
popover.add_controller(mc);
|
||||||
|
}
|
||||||
|
|
||||||
autodispose(&popover);
|
autodispose(&popover);
|
||||||
popover.popup();
|
popover.popup();
|
||||||
}
|
}
|
||||||
|
|||||||
179
src/settings.rs
179
src/settings.rs
@@ -14,14 +14,16 @@ use gtk4::prelude::*;
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use crate::config::{
|
use crate::config::{
|
||||||
self, AutohideMode, ClickAction, HotspotAction, IndicatorStyle, Layer, MenuItem,
|
self, Alignment, AutohideMode, ClickAction, GroupPopup, HotspotAction, IndicatorStyle,
|
||||||
MiddleClick, Position, ScrollAction,
|
Layer, ListHover, MenuItem, MiddleClick, Position, ScrollAction,
|
||||||
};
|
};
|
||||||
use crate::dock::Ctx;
|
use crate::dock::Ctx;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static OPEN_WINDOW: std::cell::RefCell<Option<glib::WeakRef<gtk::Window>>> =
|
static OPEN_WINDOW: std::cell::RefCell<Option<glib::WeakRef<gtk::Window>>> =
|
||||||
const { std::cell::RefCell::new(None) };
|
const { std::cell::RefCell::new(None) };
|
||||||
|
static ILLUSTRATION: std::cell::RefCell<Option<glib::WeakRef<gtk::DrawingArea>>> =
|
||||||
|
const { std::cell::RefCell::new(None) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uppdatera config + applicera + spara (debouncat 300 ms).
|
/// Uppdatera config + applicera + spara (debouncat 300 ms).
|
||||||
@@ -30,6 +32,11 @@ fn commit(ctx: &Rc<Ctx>, mutate: impl FnOnce(&mut config::Config)) {
|
|||||||
if let Some(cb) = ctx.on_config_changed.borrow().as_ref() {
|
if let Some(cb) = ctx.on_config_changed.borrow().as_ref() {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
ILLUSTRATION.with(|i| {
|
||||||
|
if let Some(da) = i.borrow().as_ref().and_then(|w| w.upgrade()) {
|
||||||
|
da.queue_draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
let generation = ctx.save_generation.get() + 1;
|
let generation = ctx.save_generation.get() + 1;
|
||||||
ctx.save_generation.set(generation);
|
ctx.save_generation.set(generation);
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
@@ -253,6 +260,125 @@ fn build_appearance_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
|||||||
scroll
|
scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn margin_illustration(ctx: &Rc<Ctx>) -> gtk::DrawingArea {
|
||||||
|
let da = gtk::DrawingArea::new();
|
||||||
|
da.set_content_width(280);
|
||||||
|
da.set_content_height(190);
|
||||||
|
da.set_halign(gtk::Align::Center);
|
||||||
|
let ctx2 = ctx.clone();
|
||||||
|
da.set_draw_func(move |_, cr, w, h| {
|
||||||
|
let cfg = ctx2.cfg.borrow();
|
||||||
|
let (w, h) = (w as f64, h as f64);
|
||||||
|
|
||||||
|
// "skärmen"
|
||||||
|
let (sx, sy, sw, sh) = (30.0, 22.0, w - 60.0, h - 60.0);
|
||||||
|
cr.set_source_rgba(0.16, 0.16, 0.18, 1.0);
|
||||||
|
cr.rectangle(sx, sy, sw, sh);
|
||||||
|
let _ = cr.fill();
|
||||||
|
cr.set_source_rgba(0.5, 0.5, 0.55, 1.0);
|
||||||
|
cr.set_line_width(1.5);
|
||||||
|
cr.rectangle(sx, sy, sw, sh);
|
||||||
|
let _ = cr.stroke();
|
||||||
|
|
||||||
|
// förhållandena skalade: marginalerna visas symboliskt (max ~18 px)
|
||||||
|
let clamp = |v: i32| 6.0 + (v.max(0) as f64).min(60.0) * 0.2;
|
||||||
|
let a = clamp(cfg.margin);
|
||||||
|
let b = clamp(cfg.margin_start);
|
||||||
|
let c = clamp(cfg.margin_end);
|
||||||
|
let thick = 12.0;
|
||||||
|
let horizontal = matches!(cfg.position, Position::Top | Position::Bottom);
|
||||||
|
|
||||||
|
// barens längd/placering längs kanten
|
||||||
|
let full = if horizontal { sw } else { sh };
|
||||||
|
let len = if cfg.expand {
|
||||||
|
full - b - c
|
||||||
|
} else {
|
||||||
|
(full * 0.45).max(40.0)
|
||||||
|
};
|
||||||
|
let along = if cfg.expand {
|
||||||
|
b
|
||||||
|
} else {
|
||||||
|
match cfg.alignment {
|
||||||
|
Alignment::Start => b,
|
||||||
|
Alignment::End => full - len - c,
|
||||||
|
Alignment::Center => (full - len) / 2.0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// accentfärgad bar
|
||||||
|
let acc = &cfg.appearance.accent;
|
||||||
|
let hex = acc.trim_start_matches('#');
|
||||||
|
let (r, g, bl) = if hex.len() == 6 {
|
||||||
|
(
|
||||||
|
u8::from_str_radix(&hex[0..2], 16).unwrap_or(200) as f64 / 255.0,
|
||||||
|
u8::from_str_radix(&hex[2..4], 16).unwrap_or(85) as f64 / 255.0,
|
||||||
|
u8::from_str_radix(&hex[4..6], 16).unwrap_or(95) as f64 / 255.0,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(0.79, 0.33, 0.36)
|
||||||
|
};
|
||||||
|
cr.set_source_rgba(r, g, bl, 0.95);
|
||||||
|
let (bx, by, bw, bh) = match cfg.position {
|
||||||
|
Position::Bottom => (sx + along, sy + sh - a - thick, len, thick),
|
||||||
|
Position::Top => (sx + along, sy + a, len, thick),
|
||||||
|
Position::Left => (sx + a, sy + along, thick, len),
|
||||||
|
Position::Right => (sx + sw - a - thick, sy + along, thick, len),
|
||||||
|
};
|
||||||
|
cr.rectangle(bx, by, bw, bh);
|
||||||
|
let _ = cr.fill();
|
||||||
|
|
||||||
|
// måttpilar med etiketter
|
||||||
|
cr.set_source_rgba(0.85, 0.85, 0.88, 1.0);
|
||||||
|
cr.set_line_width(1.0);
|
||||||
|
cr.set_font_size(11.0);
|
||||||
|
let label = |x: f64, y: f64, text: &str| {
|
||||||
|
cr.move_to(x, y);
|
||||||
|
let _ = cr.show_text(text);
|
||||||
|
};
|
||||||
|
let line = |x1: f64, y1: f64, x2: f64, y2: f64| {
|
||||||
|
cr.move_to(x1, y1);
|
||||||
|
cr.line_to(x2, y2);
|
||||||
|
let _ = cr.stroke();
|
||||||
|
};
|
||||||
|
match cfg.position {
|
||||||
|
Position::Bottom => {
|
||||||
|
line(bx + bw / 2.0, by + thick, bx + bw / 2.0, sy + sh);
|
||||||
|
label(bx + bw / 2.0 + 3.0, sy + sh - 2.0, "A");
|
||||||
|
line(sx, by + thick / 2.0, bx, by + thick / 2.0);
|
||||||
|
label(sx + (bx - sx) / 2.0 - 3.0, by - 3.0, "B");
|
||||||
|
line(bx + bw, by + thick / 2.0, sx + sw, by + thick / 2.0);
|
||||||
|
label(bx + bw + (sx + sw - bx - bw) / 2.0 - 3.0, by - 3.0, "C");
|
||||||
|
}
|
||||||
|
Position::Top => {
|
||||||
|
line(bx + bw / 2.0, sy, bx + bw / 2.0, by);
|
||||||
|
label(bx + bw / 2.0 + 3.0, sy + 12.0, "A");
|
||||||
|
line(sx, by + thick / 2.0, bx, by + thick / 2.0);
|
||||||
|
label(sx + (bx - sx) / 2.0 - 3.0, by + thick + 12.0, "B");
|
||||||
|
line(bx + bw, by + thick / 2.0, sx + sw, by + thick / 2.0);
|
||||||
|
label(bx + bw + (sx + sw - bx - bw) / 2.0 - 3.0, by + thick + 12.0, "C");
|
||||||
|
}
|
||||||
|
Position::Left => {
|
||||||
|
line(sx, by + bh / 2.0, bx, by + bh / 2.0);
|
||||||
|
label(sx + 2.0, by + bh / 2.0 - 4.0, "A");
|
||||||
|
line(bx + thick / 2.0, sy, bx + thick / 2.0, by);
|
||||||
|
label(bx + thick + 3.0, sy + (by - sy) / 2.0, "B");
|
||||||
|
line(bx + thick / 2.0, by + bh, bx + thick / 2.0, sy + sh);
|
||||||
|
label(bx + thick + 3.0, by + bh + (sy + sh - by - bh) / 2.0, "C");
|
||||||
|
}
|
||||||
|
Position::Right => {
|
||||||
|
line(bx + thick, by + bh / 2.0, sx + sw, by + bh / 2.0);
|
||||||
|
label(sx + sw - 12.0, by + bh / 2.0 - 4.0, "A");
|
||||||
|
line(bx + thick / 2.0, sy, bx + thick / 2.0, by);
|
||||||
|
label(bx - 12.0, sy + (by - sy) / 2.0, "B");
|
||||||
|
line(bx + thick / 2.0, by + bh, bx + thick / 2.0, sy + sh);
|
||||||
|
label(bx - 12.0, by + bh + (sy + sh - by - bh) / 2.0, "C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ILLUSTRATION.with(|i| *i.borrow_mut() = Some(da.downgrade()));
|
||||||
|
da
|
||||||
|
}
|
||||||
|
|
||||||
fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
||||||
let (scroll, list) = page();
|
let (scroll, list) = page();
|
||||||
let cfg = ctx.cfg.borrow().clone();
|
let cfg = ctx.cfg.borrow().clone();
|
||||||
@@ -272,8 +398,19 @@ fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
|||||||
_ => Position::Bottom,
|
_ => Position::Bottom,
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
row(&list, "Centrerad ö", &switch(ctx, cfg.centered, |c, v| c.centered = v));
|
let align_idx = match cfg.alignment {
|
||||||
row(&list, "Marginal från kant", &spin(ctx, cfg.margin as f64, (0.0, 64.0), 1.0, |c, v| c.margin = v as i32));
|
Alignment::Center => 0,
|
||||||
|
Alignment::Start => 1,
|
||||||
|
Alignment::End => 2,
|
||||||
|
};
|
||||||
|
row(&list, "Justering längs kanten", &dropdown(ctx, &["Centrerad", "Start (vänster/topp)", "Slut (höger/botten)"], align_idx, |c, i| {
|
||||||
|
c.alignment = match i {
|
||||||
|
1 => Alignment::Start,
|
||||||
|
2 => Alignment::End,
|
||||||
|
_ => Alignment::Center,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
row(&list, "Täck hela kanten", &switch(ctx, cfg.expand, |c, v| c.expand = v));
|
||||||
let layer_idx = match cfg.layer {
|
let layer_idx = match cfg.layer {
|
||||||
Layer::Top => 0,
|
Layer::Top => 0,
|
||||||
Layer::Overlay => 1,
|
Layer::Overlay => 1,
|
||||||
@@ -288,6 +425,18 @@ fn build_layout_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
|||||||
}));
|
}));
|
||||||
row(&list, "Reservera plats (undvik fönster)", &switch(ctx, cfg.avoid_windows, |c, v| c.avoid_windows = v));
|
row(&list, "Reservera plats (undvik fönster)", &switch(ctx, cfg.avoid_windows, |c, v| c.avoid_windows = v));
|
||||||
|
|
||||||
|
heading(&list, "Marginaler");
|
||||||
|
list.append(&margin_illustration(ctx));
|
||||||
|
row(&list, "A — Avstånd från kanten", &spin(ctx, cfg.margin as f64, (0.0, 128.0), 1.0, |c, v| c.margin = v as i32));
|
||||||
|
row(&list, "B — Marginal start", &spin(ctx, cfg.margin_start as f64, (0.0, 512.0), 1.0, |c, v| c.margin_start = v as i32));
|
||||||
|
row(&list, "C — Marginal slut", &spin(ctx, cfg.margin_end as f64, (0.0, 512.0), 1.0, |c, v| c.margin_end = v as i32));
|
||||||
|
let note = gtk::Label::new(Some(
|
||||||
|
"A = avstånd från kanten baren ligger mot.\nB/C = marginal i början/slutet av kanten — indrag när baren\ntäcker kanten, annars avstånd från vald sida (vid Start/Slut).",
|
||||||
|
));
|
||||||
|
note.add_css_class("dim-label");
|
||||||
|
note.set_halign(gtk::Align::Start);
|
||||||
|
list.append(¬e);
|
||||||
|
|
||||||
heading(&list, "Autohide");
|
heading(&list, "Autohide");
|
||||||
row(&list, "Aktiverad", &switch(ctx, cfg.autohide.enabled, |c, v| c.autohide.enabled = v));
|
row(&list, "Aktiverad", &switch(ctx, cfg.autohide.enabled, |c, v| c.autohide.enabled = v));
|
||||||
let mode_idx = match cfg.autohide.mode {
|
let mode_idx = match cfg.autohide.mode {
|
||||||
@@ -348,8 +497,27 @@ fn build_behavior_page(ctx: &Rc<Ctx>) -> gtk::ScrolledWindow {
|
|||||||
c.behavior.scroll = if i == 1 { ScrollAction::None } else { ScrollAction::CycleWindows };
|
c.behavior.scroll = if i == 1 { ScrollAction::None } else { ScrollAction::CycleWindows };
|
||||||
}));
|
}));
|
||||||
|
|
||||||
heading(&list, "Övrigt");
|
heading(&list, "Gruppering & popup");
|
||||||
row(&list, "Gruppera appar", &switch(ctx, b.group_apps, |c, v| c.behavior.group_apps = v));
|
row(&list, "Gruppera appar", &switch(ctx, b.group_apps, |c, v| c.behavior.group_apps = v));
|
||||||
|
let popup_idx = match b.group_popup {
|
||||||
|
GroupPopup::Click => 0,
|
||||||
|
GroupPopup::Hover => 1,
|
||||||
|
};
|
||||||
|
row(&list, "Fönsterlistan öppnas", &dropdown(ctx, &["Vid klick", "Vid hover"], popup_idx, |c, i| {
|
||||||
|
c.behavior.group_popup = if i == 1 { GroupPopup::Hover } else { GroupPopup::Click };
|
||||||
|
}));
|
||||||
|
let lh_idx = match b.list_hover {
|
||||||
|
ListHover::Peek => 0,
|
||||||
|
ListHover::Focus => 1,
|
||||||
|
ListHover::None => 2,
|
||||||
|
};
|
||||||
|
row(&list, "Hover i fönsterlistan", &dropdown(ctx, &["Peek — visa bara det fönstret", "Fokusera fönstret", "Ingen effekt"], lh_idx, |c, i| {
|
||||||
|
c.behavior.list_hover = match i {
|
||||||
|
1 => ListHover::Focus,
|
||||||
|
2 => ListHover::None,
|
||||||
|
_ => ListHover::Peek,
|
||||||
|
};
|
||||||
|
}));
|
||||||
row(&list, "Tooltips", &switch(ctx, b.tooltips, |c, v| c.behavior.tooltips = v));
|
row(&list, "Tooltips", &switch(ctx, b.tooltips, |c, v| c.behavior.tooltips = v));
|
||||||
row(&list, "Urgency-puls", &switch(ctx, b.urgent_pulse, |c, v| c.behavior.urgent_pulse = v));
|
row(&list, "Urgency-puls", &switch(ctx, b.urgent_pulse, |c, v| c.behavior.urgent_pulse = v));
|
||||||
|
|
||||||
@@ -510,6 +678,7 @@ pub fn open(ctx: &Rc<Ctx>) {
|
|||||||
|
|
||||||
let win = gtk::Window::new();
|
let win = gtk::Window::new();
|
||||||
win.set_title(Some("waydock — inställningar"));
|
win.set_title(Some("waydock — inställningar"));
|
||||||
|
win.set_icon_name(Some("waydock"));
|
||||||
win.set_default_size(520, 620);
|
win.set_default_size(520, 620);
|
||||||
|
|
||||||
let notebook = gtk::Notebook::new();
|
let notebook = gtk::Notebook::new();
|
||||||
|
|||||||
@@ -178,6 +178,14 @@ pub fn subscribe(on_event: std::rc::Rc<dyn Fn(&Value)>) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sätt en vys genomskinlighet (kräver alpha-pluginen).
|
||||||
|
pub fn set_view_alpha(view_id: i64, alpha: f64) {
|
||||||
|
call_logged(
|
||||||
|
"wf/alpha/set-view-alpha",
|
||||||
|
json!({ "view-id": view_id, "alpha": alpha }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Flytta en vy till en annan output (namn t.ex. "DP-1").
|
/// Flytta en vy till en annan output (namn t.ex. "DP-1").
|
||||||
pub fn move_view_to_output(view_id: i64, output_name: &str) {
|
pub fn move_view_to_output(view_id: i64, output_name: &str) {
|
||||||
let Some(outputs) = list_outputs() else {
|
let Some(outputs) = list_outputs() else {
|
||||||
|
|||||||
Reference in New Issue
Block a user