better config + notifpopups max popups
Use a config file
This commit is contained in:
parent
ecf4916687
commit
c2e03775db
4 changed files with 44 additions and 45 deletions
21
config.ts
Normal file
21
config.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
export const bar = {
|
||||||
|
wsPerGroup: 10,
|
||||||
|
dateTimeFormat: "%d/%m/%y %R",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const launcher = {
|
||||||
|
maxResults: 15,
|
||||||
|
fdOpts: ["-a", "-t", "f"],
|
||||||
|
pins: [
|
||||||
|
["firefox", "waterfox", "google-chrome", "chromium", "brave-browser", "vivaldi-stable", "vivaldi-snapshot"],
|
||||||
|
["foot", "alacritty", "kitty", "wezterm"],
|
||||||
|
["thunar", "nemo", "nautilus"],
|
||||||
|
["codium", "code", "clion", "intellij-idea-ultimate-edition"],
|
||||||
|
["spotify-adblock", "spotify", "audacious", "elisa"],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const notifpopups = {
|
||||||
|
maxPopups: -1,
|
||||||
|
expire: false,
|
||||||
|
};
|
||||||
|
|
@ -4,6 +4,7 @@ import { App, Astal, astalify, Gdk, Gtk, type ConstructProps } from "astal/gtk3"
|
||||||
import AstalHyprland from "gi://AstalHyprland";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import AstalTray from "gi://AstalTray";
|
import AstalTray from "gi://AstalTray";
|
||||||
|
import { bar as config } from "../config";
|
||||||
import Players from "../services/players";
|
import Players from "../services/players";
|
||||||
import { getAppCategoryIcon } from "../utils/icons";
|
import { getAppCategoryIcon } from "../utils/icons";
|
||||||
import { ellipsize } from "../utils/strings";
|
import { ellipsize } from "../utils/strings";
|
||||||
|
|
@ -12,8 +13,6 @@ import { setupCustomTooltip } from "../utils/widgets";
|
||||||
|
|
||||||
const hyprland = AstalHyprland.get_default();
|
const hyprland = AstalHyprland.get_default();
|
||||||
|
|
||||||
const wsPerGroup = 10;
|
|
||||||
|
|
||||||
const hookFocusedClientProp = (
|
const hookFocusedClientProp = (
|
||||||
self: any, // Ugh why is there no base Widget type
|
self: any, // Ugh why is there no base Widget type
|
||||||
prop: keyof AstalHyprland.Client,
|
prop: keyof AstalHyprland.Client,
|
||||||
|
|
@ -99,7 +98,7 @@ const MediaPlaying = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Workspace = ({ idx }: { idx: number }) => {
|
const Workspace = ({ idx }: { idx: number }) => {
|
||||||
let wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / wsPerGroup) * wsPerGroup + idx;
|
let wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / config.wsPerGroup) * config.wsPerGroup + idx;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
halign={Gtk.Align.CENTER}
|
halign={Gtk.Align.CENTER}
|
||||||
|
|
@ -115,7 +114,7 @@ const Workspace = ({ idx }: { idx: number }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.hook(hyprland, "notify::focused-workspace", () => {
|
self.hook(hyprland, "notify::focused-workspace", () => {
|
||||||
wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / wsPerGroup) * wsPerGroup + idx;
|
wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / config.wsPerGroup) * config.wsPerGroup + idx;
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
self.hook(hyprland, "client-added", update);
|
self.hook(hyprland, "client-added", update);
|
||||||
|
|
@ -138,7 +137,7 @@ const Workspaces = () => (
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<box className="module workspaces">
|
<box className="module workspaces">
|
||||||
{Array.from({ length: wsPerGroup }).map((_, idx) => (
|
{Array.from({ length: config.wsPerGroup }).map((_, idx) => (
|
||||||
<Workspace idx={idx + 1} /> // Start from 1
|
<Workspace idx={idx + 1} /> // Start from 1
|
||||||
))}
|
))}
|
||||||
</box>
|
</box>
|
||||||
|
|
@ -237,7 +236,8 @@ const DateTime = () => (
|
||||||
<label
|
<label
|
||||||
setup={self => {
|
setup={self => {
|
||||||
const pollVar = Variable(null).poll(5000, () => {
|
const pollVar = Variable(null).poll(5000, () => {
|
||||||
self.label = GLib.DateTime.new_now_local().format("%d/%m/%y %R") ?? new Date().toLocaleString();
|
self.label =
|
||||||
|
GLib.DateTime.new_now_local().format(config.dateTimeFormat) ?? new Date().toLocaleString();
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
self.connect("destroy", () => pollVar.drop());
|
self.connect("destroy", () => pollVar.drop());
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import fuzzysort from "fuzzysort";
|
import fuzzysort from "fuzzysort";
|
||||||
import type AstalApps from "gi://AstalApps";
|
import type AstalApps from "gi://AstalApps";
|
||||||
import AstalHyprland from "gi://AstalHyprland";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
import { launcher as config } from "../config";
|
||||||
import { Apps } from "../services/apps";
|
import { Apps } from "../services/apps";
|
||||||
import Math, { type HistoryItem } from "../services/math";
|
import Math, { type HistoryItem } from "../services/math";
|
||||||
import { HOME } from "../utils/constants";
|
import { HOME } from "../utils/constants";
|
||||||
|
|
@ -19,23 +20,6 @@ interface Subcommand {
|
||||||
command: (...args: string[]) => void;
|
command: (...args: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxSearchResults = 15;
|
|
||||||
const fdOptions = ["-a", "-t", "f", "-E", ".git"];
|
|
||||||
|
|
||||||
const browser = [
|
|
||||||
"firefox",
|
|
||||||
"waterfox",
|
|
||||||
"google-chrome",
|
|
||||||
"chromium",
|
|
||||||
"brave-browser",
|
|
||||||
"vivaldi-stable",
|
|
||||||
"vivaldi-snapshot",
|
|
||||||
];
|
|
||||||
const terminal = ["foot", "alacritty", "kitty", "wezterm"];
|
|
||||||
const files = ["thunar", "nemo", "nautilus"];
|
|
||||||
const ide = ["codium", "code", "clion", "intellij-idea-ultimate-edition"];
|
|
||||||
const music = ["spotify-adblock", "spotify", "audacious", "elisa"];
|
|
||||||
|
|
||||||
const getIconFromMode = (mode: Mode) => {
|
const getIconFromMode = (mode: Mode) => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "apps":
|
case "apps":
|
||||||
|
|
@ -77,7 +61,7 @@ const openFileAndClose = (self: JSX.Element, path: string) => {
|
||||||
]).catch(console.error);
|
]).catch(console.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PinnedApp = ({ names }: { names: string[] }) => {
|
const PinnedApp = (names: string[]) => {
|
||||||
let app: Gio.DesktopAppInfo | null = null;
|
let app: Gio.DesktopAppInfo | null = null;
|
||||||
let astalApp: AstalApps.Application | undefined;
|
let astalApp: AstalApps.Application | undefined;
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
|
|
@ -103,15 +87,7 @@ const PinnedApp = ({ names }: { names: string[] }) => {
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PinnedApps = () => (
|
const PinnedApps = () => <box homogeneous>{config.pins.map(PinnedApp)}</box>;
|
||||||
<box homogeneous>
|
|
||||||
<PinnedApp names={browser} />
|
|
||||||
<PinnedApp names={terminal} />
|
|
||||||
<PinnedApp names={files} />
|
|
||||||
<PinnedApp names={ide} />
|
|
||||||
<PinnedApp names={music} />
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const SearchEntry = ({ entry }: { entry: Widget.Entry }) => (
|
const SearchEntry = ({ entry }: { entry: Widget.Entry }) => (
|
||||||
<stack
|
<stack
|
||||||
|
|
@ -275,7 +251,7 @@ const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> })
|
||||||
|
|
||||||
const appSearch = () => {
|
const appSearch = () => {
|
||||||
const apps = Apps.fuzzy_query(entry.text);
|
const apps = Apps.fuzzy_query(entry.text);
|
||||||
if (apps.length > maxSearchResults) apps.length = maxSearchResults;
|
if (apps.length > config.maxResults) apps.length = config.maxResults;
|
||||||
for (const app of apps) self.add(<AppResult app={app} />);
|
for (const app of apps) self.add(<AppResult app={app} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -289,10 +265,10 @@ const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> })
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileSearch = () =>
|
const fileSearch = () =>
|
||||||
execAsync(["fd", ...fdOptions, entry.text, HOME])
|
execAsync(["fd", ...config.fdOpts, entry.text, HOME])
|
||||||
.then(out => {
|
.then(out => {
|
||||||
const paths = out.split("\n").filter(path => path);
|
const paths = out.split("\n").filter(path => path);
|
||||||
if (paths.length > maxSearchResults) paths.length = maxSearchResults;
|
if (paths.length > config.maxResults) paths.length = config.maxResults;
|
||||||
self.foreach(ch => ch.destroy());
|
self.foreach(ch => ch.destroy());
|
||||||
for (const path of paths) self.add(<FileResult path={path} />);
|
for (const path of paths) self.add(<FileResult path={path} />);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { GLib, register, timeout } from "astal";
|
import { GLib, register, timeout } from "astal";
|
||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
|
import { notifpopups as config } from "../config";
|
||||||
import { desktopEntrySubs } from "../utils/icons";
|
import { desktopEntrySubs } from "../utils/icons";
|
||||||
import { setupChildClickthrough } from "../utils/widgets";
|
import { setupChildClickthrough } from "../utils/widgets";
|
||||||
|
|
||||||
|
|
@ -104,8 +105,8 @@ class NotifPopup extends Widget.Box {
|
||||||
this.css = `transition: 300ms cubic-bezier(0.05, 0.9, 0.1, 1.1); margin-left: 0; margin-right: 0;`;
|
this.css = `transition: 300ms cubic-bezier(0.05, 0.9, 0.1, 1.1); margin-left: 0; margin-right: 0;`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close popup after timeout if transient
|
// Close popup after timeout if transient or expire enabled in config
|
||||||
if (notification.transient)
|
if (config.expire || notification.transient)
|
||||||
timeout(
|
timeout(
|
||||||
notification.expireTimeout > 0
|
notification.expireTimeout > 0
|
||||||
? notification.expireTimeout
|
? notification.expireTimeout
|
||||||
|
|
@ -145,8 +146,11 @@ export default () => (
|
||||||
const map = new Map<number, NotifPopup>();
|
const map = new Map<number, NotifPopup>();
|
||||||
self.hook(notifd, "notified", (self, id) => {
|
self.hook(notifd, "notified", (self, id) => {
|
||||||
const notification = notifd.get_notification(id);
|
const notification = notifd.get_notification(id);
|
||||||
|
|
||||||
const popup = (<NotifPopup notification={notification} />) as NotifPopup;
|
const popup = (<NotifPopup notification={notification} />) as NotifPopup;
|
||||||
|
popup.connect("destroy", () => map.delete(notification.id));
|
||||||
map.set(notification.id, popup);
|
map.set(notification.id, popup);
|
||||||
|
|
||||||
self.add(
|
self.add(
|
||||||
<eventbox
|
<eventbox
|
||||||
// Dismiss on middle click
|
// Dismiss on middle click
|
||||||
|
|
@ -157,14 +161,12 @@ export default () => (
|
||||||
{popup}
|
{popup}
|
||||||
</eventbox>
|
</eventbox>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Limit number of popups
|
||||||
|
if (config.maxPopups > 0 && self.children.length > config.maxPopups)
|
||||||
|
map.values().next().value?.destroyWithAnims();
|
||||||
});
|
});
|
||||||
self.hook(notifd, "resolved", (_, id) => {
|
self.hook(notifd, "resolved", (_, id) => map.get(id)?.destroyWithAnims());
|
||||||
const popup = map.get(id);
|
|
||||||
if (popup) {
|
|
||||||
popup.destroyWithAnims();
|
|
||||||
map.delete(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Change input region to child region so can click through empty space
|
// Change input region to child region so can click through empty space
|
||||||
setupChildClickthrough(self);
|
setupChildClickthrough(self);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue