mirror of
https://github.com/Theaninova/TheaninovOS.git
synced 2026-01-05 23:32:48 +00:00
feat: update ags
This commit is contained in:
326
home/desktops/hyprland/ags/js/settings/SettingsDialog.js
Normal file
326
home/desktops/hyprland/ags/js/settings/SettingsDialog.js
Normal file
@@ -0,0 +1,326 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import RegularWindow from "../misc/RegularWindow.js";
|
||||
import Variable from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import icons from "../icons.js";
|
||||
import { getOptions, getValues } from "./option.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const optionsList = getOptions();
|
||||
const categories = Array.from(
|
||||
new Set(optionsList.map((opt) => opt.category)),
|
||||
).filter((category) => category !== "exclude");
|
||||
|
||||
const currentPage = Variable(categories[0]);
|
||||
const search = Variable("");
|
||||
const showSearch = Variable(false);
|
||||
showSearch.connect("changed", ({ value }) => {
|
||||
if (!value) search.value = "";
|
||||
});
|
||||
|
||||
/** @param {import('./option.js').Opt<string>} opt */
|
||||
const EnumSetter = (opt) => {
|
||||
const lbl = Widget.Label({ binds: [["label", opt]] });
|
||||
const step = (dir = 1) => {
|
||||
const i = opt.enums.findIndex((i) => i === lbl.label);
|
||||
opt.setValue(
|
||||
dir > 0
|
||||
? i + dir > opt.enums.length - 1
|
||||
? opt.enums[0]
|
||||
: opt.enums[i + dir]
|
||||
: i + dir < 0
|
||||
? opt.enums[opt.enums.length - 1]
|
||||
: opt.enums[i + dir],
|
||||
true,
|
||||
);
|
||||
};
|
||||
const next = Widget.Button({
|
||||
child: Widget.Icon(icons.ui.arrow.right),
|
||||
on_clicked: () => step(+1),
|
||||
});
|
||||
const prev = Widget.Button({
|
||||
child: Widget.Icon(icons.ui.arrow.left),
|
||||
on_clicked: () => step(-1),
|
||||
});
|
||||
return Widget.Box({
|
||||
class_name: "enum-setter",
|
||||
children: [prev, lbl, next],
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {import('./option.js').Opt} opt */
|
||||
const Setter = (opt) => {
|
||||
switch (opt.type) {
|
||||
case "number":
|
||||
return Widget.SpinButton({
|
||||
setup(self) {
|
||||
self.set_range(0, 1000);
|
||||
self.set_increments(1, 5);
|
||||
},
|
||||
connections: [
|
||||
["value-changed", (self) => opt.setValue(self.value, true)],
|
||||
[opt, (self) => (self.value = opt.value)],
|
||||
],
|
||||
});
|
||||
case "float":
|
||||
case "object":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(JSON.parse(self.text || ""), true),
|
||||
connections: [[opt, (self) => (self.text = JSON.stringify(opt.value))]],
|
||||
});
|
||||
case "string":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(self.text, true),
|
||||
connections: [[opt, (self) => (self.text = opt.value)]],
|
||||
});
|
||||
case "enum":
|
||||
return EnumSetter(opt);
|
||||
case "boolean":
|
||||
return Widget.Switch({
|
||||
connections: [
|
||||
["notify::active", (self) => opt.setValue(self.active, true)],
|
||||
[opt, (self) => (self.active = opt.value)],
|
||||
],
|
||||
});
|
||||
case "img":
|
||||
return Widget.FileChooserButton({
|
||||
connections: [
|
||||
[
|
||||
"selection-changed",
|
||||
(self) => {
|
||||
opt.setValue(self.get_uri()?.replace("file://", ""), true);
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
case "font":
|
||||
return Widget.FontButton({
|
||||
show_size: false,
|
||||
use_size: false,
|
||||
connections: [
|
||||
["notify::font", ({ font }) => opt.setValue(font, true)],
|
||||
[opt, (self) => (self.font = opt.value)],
|
||||
],
|
||||
});
|
||||
default:
|
||||
return Widget.Label({
|
||||
label: "no setter with type " + opt.type,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** @param {import('./option.js').Opt} opt */
|
||||
const Row = (opt) =>
|
||||
Widget.Box({
|
||||
class_name: "row",
|
||||
setup: (self) => (self.opt = opt),
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
children: [
|
||||
opt.title &&
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
class_name: "summary",
|
||||
label: opt.title,
|
||||
}),
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
class_name: "id",
|
||||
label: `id: "${opt.id}"`,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
child: Setter(opt),
|
||||
}),
|
||||
opt.note &&
|
||||
Widget.Label({
|
||||
xalign: 1,
|
||||
class_name: "note",
|
||||
label: opt.note,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {string} category */
|
||||
const Page = (category) =>
|
||||
Widget.Scrollable({
|
||||
vexpand: true,
|
||||
class_name: "page",
|
||||
child: Widget.Box({
|
||||
class_name: "page-content vertical",
|
||||
vertical: true,
|
||||
connections: [
|
||||
[
|
||||
search,
|
||||
(self) => {
|
||||
for (const child of self.children) {
|
||||
child.visible =
|
||||
child.opt.id.includes(search.value) ||
|
||||
child.opt.title.includes(search.value) ||
|
||||
child.opt.note.includes(search.value);
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
children: optionsList
|
||||
.filter((opt) => opt.category.includes(category))
|
||||
.map(Row),
|
||||
}),
|
||||
});
|
||||
|
||||
const sidebar = Widget.Revealer({
|
||||
binds: [["reveal-child", search, "value", (v) => !v]],
|
||||
transition: "slide_right",
|
||||
child: Widget.Box({
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "sidebar-header",
|
||||
children: [
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
label: icons.dialog.Search + " Search",
|
||||
on_clicked: () => (showSearch.value = !showSearch.value),
|
||||
}),
|
||||
Widget.Button({
|
||||
hpack: "end",
|
||||
child: Widget.Icon(icons.ui.info),
|
||||
on_clicked: () => App.toggleWindow("about"),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Scrollable({
|
||||
vexpand: true,
|
||||
hscroll: "never",
|
||||
child: Widget.Box({
|
||||
class_name: "sidebar-box vertical",
|
||||
vertical: true,
|
||||
children: [
|
||||
...categories.map((name) =>
|
||||
Widget.Button({
|
||||
label: (icons.dialog[name] || "") + " " + name,
|
||||
xalign: 0,
|
||||
binds: [
|
||||
[
|
||||
"class-name",
|
||||
currentPage,
|
||||
"value",
|
||||
(v) => (v === name ? "active" : ""),
|
||||
],
|
||||
],
|
||||
on_clicked: () => currentPage.setValue(name),
|
||||
}),
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "sidebar-footer",
|
||||
child: Widget.Button({
|
||||
class_name: "copy",
|
||||
child: Widget.Label({
|
||||
label: " Save",
|
||||
xalign: 0,
|
||||
}),
|
||||
hexpand: true,
|
||||
on_clicked: () => {
|
||||
Utils.execAsync(["wl-copy", getValues()]);
|
||||
Utils.execAsync([
|
||||
"notify-send",
|
||||
"-i",
|
||||
"preferences-desktop-theme-symbolic",
|
||||
"Theme copied to clipboard",
|
||||
'To save it permanently, make a new theme in <span weight="bold">themes.js</span>',
|
||||
]);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const searchEntry = Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
binds: [
|
||||
["reveal-child", showSearch],
|
||||
["transition-duration", options.transition],
|
||||
],
|
||||
child: Widget.Entry({
|
||||
connections: [
|
||||
[
|
||||
showSearch,
|
||||
(self) => {
|
||||
if (!showSearch.value) self.text = "";
|
||||
|
||||
if (showSearch.value) self.grab_focus();
|
||||
},
|
||||
],
|
||||
],
|
||||
hexpand: true,
|
||||
class_name: "search",
|
||||
placeholder_text: "Search Options",
|
||||
secondary_icon_name: icons.apps.search,
|
||||
on_change: ({ text }) => (search.value = text || ""),
|
||||
}),
|
||||
});
|
||||
|
||||
const categoriesStack = Widget.Stack({
|
||||
transition: "slide_left_right",
|
||||
items: categories.map((name) => [name, Page(name)]),
|
||||
binds: [
|
||||
["shown", currentPage],
|
||||
["visible", search, "value", (v) => !v],
|
||||
],
|
||||
});
|
||||
|
||||
const searchPage = Widget.Box({
|
||||
binds: [["visible", search, "value", (v) => !!v]],
|
||||
child: Page(""),
|
||||
});
|
||||
|
||||
export default RegularWindow({
|
||||
name: "settings-dialog",
|
||||
title: "Settings",
|
||||
setup: (win) => win.set_default_size(800, 500),
|
||||
connections: [
|
||||
[
|
||||
"delete-event",
|
||||
(win) => {
|
||||
win.hide();
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
"key-press-event",
|
||||
(self, event) => {
|
||||
if (event.get_keyval()[1] === imports.gi.Gdk.KEY_Escape) {
|
||||
self.text = "";
|
||||
showSearch.setValue(false);
|
||||
search.setValue("");
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
sidebar,
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [searchEntry, categoriesStack, searchPage],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
Reference in New Issue
Block a user