Settings menu now warns when a setting is unsaved. (#65)
* Settings menu now warns when a setting is unsaved. Additionally, font-weight increments properly now. * Remove font weight fix - another PR already open for fix.
This commit is contained in:
@@ -355,3 +355,9 @@ dialog {
|
||||
color: $bar-menus-label;
|
||||
}
|
||||
}
|
||||
|
||||
.unsaved-icon {
|
||||
margin-right: 1em;
|
||||
font-size: 1em;
|
||||
color: $yellow;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const BarSettings = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "always",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page paged-container",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
|
||||
@@ -7,7 +7,7 @@ export const DashboardMenuSettings = () => {
|
||||
return Widget.Scrollable({
|
||||
class_name: "bar-theme-page paged-container",
|
||||
vscroll: "always",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
vexpand: true,
|
||||
overlayScrolling: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const BarTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "always",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "bar-theme-page paged-container",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const BatteryMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page battery paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const BluetoothMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page bluetooth paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const ClockMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page clock paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const DashboardMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "always",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page dashboard paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const MenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const MediaMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page media paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const NetworkMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page network paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const NotificationsMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page notifications paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const SystrayMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page systray paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const VolumeMenuTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "menu-theme-page volume paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const NotificationsTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "notifications-theme-page paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -6,7 +6,7 @@ import options from "options";
|
||||
export const OsdTheme = () => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
hscroll: "never",
|
||||
hscroll: "automatic",
|
||||
class_name: "osd-theme-page paged-container",
|
||||
vexpand: true,
|
||||
child: Widget.Box({
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Opt } from "lib/option"
|
||||
import Gdk from "gi://Gdk"
|
||||
import icons from "lib/icons"
|
||||
import { RowProps } from "lib/types/options"
|
||||
import { Variable } from "types/variable";
|
||||
|
||||
const EnumSetter = (opt: Opt<string>, values: string[]) => {
|
||||
const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) })
|
||||
@@ -34,33 +35,99 @@ export const Inputter = <T>({
|
||||
min = 0,
|
||||
increment = 1
|
||||
}: RowProps<T>,
|
||||
className: string
|
||||
className: string,
|
||||
isUnsaved: Variable<boolean>
|
||||
) => {
|
||||
return Widget.Box({
|
||||
class_name: "inputter-container",
|
||||
setup: self => {
|
||||
|
||||
switch (type) {
|
||||
case "number": return self.child = Widget.SpinButton({
|
||||
setup(self) {
|
||||
self.set_range(min, max)
|
||||
self.set_increments(1 * increment, 5 * increment)
|
||||
self.on("value-changed", () => opt.value = self.value as T)
|
||||
self.hook(opt, () => self.value = opt.value as number)
|
||||
},
|
||||
})
|
||||
case "number": return self.children = [
|
||||
Widget.Box({
|
||||
class_name: "unsaved-icon-container",
|
||||
child: isUnsaved.bind("value").as(unsvd => {
|
||||
if (unsvd) {
|
||||
return Widget.Icon({
|
||||
class_name: "unsaved-icon",
|
||||
icon: icons.ui.warning,
|
||||
tooltipText: "Press 'Enter' to apply your changes."
|
||||
})
|
||||
}
|
||||
return Widget.Box();
|
||||
}),
|
||||
}),
|
||||
Widget.SpinButton({
|
||||
setup(self) {
|
||||
self.set_range(min, max)
|
||||
self.set_increments(1 * increment, 5 * increment)
|
||||
self.on("value-changed", () => {
|
||||
opt.value = self.value as T;
|
||||
})
|
||||
self.hook(opt, () => {
|
||||
self.value = opt.value as number;
|
||||
isUnsaved.value = Number(self.text) !== opt.value as number;
|
||||
})
|
||||
self.connect("key-release-event", () => {
|
||||
isUnsaved.value = Number(self.text) !== opt.value as number;
|
||||
})
|
||||
},
|
||||
})
|
||||
]
|
||||
|
||||
case "float":
|
||||
case "object": return self.child = Widget.Entry({
|
||||
class_name: className,
|
||||
on_accept: self => opt.value = JSON.parse(self.text || ""),
|
||||
setup: self => self.hook(opt, () => self.text = JSON.stringify(opt.value)),
|
||||
})
|
||||
case "object": return self.children = [
|
||||
Widget.Box({
|
||||
class_name: "unsaved-icon-container",
|
||||
child: isUnsaved.bind("value").as(unsvd => {
|
||||
if (unsvd) {
|
||||
return Widget.Icon({
|
||||
class_name: "unsaved-icon",
|
||||
icon: icons.ui.warning,
|
||||
tooltipText: "Press 'Enter' to apply your changes."
|
||||
})
|
||||
}
|
||||
return Widget.Box();
|
||||
}),
|
||||
}),
|
||||
Widget.Entry({
|
||||
class_name: className,
|
||||
on_change: self => isUnsaved.value = self.text !== JSON.stringify(opt.value),
|
||||
on_accept: self => opt.value = JSON.parse(self.text || ""),
|
||||
setup: self => self.hook(opt, () => {
|
||||
self.text = JSON.stringify(opt.value);
|
||||
isUnsaved.value = self.text !== JSON.stringify(opt.value);
|
||||
})
|
||||
})
|
||||
]
|
||||
|
||||
case "string": return self.child = Widget.Entry({
|
||||
on_accept: self => opt.value = self.text as T,
|
||||
setup: self => self.hook(opt, () => self.text = opt.value as string),
|
||||
})
|
||||
|
||||
case "string": return self.children = [
|
||||
Widget.Box({
|
||||
class_name: "unsaved-icon-container",
|
||||
child: isUnsaved.bind("value").as(unsvd => {
|
||||
if (unsvd) {
|
||||
return Widget.Icon({
|
||||
class_name: "unsaved-icon",
|
||||
icon: icons.ui.warning,
|
||||
tooltipText: "Press 'Enter' to apply your changes."
|
||||
})
|
||||
}
|
||||
return Widget.Box();
|
||||
}),
|
||||
}),
|
||||
Widget.Entry({
|
||||
class_name: isUnsaved.bind("value").as(unsaved => unsaved ? "unsaved" : ""),
|
||||
on_change: self => isUnsaved.value = self.text !== opt.value,
|
||||
on_accept: self => {
|
||||
opt.value = self.text as T;
|
||||
},
|
||||
setup: self => self.hook(opt, () => {
|
||||
isUnsaved.value = self.text !== opt.value;
|
||||
self.text = opt.value as string;
|
||||
}),
|
||||
})
|
||||
]
|
||||
|
||||
case "enum": return self.child = EnumSetter(opt as unknown as Opt<string>, enums!)
|
||||
case "boolean": return self.child = Widget.Switch()
|
||||
|
||||
@@ -8,7 +8,10 @@ type Option = {
|
||||
subtitle: string,
|
||||
|
||||
}
|
||||
|
||||
export const Option = <T>(props: RowProps<T>, className: string = '') => {
|
||||
const isUnsaved = Variable(false);
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "option-item",
|
||||
hexpand: true,
|
||||
@@ -19,7 +22,7 @@ export const Option = <T>(props: RowProps<T>, className: string = '') => {
|
||||
hexpand: true,
|
||||
child: Label(props.title, props.subtitle || ""),
|
||||
}),
|
||||
Inputter(props, className),
|
||||
Inputter(props, className, isUnsaved),
|
||||
Widget.Button({
|
||||
vpack: "center",
|
||||
class_name: "reset-options",
|
||||
|
||||
Reference in New Issue
Block a user