-- ─────────────────────────────────────────────────────────────────────────────
-- Window, workspace and layer rules
--
-- Rules evaluate top to bottom, last match wins -- but every NAMED rule is
-- evaluated before every anonymous one, so a named rule can never override an
-- anonymous rule further down. Names are used sparingly here for that reason.
--
-- Matching is Google RE2 (no backtracking). Prefix a pattern with "negative:"
-- to invert it.
-- ─────────────────────────────────────────────────────────────────────────────

-- ── Upstream sanity rules ───────────────────────────────────────────────────
hl.window_rule({
    name  = "suppress-maximize-events",
    match = { class = ".*" },
    suppress_event = "maximize",
})

hl.window_rule({
    name  = "fix-xwayland-drags",
    match = {
        class      = "^$",
        title      = "^$",
        xwayland   = true,
        float      = true,
        fullscreen = false,
        pin        = false,
    },
    no_focus = true,
})

-- ── Floating apps ───────────────────────────────────────────────────────────
-- Carried over from the Forge windows.json override list, minus entries for
-- apps that are no longer installed.
hl.window_rule({
    match = {
        class = "^(jetbrains-toolbox|zoom|Cider|Hidamari|com\\.mattjakeman\\.ExtensionManager)$",
    },
    float = true,
})

hl.window_rule({
    match = { class = "^([Bb]itwarden|com\\.bitwarden\\.desktop)$" },
    float = true,
})

hl.window_rule({
    match = { class = "^(com\\.nextcloud\\.desktopclient\\.nextcloud|Nextcloud)$" },
    float = true,
})

hl.window_rule({
    match = { class = "^(org\\.gnome\\.Calculator|gnome-calculator)$" },
    float = true,
    size  = { 400, 600 },
    center = true,
})

hl.window_rule({
    match = { class = "^(mpv|io\\.mpv\\.Mpv)$" },
    float = true,
    -- Video should never be blurred or dimmed behind another window.
    no_blur = true,
    no_dim  = true,
})

hl.window_rule({
    match = { class = "^(com\\.spotify\\.Client|Spotify)$" },
    float = true,
})

-- Settings-style utility windows and pickers: float and center, like GNOME did.
hl.window_rule({
    match = { class = "^(pavucontrol|org\\.pulseaudio\\.pavucontrol|nm-connection-editor|blueman-manager|org\\.gnome\\.Settings)$" },
    float  = true,
    size   = { "monitor_w * 0.45", "monitor_h * 0.6" },
    center = true,
})

-- Quick Look. The GNOME previewer is what the file manager opens on space,
-- and it is an overlay rather than a window someone manages: tiled, it shoves
-- the file manager aside and has to be dismissed before the list is usable
-- again. Sized generously because a preview that needs zooming is not a
-- preview; it still gets a margin so the file underneath stays visible.
hl.window_rule({
    match  = { class = "^org\\.gnome\\.NautilusPreviewer$" },
    float  = true,
    size   = { "monitor_w * 0.7", "monitor_h * 0.8" },
    center = true,
})

-- Portal dialogs (file chooser, screen share picker) should always float.
hl.window_rule({
    match = { class = "^(xdg-desktop-portal-gtk|org\\.freedesktop\\.impl\\.portal\\.desktop\\.gtk|hyprland-share-picker)$" },
    float  = true,
    center = true,
})

hl.window_rule({
    match = { title = "^(Open File|Save File|Save As|Open Folder|Select a File|Choose Files)$" },
    float  = true,
    center = true,
})

-- Polkit prompt.
hl.window_rule({
    match  = { class = "^(hyprpolkitagent|polkit-gnome-authentication-agent-1)$" },
    float  = true,
    center = true,
})

