From 737fc95e67fd5f9d5a313b2cc1380b7d2a2c3e2d Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 16 Aug 2023 01:47:57 +0200 Subject: [PATCH] es modules and clean up codebase --- src/ags.src.gresource.xml | 22 +- src/app.ts | 80 ++-- src/main.ts | 156 +++++--- src/service/bluetooth.ts | 10 +- src/service/hyprland.ts | 116 +++--- src/service/mpris.ts | 22 +- src/service/network.ts | 36 +- src/service/notifications.ts | 5 +- src/utils.ts | 90 +---- src/widget.ts | 211 +---------- src/widgets.js | 690 ----------------------------------- src/widgets/shared.ts | 8 +- src/window.ts | 145 -------- 13 files changed, 277 insertions(+), 1314 deletions(-) delete mode 100644 src/widgets.js delete mode 100644 src/window.ts diff --git a/src/ags.src.gresource.xml b/src/ags.src.gresource.xml index 873b6b2..d2b9d8f 100644 --- a/src/ags.src.gresource.xml +++ b/src/ags.src.gresource.xml @@ -5,24 +5,22 @@ app.js utils.js widget.js - widgets.js - window.js - widgets/shared.js widgets/box.js - widgets/eventbox.js + widgets/button.js widgets/centerbox.js + widgets/entry.js + widgets/eventbox.js widgets/icon.js widgets/label.js - widgets/button.js - widgets/slider.js - widgets/scrollable.js - widgets/stack.js - widgets/overlay.js - widgets/revealer.js - widgets/progressbar.js widgets/menu.js - widgets/entry.js + widgets/overlay.js + widgets/progressbar.js + widgets/revealer.js + widgets/scrollable.js + widgets/shared.js + widgets/slider.js + widgets/stack.js widgets/window.js service/service.js diff --git a/src/app.ts b/src/app.ts index d755daf..d65ff2b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,16 +3,22 @@ import Gdk from 'gi://Gdk?version=3.0'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; -import Window from './window.js'; -import { error, warning, getConfig, timeout, connect } from './utils.js'; +import { timeout, connect } from './utils.js'; -const APP_BUS = 'com.github.Aylur.' + pkg.name; +interface Config { + windows?: Gtk.Window[] + style?: string + notificationPopupTimeout?: number + closeWindowDelay?: { [key: string]: number } +} export default class App extends Gtk.Application { private _windows: Map; private _closeDelay!: { [key: string]: number }; private _cssProviders: Gtk.CssProvider[] = []; + static configPath: string; + static config: Config; static instance: App; static { @@ -32,7 +38,7 @@ export default class App extends Gtk.Application { static getWindow(name: string) { const w = App.instance._windows.get(name); - return w ? w : warning(`There is no window named ${name}`); + return w ? w : console.error(`There is no window named ${name}`); } static closeWindow(name: string) { @@ -69,7 +75,7 @@ export default class App extends Gtk.Application { static resetCss() { const screen = Gdk.Screen.get_default(); if (!screen) { - error("couldn't get screen"); + console.error("couldn't get screen"); return; } @@ -83,7 +89,7 @@ export default class App extends Gtk.Application { static applyCss(path: string) { const screen = Gdk.Screen.get_default(); if (!screen) { - error("couldn't get screen"); + console.error("couldn't get screen"); return; } @@ -107,52 +113,58 @@ export default class App extends Gtk.Application { connect(this, widget, callback, event); } - constructor() { + constructor({ bus, config }: { + bus: string + config: string + }) { super({ - application_id: APP_BUS, + application_id: bus, flags: Gio.ApplicationFlags.DEFAULT_FLAGS, }); this._windows = new Map(); + App.configPath = config; App.instance = this; } vfunc_activate() { this.hold(); - this._load(); this._exportActions(); + this._load(); } - _load() { - for (const [name, window] of this._windows) { - window.destroy(); - this._windows.delete(name); - } + async _load() { + try { + const mod = await import(`file://${App.configPath}`); + const config = mod.default as Config; + App.config = config; - const config = getConfig(); - if (!config) { - this.quit(); - return; - } - - this._closeDelay = config.closeWindowDelay || {}; - - if (config.style) - App.applyCss(config.style); - - config.windows?.forEach(window => { - const w = Window(window); - w.connect('notify::visible', () => this.emit('window-toggled', w.name, w.visible)); - - if (this._windows.has(w.name)) { - error('name of window has to be unique!'); + if (!config) { + this.quit(); return; } - this._windows.set(w.name, w); - }); + this._closeDelay = config.closeWindowDelay || {}; - this.emit('config-parsed'); + if (config.style) + App.applyCss(config.style); + + config.windows?.forEach(w => { + w.connect('notify::visible', () => this.emit('window-toggled', w.name, w.visible)); + + if (this._windows.has(w.name)) { + console.error('name of window has to be unique!'); + this.quit(); + return; + } + + this._windows.set(w.name, w); + }); + + this.emit('config-parsed'); + } catch (error) { + logError(error as Error); + } } _addAction( diff --git a/src/main.ts b/src/main.ts index 00ca020..ca29995 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,6 @@ import GLib from 'gi://GLib'; import * as Utils from './utils.js'; import App from './app.js'; import Service from './service/service.js'; -import Window from './window.js'; import Widget from './widget.js'; import './service/apps.js'; import './service/audio.js'; @@ -14,10 +13,10 @@ import './service/mpris.js'; import './service/network.js'; import './service/notifications.js'; -const APP_BUS = 'com.github.Aylur.' + pkg.name; +const APP_BUS = (name: string) => 'com.github.Aylur.' + name; const APP_PATH = '/com/github/Aylur/' + pkg.name; -export const help = (bin: string) => `USAGE: +const help = (bin: string) => `USAGE: ${bin} [COMMAND] COMMANDS: @@ -29,64 +28,125 @@ COMMANDS: run-js string\tRuns string as a js function inspector\t\tOpen debugger`; +function client(bus: string, inspector: boolean, runJs: string, toggleWindow: string, quit: boolean) { + const actions = Gio.DBusActionGroup.get( + Gio.DBus.session, bus, APP_PATH); + + if (toggleWindow) + actions.activate_action('toggle-window', new GLib.Variant('s', toggleWindow)); + + if (runJs) + actions.activate_action('run-js', new GLib.Variant('s', runJs)); + + if (inspector) + actions.activate_action('inspector', null); + + if (quit) + actions.activate_action('quit', null); +} + +function isRunning(dbusName: string) { + return Gio.DBus.session.call_sync( + 'org.freedesktop.DBus', + '/org/freedesktop/DBus', + 'org.freedesktop.DBus', + 'NameHasOwner', + // @ts-ignore + GLib.Variant.new_tuple([new GLib.Variant('s', dbusName)]), + new GLib.VariantType('(b)'), + Gio.DBusCallFlags.NONE, + -1, + null, + ).deepUnpack()?.toString() === 'true' || false; +} + export function main(args: string[]) { - switch (args[1]) { - case 'version': - case '-v': - case '--version': - print(pkg.version); - return; + let appBus = pkg.name; + let config = `${GLib.get_user_config_dir()}/${pkg.name}/config.js`; + let inspector = false; + let runJs = ''; + let toggleWindow = ''; + let quit = false; - case 'help': - case '-h': - case '--help': - print(help(args[0])); - return; + args.forEach((arg, i) => { + switch (arg) { + case 'version': + case '-v': + case '--version': + print(pkg.version); + return; - case 'clear-cache': - Utils.exec(`rm -r ${Utils.CACHE_DIR}`); - return; + case 'help': + case '-h': + case '--help': + print(help(args[0])); + return; - default: - break; - } + case 'clear-cache': + case '--clear-cache': + Utils.execAsync(`rm -r ${Utils.CACHE_DIR}`); + break; + + case '-b': + case '--bus-name': + appBus = args[i + 1]; + break; + + case '-c': + case '--config': + config = args[i + 1]; + break; + + case 'inspector': + case '-i': + case '--inspector': + inspector = true; + break; + + case 'run-js': + case '-r': + case '--run-js': + runJs = args[i + 1]; + break; + + case 'toggle-window': + case '-t': + case '--toggle-window': + toggleWindow = args[i + 1]; + break; + + case 'quit': + case '-q': + case '--quit': + quit = true; + break; + + default: + break; + } + }); // @ts-ignore globalThis.ags = { App, Utils, - Window, Widget, Service, }; - if (!Utils.isRunning(APP_BUS)) + const bus = APP_BUS(appBus); + if (!isRunning(bus)) { + const app = new App({ bus, config }); + app.connect('config-parsed', () => { + client(bus, inspector, runJs, toggleWindow, quit); + }); + // @ts-ignore - return new App().runAsync(null); - - const actions = Gio.DBusActionGroup.get( - Gio.DBus.session, APP_BUS, APP_PATH, - ); - - switch (args[1]) { - case 'toggle-window': - actions.activate_action('toggle-window', new GLib.Variant('s', args[2])); - return; - - case 'run-js': - actions.activate_action('run-js', new GLib.Variant('s', args[2])); - return; - - case 'inspector': - actions.activate_action('inspector', null); - return; - - case 'quit': - actions.activate_action('quit', null); - return; - - default: - print(help(args[0])); - return; + return app.runAsync(null); } + + client(bus, inspector, runJs, toggleWindow, quit); + + if (args.length === 1) + print('Ags is already running'); } diff --git a/src/service/bluetooth.ts b/src/service/bluetooth.ts index 3d55f1f..3eef933 100644 --- a/src/service/bluetooth.ts +++ b/src/service/bluetooth.ts @@ -139,11 +139,11 @@ class BluetoothService extends Service { get state() { switch (this._client.default_adapter_state) { - case GnomeBluetooth.AdapterState.ON: return 'on'; - case GnomeBluetooth.AdapterState.TURNING_ONON: return 'turning-on'; - case GnomeBluetooth.AdapterState.OFF: return 'off'; - case GnomeBluetooth.AdapterState.TURNING_OFF: return 'turning-off'; - default: return 'absent'; + case GnomeBluetooth.AdapterState.ON: return 'on'; + case GnomeBluetooth.AdapterState.TURNING_ONON: return 'turning-on'; + case GnomeBluetooth.AdapterState.OFF: return 'off'; + case GnomeBluetooth.AdapterState.TURNING_OFF: return 'turning-off'; + default: return 'absent'; } } diff --git a/src/service/hyprland.ts b/src/service/hyprland.ts index 5f250b9..0baeecb 100644 --- a/src/service/hyprland.ts +++ b/src/service/hyprland.ts @@ -1,6 +1,6 @@ import GLib from 'gi://GLib'; import Service from './service.js'; -import { error, execAsync, subprocess } from '../utils.js'; +import { execAsync, subprocess } from '../utils.js'; const HIS = GLib.getenv('HYPRLAND_INSTANCE_SIGNATURE'); @@ -33,7 +33,7 @@ class HyprlandService extends Service { constructor() { if (!HIS) - error('Hyprland is not running'); + console.error('Hyprland is not running'); super(); this._active = { @@ -112,71 +112,71 @@ class HyprlandService extends Service { try { switch (e) { - case 'workspace': - case 'focusedmon': - case 'monitorremoved': - case 'monitoradded': - await this._syncMonitors(); - break; + case 'workspace': + case 'focusedmon': + case 'monitorremoved': + case 'monitoradded': + await this._syncMonitors(); + break; - case 'createworkspace': - case 'destroyworkspace': - await this._syncWorkspaces(); - break; + case 'createworkspace': + case 'destroyworkspace': + await this._syncWorkspaces(); + break; - case 'openwindow': - case 'movewindow': - case 'windowtitle': - await this._syncClients(); - await this._syncWorkspaces(); - break; + case 'openwindow': + case 'movewindow': + case 'windowtitle': + await this._syncClients(); + await this._syncWorkspaces(); + break; - case 'moveworkspace': - await this._syncWorkspaces(); - await this._syncMonitors(); - break; + case 'moveworkspace': + await this._syncWorkspaces(); + await this._syncMonitors(); + break; - case 'activewindow': - this._active.client.class = argv[0]; - this._active.client.title = argv[1]; - break; + case 'activewindow': + this._active.client.class = argv[0]; + this._active.client.title = argv[1]; + break; - case 'activewindowv2': - this._active.client.address = argv[0]; - break; + case 'activewindowv2': + this._active.client.address = argv[0]; + break; - case 'closewindow': - this._active.client = { - class: '', - title: '', - address: '', - }; - await this._syncClients(); - await this._syncWorkspaces(); - break; + case 'closewindow': + this._active.client = { + class: '', + title: '', + address: '', + }; + await this._syncClients(); + await this._syncWorkspaces(); + break; - case 'urgent': - this.emit('urgent-window', argv[0]); - break; + case 'urgent': + this.emit('urgent-window', argv[0]); + break; - case 'activelayout': { - const [kbName, layoutName] = argv[0].split(','); - this.emit('keyboard-layout', `${kbName}`, `${layoutName}`); - break; - } - case 'changefloating': { - const client = this._clients.get(argv[0]); - if (client) - // @ts-ignore - client.floating = argv[1] === '1'; - break; - } - case 'submap': - this.emit('submap', argv[0]); - break; + case 'activelayout': { + const [kbName, layoutName] = argv[0].split(','); + this.emit('keyboard-layout', `${kbName}`, `${layoutName}`); + break; + } + case 'changefloating': { + const client = this._clients.get(argv[0]); + if (client) + // @ts-ignore + client.floating = argv[1] === '1'; + break; + } + case 'submap': + this.emit('submap', argv[0]); + break; - default: - break; + default: + break; } } catch (error) { logError(error as Error); diff --git a/src/service/mpris.ts b/src/service/mpris.ts index e28aea5..a1b4c27 100644 --- a/src/service/mpris.ts +++ b/src/service/mpris.ts @@ -209,17 +209,17 @@ class MprisPlayer extends GObject.Object { shuffle() { this._playerProxy.Shuffle = !this._playerProxy.Shuffle; } loop() { switch (this._playerProxy.LoopStatus) { - case 'None': - this._playerProxy.LoopStatus = 'Track'; - break; - case 'Track': - this._playerProxy.LoopStatus = 'Playlist'; - break; - case 'Playlist': - this._playerProxy.LoopStatus = 'None'; - break; - default: - break; + case 'None': + this._playerProxy.LoopStatus = 'Track'; + break; + case 'Track': + this._playerProxy.LoopStatus = 'Playlist'; + break; + case 'Playlist': + this._playerProxy.LoopStatus = 'None'; + break; + default: + break; } } } diff --git a/src/service/network.ts b/src/service/network.ts index 632293d..638dd1e 100644 --- a/src/service/network.ts +++ b/src/service/network.ts @@ -4,29 +4,29 @@ import { bulkConnect } from '../utils.js'; const _INTERNET = (device: NM.Device) => { switch (device?.active_connection?.state) { - case NM.ActiveConnectionState.ACTIVATED: return 'connected'; - case NM.ActiveConnectionState.ACTIVATING: return 'connecting'; - case NM.ActiveConnectionState.DEACTIVATING: - case NM.ActiveConnectionState.DEACTIVATED: - default: return 'disconnected'; + case NM.ActiveConnectionState.ACTIVATED: return 'connected'; + case NM.ActiveConnectionState.ACTIVATING: return 'connecting'; + case NM.ActiveConnectionState.DEACTIVATING: + case NM.ActiveConnectionState.DEACTIVATED: + default: return 'disconnected'; } }; const _DEVICE_STATE = (device: NM.Device) => { switch (device?.state) { - case NM.DeviceState.UNMANAGED: return 'unmanaged'; - case NM.DeviceState.UNAVAILABLE: return 'unavailable'; - case NM.DeviceState.DISCONNECTED: return 'disconnected'; - case NM.DeviceState.PREPARE: return 'prepare'; - case NM.DeviceState.CONFIG: return 'config'; - case NM.DeviceState.NEED_AUTH: return 'need_auth'; - case NM.DeviceState.IP_CONFIG: return 'ip_config'; - case NM.DeviceState.IP_CHECK: return 'ip_check'; - case NM.DeviceState.SECONDARIES: return 'secondaries'; - case NM.DeviceState.ACTIVATED: return 'activated'; - case NM.DeviceState.DEACTIVATING: return 'deactivating'; - case NM.DeviceState.FAILED: return 'failed'; - default: return 'unknown'; + case NM.DeviceState.UNMANAGED: return 'unmanaged'; + case NM.DeviceState.UNAVAILABLE: return 'unavailable'; + case NM.DeviceState.DISCONNECTED: return 'disconnected'; + case NM.DeviceState.PREPARE: return 'prepare'; + case NM.DeviceState.CONFIG: return 'config'; + case NM.DeviceState.NEED_AUTH: return 'need_auth'; + case NM.DeviceState.IP_CONFIG: return 'ip_config'; + case NM.DeviceState.IP_CHECK: return 'ip_check'; + case NM.DeviceState.SECONDARIES: return 'secondaries'; + case NM.DeviceState.ACTIVATED: return 'activated'; + case NM.DeviceState.DEACTIVATING: return 'deactivating'; + case NM.DeviceState.FAILED: return 'failed'; + default: return 'unknown'; } }; diff --git a/src/service/notifications.ts b/src/service/notifications.ts index 02dff0b..e870bc1 100644 --- a/src/service/notifications.ts +++ b/src/service/notifications.ts @@ -2,8 +2,9 @@ import Gio from 'gi://Gio'; import GdkPixbuf from 'gi://GdkPixbuf'; import GLib from 'gi://GLib'; import Service from './service.js'; +import App from '../app.js'; import { NotificationIFace } from '../dbus/notifications.js'; -import { NOTIFICATIONS_CACHE_PATH, ensureDirectory, getConfig, readFileAsync, timeout, writeFile } from '../utils.js'; +import { NOTIFICATIONS_CACHE_PATH, ensureDirectory, readFileAsync, timeout, writeFile } from '../utils.js'; interface action { id: string @@ -45,7 +46,7 @@ class NotificationsService extends Service { _notifications: Map; _dnd = false; _idCount = 0; - _timeout = getConfig()?.notificationPopupTimeout || 3000; + _timeout = App.config.notificationPopupTimeout || 3000; constructor() { super(); diff --git a/src/utils.ts b/src/utils.ts index 1fb09f4..413d9e3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,71 +2,11 @@ import Gtk from 'gi://Gtk?version=3.0'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; -import App from './app.js'; -import { type Window } from './window.js'; - -interface Config { - windows?: Window[] - style?: string - stackTraceOnError?: boolean - baseIconSize?: number - notificationPopupTimeout?: number - exitOnError?: boolean - closeWindowDelay: { [key: string]: number } -} export const USER = GLib.get_user_name(); export const CACHE_DIR = `${GLib.get_user_cache_dir()}/${pkg.name}`; export const MEDIA_CACHE_PATH = `${CACHE_DIR}/media`; export const NOTIFICATIONS_CACHE_PATH = `${CACHE_DIR}/notifications`; -export const CONFIG_DIR = `${GLib.get_user_config_dir()}/${pkg.name}`; - -export function error(message: string) { - getConfig()?.stackTraceOnError - ? logError(new Error(message)) - : print(`AGS ERROR: ${message}`); - - if (getConfig()?.exitOnError) - App.quit(); -} - -export function warning(message: string) { - getConfig()?.stackTraceOnError - ? logError(new Error(message)) - : print(`AGS WARNING: ${message}`); -} - -export function typecheck(key: string, value: unknown, type: string | string[], widget: string) { - if (Array.isArray(type)) { - for (const t of type) { - if (t === 'array' && Array.isArray(value)) - return true; - - if (typeof value === t) - return true; - } - - warning(`"${key}" has to be one of ${type.join(' or ')} on ${widget}`); - return false; - } - - if (type === 'array' && Array.isArray(value)) - return true; - - if (typeof value === type) - return true; - - warning(`"${key}" has to be a ${type} on ${widget} but it is of type ${typeof value}`); - return false; -} - -export function restcheck(rest: object, widget: string) { - const keys = Object.keys(rest); - if (keys.length === 0) - return; - - warning(`unknown keys on ${widget}: ${JSON.stringify(keys)}`); -} export function readFile(path: string) { try { @@ -164,7 +104,7 @@ export function timeout(ms: number, callback: () => void) { export function runCmd(cmd: string | ((...args: any[]) => boolean), ...args: any[]) { if (typeof cmd !== 'string' && typeof cmd !== 'function') { - warning('Command has to be string or function'); + console.error('Command has to be string or function'); return false; } @@ -182,19 +122,6 @@ export function runCmd(cmd: string | ((...args: any[]) => boolean), ...args: any return false; } -export function getConfig() { - try { - imports.searchPath.push(CONFIG_DIR); - return imports.config.config as Config; - } catch (err) { - GLib.file_test(CONFIG_DIR + '/config.js', GLib.FileTest.EXISTS) - ? logError(err as Error) - : print('No config was provided'); - - return null; - } -} - export function lookUpIcon(name?: string, size = 16) { if (!name) return null; @@ -222,21 +149,6 @@ export function ensureDirectory(path?: string) { } } -export function isRunning(dbusName: string) { - return Gio.DBus.session.call_sync( - 'org.freedesktop.DBus', - '/org/freedesktop/DBus', - 'org.freedesktop.DBus', - 'NameHasOwner', - // @ts-ignore - GLib.Variant.new_tuple([new GLib.Variant('s', dbusName)]), - new GLib.VariantType('(b)'), - Gio.DBusCallFlags.NONE, - -1, - null, - ).deepUnpack()?.toString() === 'true' || false; -} - export function execAsync(cmd: string | string[]): Promise { const proc = Gio.Subprocess.new( typeof cmd === 'string' ? cmd.split(' ') : cmd, diff --git a/src/widget.ts b/src/widget.ts index 432c0fd..c98d2ba 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -1,7 +1,4 @@ import Gtk from 'gi://Gtk?version=3.0'; -import { typecheck, error, warning, interval } from './utils.js'; -import * as Widgets from './widgets.js'; -import { constructor } from './widgets/shared.js'; import Box from './widgets/box.js'; import CenterBox from './widgets/centerbox.js'; import EventBox from './widgets/eventbox.js'; @@ -17,209 +14,27 @@ import ProgressBar from './widgets/progressbar.js'; import Entry from './widgets/entry.js'; import { Menu, MenuItem } from './widgets/menu.js'; import Window from './widgets/window.js'; +import constructor from './widgets/shared.js'; -interface ServiceAPI { - instance: { - connectWidget: (widget: Gtk.Widget, callback: (widget: Gtk.Widget, ...args: any[]) => void, event?: string) => void - } +function Widget({ type, ...params }: { type: { new(...args: any[]): Gtk.Widget } }) { + return constructor(type, params); } -interface Widget { - type: string | (() => Gtk.Widget) - className?: string - style?: string - halign?: 'start' | 'center' | 'end' | 'fill' - valign?: 'start' | 'center' | 'end' | 'fill' - hexpand?: boolean - vexpand?: boolean - sensitive?: boolean - tooltip?: string - visible?: boolean - connections?: ( - [string, (...args: any[]) => any] | - [number, (...args: any[]) => any] | - [ServiceAPI, (...args: any[]) => any, string] - )[] - properties?: [any, any][] - setup?: (widget: Gtk.Widget) => void -} - -const widgets: { [key: string]: (props: any) => Gtk.Widget } = { - 'box': Widgets.Box, - 'button': Widgets.Button, - 'centerbox': Widgets.CenterBox, - 'dynamic': Widgets.Dynamic, - 'entry': Widgets.Entry, - 'eventbox': Widgets.EventBox, - 'icon': Widgets.Icon, - 'label': Widgets.Label, - 'overlay': Widgets.Overlay, - 'progressbar': Widgets.ProgressBar, - 'revealer': Widgets.Revealer, - 'scrollable': Widgets.Scrollable, - 'slider': Widgets.Slider, - 'stack': Widgets.Stack, - 'switch': Widgets.Switch, - 'menubutton': Widgets.MenuButton, - 'popover': Widgets.Popover, - 'menu': Widgets.Menu, - 'menuitem': Widgets.MenutItem, -}; - -export function setStyle(widget: Gtk.Widget, css: string) { - const provider = new Gtk.CssProvider(); - const style = `* { ${css} }`; - provider.load_from_data(style); - widget.get_style_context().add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER); -} - -export function toggleClassName(widget: Gtk.Widget, className: string, condition = true) { - condition - ? widget.get_style_context().add_class(className) - : widget.get_style_context().remove_class(className); -} - -function parseParams(widget: Gtk.Widget, { - type, className, style, sensitive, tooltip, connections, properties, setup, - halign, valign, hexpand, vexpand, visible = true, -}: Widget) { - type = type.toString(); - typecheck('className', className, ['string', 'undefined'], type); - typecheck('style', style, ['string', 'undefined'], type); - typecheck('sensitive', sensitive, ['boolean', 'undefined'], type); - typecheck('tooltip', tooltip, ['string', 'undefined'], type); - typecheck('halign', halign, ['string', 'undefined'], type); - typecheck('valign', valign, ['string', 'undefined'], type); - typecheck('hexpand', hexpand, ['boolean', 'undefined'], type); - typecheck('vexpand', vexpand, ['boolean', 'undefined'], type); - typecheck('visible', visible, 'boolean', type); - - // @ts-ignore - widget.setStyle = (css: string) => setStyle(widget, css); - - // @ts-ignore - widget.toggleClassName = (className: string, condition = true) => toggleClassName(widget, className, condition); - - if (typeof className === 'string') { - className.split(' ').forEach(cn => { - widget.get_style_context().add_class(cn); - }); - } - - if (typeof halign === 'string') { - // @ts-ignore - const align = Gtk.Align[halign.toUpperCase()]; - if (typeof align !== 'number') - warning('wrong halign value'); - widget.halign = align; - } - - if (typeof valign === 'string') { - // @ts-ignore - const align = Gtk.Align[valign.toUpperCase()]; - if (typeof align !== 'number') - warning('wrong valign value'); - widget.valign = align; - } - - if (typeof hexpand === 'boolean') - widget.hexpand = hexpand; - - if (typeof vexpand === 'boolean') - widget.vexpand = vexpand; - - if (typeof sensitive === 'boolean') - widget.sensitive = sensitive; - - if (typeof tooltip === 'string') - widget.set_tooltip_text(tooltip); - - if (typeof style === 'string') - setStyle(widget, style); - - if (typeof visible === 'boolean') - widget.visible = visible; - - if (properties) { - properties.forEach(([key, value]) => { - // @ts-ignore - widget[`_${key}`] = value; - }); - } - - if (connections) { - connections.forEach(([s, callback, event]) => { - if (typeof s === 'string') - widget.connect(s, callback); - - else if (typeof s === 'number') - interval(s, () => callback(widget), widget); - - else - s.instance.connectWidget(widget, callback, event); - }); - } - - if (typeof setup === 'function') - setup(widget); -} - -export default function Widget(params: Widget | string | (() => Gtk.Widget) | Gtk.Widget): Gtk.Widget { - if (!params) { - error('Widget from null/undefined'); - return new Gtk.Label({ label: `error widget from: "${params}"` }); - } - - if (typeof params === 'string') - return new Gtk.Label({ label: params }); - - if (typeof params === 'function') - return params(); - - if (params instanceof Gtk.Widget) - return params; - - const { - type, className, style, halign, valign, connections, properties, - hexpand, vexpand, sensitive, tooltip, visible, setup, - ...props - }: Widget = params; - - let widget: Gtk.Widget | null = null; - if (typeof type === 'function') - widget = type(); - - if (typeof type === 'string' && type in widgets) - widget = widgets[type]({ type, ...props }); - - if (widget === null) { - error(`There is no widget with type ${type}`); - return new Gtk.Label({ label: `${type} doesn't exist` }); - } - - parseParams(widget, { - type, className, style, halign, valign, connections, properties, - hexpand, vexpand, sensitive, tooltip, visible, setup, - }); - - return widget; -} - -Widget.widgets = widgets; Widget.Box = (params: object) => constructor(Box, params); +Widget.Button = (params: object) => constructor(Button, params); Widget.CenterBox = (params: object) => constructor(CenterBox, params); +Widget.Entry = (params: object) => constructor(Entry, params); Widget.EventBox = (params: object) => constructor(EventBox, params); Widget.Icon = (params: object) => constructor(Icon, params); Widget.Label = (params: object) => constructor(Label, params); -Widget.Button = (params: object) => constructor(Button, params); -Widget.Slider = (params: object) => constructor(Slider, params); -Widget.Stack = (params: object) => constructor(Stack, params); -Widget.Scrollable = (params: object) => constructor(Scrollable, params); -Widget.Overlay = (params: object) => constructor(Overlay, params); -Widget.Revealer = (params: object) => constructor(Revealer, params); -Widget.ProgressBar = (params: object) => constructor(ProgressBar, params); Widget.Menu = (params: object) => constructor(Menu, params); Widget.MenuItem = (params: object) => constructor(MenuItem, params); -Widget.Entry = (params: object) => constructor(Entry, params); +Widget.Overlay = (params: object) => constructor(Overlay, params); +Widget.ProgressBar = (params: object) => constructor(ProgressBar, params); +Widget.Revealer = (params: object) => constructor(Revealer, params); +Widget.Scrollable = (params: object) => constructor(Scrollable, params); +Widget.Slider = (params: object) => constructor(Slider, params); +Widget.Stack = (params: object) => constructor(Stack, params); Widget.Window = (params: object) => constructor(Window, params); -Widget.Widget = ({ type, ...params }: { type: { new(...args: any[]): Gtk.Widget } }) => constructor(type, params); + +export default Widget; diff --git a/src/widgets.js b/src/widgets.js deleted file mode 100644 index 267ae95..0000000 --- a/src/widgets.js +++ /dev/null @@ -1,690 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; -import GLib from 'gi://GLib'; -import GdkPixbuf from 'gi://GdkPixbuf'; -import Widget from './widget.js'; -import { typecheck, runCmd, restcheck, warning, getConfig } from './utils.js'; - -function _orientation(str = 'horizontal') { - if (str === 'v') - str = 'vertical'; - - if (str === 'h') - str = 'horizontal'; - - const orientation = Gtk.Orientation[str.toUpperCase()]; - if (typeof orientation !== 'number') - warning('wrong orientation value'); - - return orientation; -} - -export function Box({ - type, - orientation = 'horizontal', - homogeneous = false, - children = [], - ...rest -}) { - typecheck('orientation', orientation, 'string', type); - typecheck('homogeneous', homogeneous, 'boolean', type); - typecheck('children', children, 'array', type); - restcheck(rest, type); - - const box = new Gtk.Box({ - orientation: _orientation(orientation), - homogeneous, - }); - - children.forEach(w => box.add(Widget(w))); - - return box; -} - -export function EventBox({ - type, - onClick = '', - onSecondaryClick = '', - onMiddleClick = '', - onClickRelease = '', - onSecondaryClickRelease = '', - onMiddleClickRelease = '', - onHover = '', - onHoverLost = '', - onScrollUp = '', - onScrollDown = '', - child, ...rest -}) { - typecheck('onClick', onClick, ['string', 'function'], type); - typecheck('onSecondaryClick', onSecondaryClick, ['string', 'function'], type); - typecheck('onMiddleClick', onMiddleClick, ['string', 'function'], type); - typecheck('onClickRelease', onClickRelease, ['string', 'function'], type); - typecheck('onMiddleClickRelease', onMiddleClickRelease, ['string', 'function'], type); - typecheck('onSecondaryClickRelease', onSecondaryClickRelease, ['string', 'function'], type); - typecheck('onHover', onHover, ['string', 'function'], type); - typecheck('onHoverLost', onHoverLost, ['string', 'function'], type); - typecheck('onScrollUp', onScrollUp, ['string', 'function'], type); - typecheck('onScrollDown', onScrollDown, ['string', 'function'], type); - restcheck(rest, type); - - const box = new Gtk.EventBox(); - - box.connect('enter-notify-event', (box, event) => { - box.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - runCmd(onHover, box, event); - }); - - box.connect('leave-notify-event', (box, event) => { - box.unset_state_flags(Gtk.StateFlags.PRELIGHT); - runCmd(onHoverLost, box, event); - }); - - box.connect('button-press-event', (box, event) => { - box.set_state_flags(Gtk.StateFlags.ACTIVE, false); - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - runCmd(onClick, box, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - runCmd(onSecondaryClick, box, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - runCmd(onMiddleClick, box, event); - }); - - box.connect('button-release-event', (box, event) => { - box.unset_state_flags(Gtk.StateFlags.ACTIVE); - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - runCmd(onClickRelease, box, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - runCmd(onSecondaryClickRelease, box, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - runCmd(onMiddleClickRelease, box, event); - }); - - if (onScrollUp || onScrollDown) { - box.add_events(Gdk.EventMask.SCROLL_MASK); - box.connect('scroll-event', (box, event) => { - if (event.get_scroll_direction()[1] === Gdk.ScrollDirection.UP) - runCmd(onScrollUp, box, event); - else if (event.get_scroll_direction()[1] === Gdk.ScrollDirection.DOWN) - runCmd(onScrollDown, box, event); - }); - } - - if (child) - box.add(Widget(child)); - - return box; -} - -export function CenterBox({ type, children = [], ...props }) { - typecheck('children', children, 'array', type); - - if (children.length !== 3) - warning(`${type} should have exactly 3 children!`); - - const box = Box({ type, ...props }); - - box.pack_start(Widget(children[0]), true, true, 0); - box.set_center_widget(Widget(children[1])); - box.pack_end(Widget(children[2]), true, true, 0); - - return box; -} - -export function Icon({ - type, - icon = '', - size = getConfig()?.baseIconSize || 16, - ...rest -}) { - typecheck('icon', icon, 'string', type); - typecheck('icon', size, 'number', type); - restcheck(rest, type); - - return GLib.file_test(icon, GLib.FileTest.EXISTS) - ? Gtk.Image.new_from_pixbuf( - GdkPixbuf.Pixbuf.new_from_file_at_size(icon, size, size), - ) - : new Gtk.Image({ - icon_name: icon, - icon_size: 1, - pixel_size: size, - }); -} - -export function Label({ - type, - label = '', - markup = false, - wrap = false, - maxWidth = -1, - angle = 0, - justify = 'center', - xalign = 0.5, - yalign = 0.5, - ...rest -}) { - typecheck('label', label, 'string', type); - typecheck('markup', markup || false, 'boolean', type); - typecheck('wrap', wrap || false, 'boolean', type); - typecheck('angle', angle || 0, 'number', type); - typecheck('justify', justify || '', 'string', type); - typecheck('xalign', xalign, 'number', type); - typecheck('yalign', yalign, 'number', type); - restcheck(rest, type); - - const _justify = Gtk.Justification[justify.toUpperCase()]; - if (typeof _justify !== 'number') - warning('wrong justify value'); - - const lbl = new Gtk.Label({ - label, - angle, - justify: _justify, - use_markup: markup, - max_width_chars: maxWidth, - wrap, - xalign, - yalign, - }); - - return lbl; -} - -export function Button({ - type, - child, - onClick = '', - onMiddleClick = '', - onSecondaryClick = '', - onClickRelease = '', - onMiddleClickRelease = '', - onSecondaryClickRelease = '', - onScrollUp = '', - onScrollDown = '', - ...rest -}) { - typecheck('onClick', onClick, ['string', 'function'], type); - typecheck('onMiddleClick', onMiddleClick, ['string', 'function'], type); - typecheck('onSecondaryClick', onSecondaryClick, ['string', 'function'], type); - typecheck('onClickRelease', onClickRelease, ['string', 'function'], type); - typecheck('onMiddleClickRelease', onMiddleClickRelease, ['string', 'function'], type); - typecheck('onSecondaryClick', onSecondaryClickRelease, ['string', 'function'], type); - typecheck('onScrollUp', onScrollUp, ['string', 'function'], type); - typecheck('onScrollDown', onScrollDown, ['string', 'function'], type); - restcheck(rest, type); - - const btn = new Gtk.Button(); - - if (child) - btn.add(Widget(child)); - - btn.connect('button-press-event', (btn, event) => { - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - runCmd(onClick, btn, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - runCmd(onSecondaryClick, btn, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - runCmd(onMiddleClick, btn, event); - }); - - btn.connect('button-release-event', (btn, event) => { - if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) - runCmd(onClickRelease, btn, event); - - else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) - runCmd(onSecondaryClickRelease, btn, event); - - else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) - runCmd(onMiddleClickRelease, btn, event); - }); - - if (onScrollUp || onScrollDown) { - btn.add_events(Gdk.EventMask.SCROLL_MASK); - btn.connect('scroll-event', (btn, event) => { - if (event.get_scroll_direction()[1] === Gdk.ScrollDirection.UP) - runCmd(onScrollUp, btn, event); - - else if (event.get_scroll_direction()[1] === Gdk.ScrollDirection.DOWN) - runCmd(onScrollDown, btn, event); - }); - } - - return btn; -} - -export function Slider({ - type, - inverted = false, - orientation = 'horizontal', - min = 0, - max = 1, - value = 0, - onChange = '', - drawValue = false, - ...rest -}) { - typecheck('inverted', inverted, 'boolean', type); - typecheck('orientation', orientation, 'string', type); - typecheck('min', min, 'number', type); - typecheck('max', max, 'number', type); - typecheck('onChange', onChange, ['string', 'function'], type); - typecheck('value', value, 'number', type); - typecheck('drawValue', drawValue, 'boolean', type); - restcheck(rest, type); - - const slider = new Gtk.Scale({ - orientation: _orientation(orientation), - adjustment: new Gtk.Adjustment({ - value: min, - lower: min, - upper: max, - step_increment: (max - min) / 100, - }), - draw_value: drawValue, - inverted: inverted, - }); - - slider.connect('button-press-event', () => { slider._dragging = true; }); - slider.connect('button-release-event', () => { slider._dragging = false; }); - - slider.connect('scroll-event', ({ adjustment }, event) => { - const [, , y] = event.get_scroll_deltas(); - - slider._dragging = true; - y > 0 - ? adjustment.value -= adjustment.step_increment - : adjustment.value += adjustment.step_increment; - - slider._dragging = false; - }); - - if (onChange) { - slider.adjustment.connect('notify::value', ({ value }, event) => { - if (!slider._dragging) - return; - - typeof onChange === 'function' - ? onChange(slider, event, value) - : runCmd(onChange.replace(/\{\}/g, value)); - }); - } - - return slider; -} - -export function Dynamic({ type, items = [], ...rest } = {}) { - typecheck('items', items, 'array', type); - restcheck(rest, type); - - const children = items.map(({ value, widget }) => { - if (!widget) - return null; - - const w = Widget(widget); - w._value = value; - w.hide(); - return w; - }).filter(item => item !== null); - - const box = Box({ children }); - box.update = condition => { - box.hide(); - for (const item of children) - item.hide(); - - for (const item of children) { - if (condition(item._value)) { - box.show(); - item.show(); - return; - } - } - }; - - return box; -} - -export function Stack({ - type, - items = [], - hhomogeneous = true, - vhomogeneous = true, - interpolateSize = false, - transition = 'none', - transitionDuration = 200, - ...rest -}) { - typecheck('hhomogeneous', hhomogeneous, 'boolean', type); - typecheck('vhomogeneous', vhomogeneous, 'boolean', type); - typecheck('interpolateSize', interpolateSize, 'boolean', type); - typecheck('transition', transition, 'string', type); - typecheck('transitionDuration', transitionDuration, 'number', type); - typecheck('items', items, 'array', type); - restcheck(rest, type); - - const transitionType = Gtk.StackTransitionType[transition.toUpperCase()]; - if (typeof transitionType !== 'number') - warning('wrong transition value'); - - const stack = new Gtk.Stack({ - hhomogeneous, - vhomogeneous, - interpolateSize, - transitionDuration, - transitionType, - }); - - items.forEach(([name, widget]) => { - if (widget) - stack.add_named(Widget(widget), name); - }); - - stack.showChild = name => { - const n = typeof name === 'function' ? name() : name; - stack.visible = true; - stack.get_child_by_name(n) - ? stack.set_visible_child_name(n) - : stack.visible = false; - }; - return stack; -} - -export function Entry({ - type, - text = '', - placeholder = '', - onChange = '', - onAccept = '', - password = false, - ...rest -}) { - typecheck('text', text, 'string', type); - typecheck('placeholder', placeholder, 'string', type); - typecheck('onChange', onChange, ['string', 'function'], type); - typecheck('onAccept', onAccept, ['string', 'function'], type); - typecheck('password', password, 'boolean', type); - restcheck(rest, type); - - const entry = new Gtk.Entry({ - placeholder_text: placeholder, - visibility: !password, - text, - }); - - if (onAccept) { - entry.connect('activate', ({ text }, event) => { - typeof onAccept === 'function' - ? onAccept(entry, event, text) - : runCmd(onAccept.replace(/\{\}/g, text)); - }); - } - - if (onChange) { - entry.connect('notify::text', ({ text }, event) => { - typeof onAccept === 'function' - ? onChange(entry, event, text) - : runCmd(onChange.replace(/\{\}/g, text)); - }); - } - - return entry; -} - -export function Scrollable({ - type, - child, - hscroll = 'automatic', - vscroll = 'automatic', - ...rest -}) { - typecheck('hscroll', hscroll, 'string', type); - typecheck('vscroll', vscroll, 'string', type); - restcheck(rest, type); - - const scrollable = new Gtk.ScrolledWindow({ - hadjustment: new Gtk.Adjustment(), - vadjustment: new Gtk.Adjustment(), - }); - scrollable.set_policy( - Gtk.PolicyType[hscroll.toUpperCase()], - Gtk.PolicyType[vscroll.toUpperCase()], - ); - - if (child) - scrollable.add(Widget(child)); - - return scrollable; -} - -export function Revealer({ - type, - transition = 'crossfade', - duration = 250, - revealChild = false, - child, - ...rest -}) { - typecheck('transition', transition, 'string', type); - typecheck('duration', duration, 'number', type); - typecheck('revealChild', revealChild, 'boolean', type); - restcheck(rest, type); - - const transitionType = Gtk.RevealerTransitionType[transition.toUpperCase()]; - if (typeof transitionType !== 'number') - warning('wrong transition type'); - - const revealer = new Gtk.Revealer({ - transitionType, - revealChild, - transitionDuration: duration, - }); - - if (child) - revealer.add(Widget(child)); - - return revealer; -} - -export function Overlay({ type, children = [], passthrough = true, ...rest }) { - typecheck('passthrough', passthrough, 'boolean', type); - typecheck('children', children, 'array', type); - restcheck(rest, type); - - const overlay = new Gtk.Overlay(); - - if (children[0]) { - overlay.add(Widget(children[0])); - children.splice(1).forEach(ch => overlay.add_overlay(Widget(ch))); - } - - if (passthrough) - overlay.get_children().forEach(ch => overlay.set_overlay_pass_through(ch, true)); - - return overlay; -} - -export function ProgressBar({ - type, - value = 0, - inverted = false, - orientation = 'horizontal', - ...rest -}) { - typecheck('inverted', inverted, 'boolean', type); - typecheck('orientation', orientation, 'string', type); - typecheck('value', value, 'number', type); - restcheck(rest, type); - - const bar = new Gtk.ProgressBar({ - orientation: _orientation(orientation), - inverted, - fraction: value, - }); - - bar.setValue = v => bar.set_fraction(v); - return bar; -} - -export function Switch({ - type, - active = false, - onActivate = '', - ...rest -}) { - typecheck('active', active, 'boolean', type); - typecheck('onActivate', onActivate, ['string', 'function'], type); - restcheck(rest, type); - - const gtkswitch = new Gtk.Switch({ active }); - if (onActivate) { - gtkswitch.connect('notify::active', ({ active }, event) => { - typeof onActivate === 'function' - ? onActivate(gtkswitch, event, active) - : runCmd(onActivate.replace(/\{\}/g, active)); - }); - } - - return gtkswitch; -} - -export function Popover({ - type, - child, - modal = true, - position = 'bottom', - pointingTo, - relativeTo, - ...rest -}) { - typecheck('position', position, 'string', type); - typecheck('modal', modal, 'boolean', type); - typecheck('pointingTo', pointingTo, ['undefined', 'object'], type); - restcheck(rest, type); - - const _position = Gtk.PositionType[position.toUpperCase()]; - if (typeof _position !== 'number') - warning('wrong position value'); - - const popover = new Gtk.Popover({ - position: _position, - modal, - }); - - if (child) - popover.add(Widget(child)); - - if (pointingTo) - popover.set_pointing_to(new Gdk.Rectangle(pointingTo)); - - if (relativeTo) - popover.set_relative_to(relativeTo); - - return popover; -} - -export function MenuButton({ - type, - child, - popover, - popup, - onToggled = '', - ...rest -}) { - typecheck('onToggled', onToggled, ['string', 'function'], type); - restcheck(rest, type); - - const button = new Gtk.MenuButton({ - use_popover: true, - }); - - if (onToggled) - button.connect('toggled', (...args) => runCmd(onToggled, ...args)); - - if (popover) - button.set_popover(Widget(popover)); - - if (popup) - button.set_popup(Widget(popup)); - - if (child) - button.add(Widget(child)); - - return button; -} - -export function Menu({ - type, - children = [], - yOffset = 0, - xOffset = 0, - onPopup = '', - onMoveScroll = '', - attachTo, - ...rest -}) { - typecheck('children', children, 'array', type); - typecheck('yOffset', yOffset, 'number', type); - typecheck('xOffset', xOffset, 'number', type); - typecheck('onPopup', onPopup, ['string', 'function'], type); - typecheck('onMoveScroll', onMoveScroll, ['string', 'function'], type); - restcheck(rest, type); - - const menu = new Gtk.Menu({ - rect_anchor_dx: xOffset, - rect_anchor_dy: yOffset, - }); - - children.forEach(item => { - menu.add(Widget(item)); - }); - - if (attachTo) - menu.attach_widget = attachTo; - - if (onPopup) - menu.connect('popped-up', (...args) => runCmd(onPopup, ...args)); - - if (onMoveScroll) - menu.connect('move-scroll', (...args) => runCmd(onMoveScroll, ...args)); - - menu.show_all(); - menu.hide(); - return menu; -} - -export function MenutItem({ - type, - child, - submenu, - onActivate = '', - onSelect = '', - onDeselect = '', - ...rest -}) { - typecheck('onActivate', onActivate, ['string', 'function'], type); - typecheck('onSelect', onSelect, ['string', 'function'], type); - typecheck('onDeselect', onDeselect, ['string', 'function'], type); - restcheck(rest, type); - - const item = new Gtk.MenuItem(); - - if (child) - item.add(Widget(child)); - - if (submenu) - item.submenu = Widget(submenu); - - [[onActivate, 'activate'], [onSelect, 'select'], [onDeselect, 'deselect']] - .forEach(([handler, signal]) => { - if (handler) - item.connect(signal, (...args) => runCmd(handler, ...args)); - }); - - return item; -} diff --git a/src/widgets/shared.ts b/src/widgets/shared.ts index 8f5390a..3c278e1 100644 --- a/src/widgets/shared.ts +++ b/src/widgets/shared.ts @@ -1,5 +1,5 @@ import Gtk from 'gi://Gtk?version=3.0'; -import { warning, interval } from '../utils.js'; +import { interval } from '../utils.js'; interface ServiceAPI { instance: { @@ -65,7 +65,7 @@ function parseCommon(widget: Gtk.Widget, { // @ts-ignore const align = Gtk.Align[halign.toUpperCase()]; if (typeof align !== 'number') - warning('wrong halign value'); + console.error('wrong halign value'); widget.halign = align; } @@ -73,7 +73,7 @@ function parseCommon(widget: Gtk.Widget, { // @ts-ignore const align = Gtk.Align[valign.toUpperCase()]; if (typeof align !== 'number') - warning('wrong valign value'); + console.error('wrong valign value'); widget.valign = align; } @@ -104,7 +104,7 @@ function parseCommon(widget: Gtk.Widget, { setup(widget); } -export function constructor(ctor: { new(...args: any[]): Gtk.Widget }, params: CommonParams | string = {}) { +export default function constructor(ctor: { new(...args: any[]): Gtk.Widget }, params: CommonParams | string = {}) { let widget; if (typeof params === 'string') { widget = new ctor(params); diff --git a/src/window.ts b/src/window.ts deleted file mode 100644 index ec33602..0000000 --- a/src/window.ts +++ /dev/null @@ -1,145 +0,0 @@ -import Gtk from 'gi://Gtk?version=3.0'; -import Gdk from 'gi://Gdk?version=3.0'; -import { restcheck, typecheck, warning } from './utils.js'; -import Widget from './widget.js'; -import App from './app.js'; -import { setStyle, toggleClassName } from './widget.js'; - -imports.gi.versions.GtkLayerShell = '0.1'; -const { GtkLayerShell: GtkLayerShell } = imports.gi; - -export interface Window { - anchor?: string[] - child?: { type: string } | Gtk.Widget | null - className?: string - exclusive?: boolean - focusable?: boolean - layer?: string - margin?: number[] | number - monitor?: number - name?: string - popup?: boolean - style?: string - visible?: boolean - setup?: (win: Gtk.Window) => void, -} - -export default function Window({ - name = 'gtk-layer-shell', - anchor = [], - margin = [], - layer = 'top', - exclusive = false, - popup = false, - focusable = false, - child = null, - className = '', - style = '', - monitor, - visible = true, - setup, - ...rest -}: Window): Gtk.Window { - typecheck('name', name, 'string', 'window'); - typecheck('anchor', anchor, 'array', 'window'); - typecheck('margin', margin, ['number', 'array'], 'window'); - typecheck('layer', layer, 'string', 'window'); - typecheck('exclusive', exclusive, 'boolean', 'window'); - typecheck('popup', popup, 'boolean', 'window'); - typecheck('focusable', focusable, 'boolean', 'window'); - typecheck('className', className, 'string', 'window'); - typecheck('monitor', monitor, ['number', 'undefined'], 'window'); - restcheck(rest, `window: ${name}`); - - const win = new Gtk.Window({ name }); - GtkLayerShell.init_for_window(win); - GtkLayerShell.set_namespace(win, name); - - // @ts-ignore - win.setStyle = (css: string) => setStyle(win, css); - - // @ts-ignore - win.toggleClassName = (className: string, condition) => toggleClassName(win, className, condition); - - if (anchor) { - anchor.forEach(side => GtkLayerShell - .set_anchor( - win, - GtkLayerShell.Edge[side.toUpperCase()], - true, - ), - ); - } - - if (margin) { - let margins: [side: string, index: number][] = []; - if (typeof margin === 'number') - margin = [margin]; - - switch (margin.length) { - case 1: - margins = [['TOP', 0], ['RIGHT', 0], ['BOTTOM', 0], ['LEFT', 0]]; - break; - case 2: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 0], ['LEFT', 1]]; - break; - case 3: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 2], ['LEFT', 1]]; - break; - case 4: - margins = [['TOP', 0], ['RIGHT', 1], ['BOTTOM', 2], ['LEFT', 3]]; - break; - default: - break; - } - - margins.forEach(([side, i]) => - GtkLayerShell.set_margin(win, GtkLayerShell.Edge[side], (margin as number[])[i]), - ); - } - - GtkLayerShell.set_layer(win, GtkLayerShell.Layer[layer?.toUpperCase()]); - - if (exclusive) - GtkLayerShell.auto_exclusive_zone_enable(win); - - if (typeof monitor === 'number') { - const display = Gdk.Display.get_default(); - display - ? GtkLayerShell.set_monitor(win, display.get_monitor(monitor)) - : warning(`Could not find monitor with id: ${monitor}`); - } - - if (className) { - className.split(' ').forEach(cn => { - win.get_style_context().add_class(cn); - }); - } - - if (style) - setStyle(win, style); - - if (child) - win.add(Widget(child)); - - if (popup) { - win.connect('key-press-event', (_, event) => { - if (event.get_keyval()[1] === Gdk.KEY_Escape) - App.getWindow(name) ? App.closeWindow(name) : win.hide(); - }); - - visible = false; - } - - if (focusable) - GtkLayerShell.set_keyboard_mode(win, GtkLayerShell.KeyboardMode.ON_DEMAND); - - win.show_all(); - if (typeof visible === 'boolean') - win.visible = visible; - - if (setup) - setup(win); - - return win; -}