-- ── Gaming ──────────────────────────────────────────────────────────────────
-- content = "game" is the keystone: misc.vrr = 3, render.direct_scanout = 2 and
-- cursor.no_break_fs_vrr = 2 all key off it. Blur, animation, shadow and dim are
-- all disabled so the compositor gets out of the way entirely.
hl.window_rule({
    match = { class = "^(steam_app_\\d+|gamescope|lutris|net\\.lutris\\.Lutris|com\\.heroicgameslauncher\\.hgl|Minecraft.*|moonlight|com\\.moonlight_stream\\.Moonlight)$" },
    content   = "game",
    immediate = true,
    no_blur   = true,
    no_anim   = true,
    no_shadow = true,
    no_dim    = true,
})

-- Steam itself is a normal window, but its transient popups are a mess.
hl.window_rule({
    match = { class = "^steam$", title = "^(Friends List|Steam Settings|Special Offer.*)$" },
    float = true,
})

-- Fullscreen video in a browser also benefits from no blur / no dim.
hl.window_rule({
    match   = { fullscreen = true },
    no_blur = true,
    no_dim  = true,
})

-- ── Workspace rules ─────────────────────────────────────────────────────────
-- Deliberately NO "smart gaps".
--
-- The usual trick is to strip gaps, border and rounding when a workspace holds
-- a single tiled window (`workspace = "w[tv1]"` + matching window rules). It
-- was here and it was wrong for this setup: the common case is one window on a
-- workspace, so the desktop spent most of its time square-cornered and
-- edge-to-edge -- the opposite of the intended look. Gaps and rounding are
-- constant now, however many windows are open.

-- ── Layer rules (Quickshell surfaces) ───────────────────────────────────────
-- Namespaces are set in QML via WlrLayershell.namespace; keep these regexes and
-- those strings in sync. `hyprctl layers` lists what is actually live.
--
-- The bar intentionally has no rule: it is a transparent edge-to-edge input
-- surface, not glass. Its individual controls paint their own hover feedback.
-- Keeping a full-width blur rule here would spend compositor work on pixels
-- the bar does not draw.

hl.layer_rule({
    name  = "qs-dock",
    match = { namespace = "^qs-dock$" },
    blur         = true,
    -- The dock's context menu and its window previews are xdg-popups of this
    -- surface, not layers of their own, so they are only blurred if the rule
    -- says to blur the popups too. Without it they are flat panes over the
    -- wallpaper while everything else on the desktop is glass.
    blur_popups  = true,
    ignore_alpha = 0.3,
})

-- Popovers: calendar, quick settings, notification center, tray menus.
hl.layer_rule({
    name  = "qs-popover",
    match = { namespace = "^qs-popover" },
    blur         = true,
    blur_popups  = true,
    ignore_alpha = 0.2,
})

-- Overview, capture and local screen-reading UI dim the desktop behind them.
hl.layer_rule({
    name  = "qs-overlay",
    match = { namespace = "^qs-(overview|capture|screen-intelligence)$" },
    blur         = true,
    ignore_alpha = 0.4,
    dim_around   = true,
    no_screen_share = true,
})

-- Notification toasts. Blurred like every other shell surface -- without this
-- the cards are a near-transparent fill sitting directly on the wallpaper and
-- read as washed out rather than as glass.
--
-- no_anim because the toasts animate themselves in QML, and no_screen_share so
-- they don't leak into OBS or Sunshine streams.
hl.layer_rule({
    name  = "qs-notifications",
    match = { namespace = "^qs-notifications$" },
    blur             = true,
    ignore_alpha     = 0.15,
    no_anim          = true,
    no_screen_share  = true,
})

-- Signal Glass provides its own event-driven motion, so the compositor only
-- supplies the material blur and privacy behavior.
hl.layer_rule({
    name  = "qs-signal-glass",
    match = { namespace = "^qs-signal-glass$" },
    blur             = true,
    ignore_alpha     = 0.15,
    no_anim          = true,
    no_screen_share  = true,
})

-- The launcher blurs and dims whatever is behind it.
hl.layer_rule({
    name  = "launcher",
    match = { namespace = "^(vicinae|wofi)$" },
    blur         = true,
    ignore_alpha = 0.5,
    dim_around   = true,
})

return true
