begin astal rewrite in lua; implement workspace widget in statusbar
This commit is contained in:
parent
9e70891175
commit
9d44a0ef14
64
config.js
64
config.js
@ -1,64 +0,0 @@
|
|||||||
const { execAsync } = Utils
|
|
||||||
|
|
||||||
// import { reveal_menu, reveal_launcher, Bar, FakeBar } from './windows/bar.js'
|
|
||||||
import { Bar, FakeBar } from './windows/top-bar.js'
|
|
||||||
import { NotificationPopups } from './windows/notifications.js'
|
|
||||||
import { Lock, show_lock } from './windows/lock.js'
|
|
||||||
|
|
||||||
execAsync('mpDris2')
|
|
||||||
|
|
||||||
Utils.monitorFile(
|
|
||||||
`./style/style.scss`,
|
|
||||||
|
|
||||||
function() {
|
|
||||||
const scss = `./style/style.scss`
|
|
||||||
|
|
||||||
const css = `./style/style.css`
|
|
||||||
|
|
||||||
Utils.exec(`sassc ${scss} ${css}`)
|
|
||||||
App.resetCss()
|
|
||||||
App.applyCss(css)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
App.addIcons('/usr/share/icons/Papirus/symbolic/status')
|
|
||||||
|
|
||||||
App.config({
|
|
||||||
windows: [
|
|
||||||
Lock,
|
|
||||||
FakeBar,
|
|
||||||
Bar,
|
|
||||||
NotificationPopups,
|
|
||||||
],
|
|
||||||
style: './style/style.css',
|
|
||||||
icons: './icons'
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.defineProperty(globalThis, "lock", {
|
|
||||||
get() {
|
|
||||||
show_lock.value = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Object.defineProperty(globalThis, "swipeRight", {
|
|
||||||
get() {
|
|
||||||
if (reveal_menu.value) {
|
|
||||||
reveal_launcher.value = true
|
|
||||||
} else {
|
|
||||||
reveal_menu.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.defineProperty(globalThis, "swipeLeft", {
|
|
||||||
get() {
|
|
||||||
if (reveal_launcher.value) {
|
|
||||||
reveal_launcher.value = false
|
|
||||||
} else {
|
|
||||||
reveal_menu.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})*/
|
|
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2,7V8.5H3V17H4.5V7C3.7,7 2.8,7 2,7M6,7V7L6,16H7V17H14V16H22V7H6M17.5,9A2.5,2.5 0 0,1 20,11.5A2.5,2.5 0 0,1 17.5,14A2.5,2.5 0 0,1 15,11.5A2.5,2.5 0 0,1 17.5,9Z" /></svg>
|
|
Before Width: | Height: | Size: 238 B |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,11A1,1 0 0,0 11,12A1,1 0 0,0 12,13A1,1 0 0,0 13,12A1,1 0 0,0 12,11M12.5,2C17,2 17.11,5.57 14.75,6.75C13.76,7.24 13.32,8.29 13.13,9.22C13.61,9.42 14.03,9.73 14.35,10.13C18.05,8.13 22.03,8.92 22.03,12.5C22.03,17 18.46,17.1 17.28,14.73C16.78,13.74 15.72,13.3 14.79,13.11C14.59,13.59 14.28,14 13.88,14.34C15.87,18.03 15.08,22 11.5,22C7,22 6.91,18.42 9.27,17.24C10.25,16.75 10.69,15.71 10.89,14.79C10.4,14.59 9.97,14.27 9.65,13.87C5.96,15.85 2,15.07 2,11.5C2,7 5.56,6.89 6.74,9.26C7.24,10.25 8.29,10.68 9.22,10.87C9.41,10.39 9.73,9.97 10.14,9.65C8.15,5.96 8.94,2 12.5,2Z" /></svg>
|
|
Before Width: | Height: | Size: 648 B |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 64C28.7 64 0 92.7 0 128v7.4c0 6.8 4.4 12.6 10.1 16.3C23.3 160.3 32 175.1 32 192s-8.7 31.7-21.9 40.3C4.4 236 0 241.8 0 248.6V320H576V248.6c0-6.8-4.4-12.6-10.1-16.3C552.7 223.7 544 208.9 544 192s8.7-31.7 21.9-40.3c5.7-3.7 10.1-9.5 10.1-16.3V128c0-35.3-28.7-64-64-64H64zM576 352H0v64c0 17.7 14.3 32 32 32H80V416c0-8.8 7.2-16 16-16s16 7.2 16 16v32h96V416c0-8.8 7.2-16 16-16s16 7.2 16 16v32h96V416c0-8.8 7.2-16 16-16s16 7.2 16 16v32h96V416c0-8.8 7.2-16 16-16s16 7.2 16 16v32h48c17.7 0 32-14.3 32-32V352zM192 160v64c0 17.7-14.3 32-32 32s-32-14.3-32-32V160c0-17.7 14.3-32 32-32s32 14.3 32 32zm128 0v64c0 17.7-14.3 32-32 32s-32-14.3-32-32V160c0-17.7 14.3-32 32-32s32 14.3 32 32zm128 0v64c0 17.7-14.3 32-32 32s-32-14.3-32-32V160c0-17.7 14.3-32 32-32s32 14.3 32 32z"/></svg>
|
|
Before Width: | Height: | Size: 990 B |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M176 24c0-13.3-10.7-24-24-24s-24 10.7-24 24V64c-35.3 0-64 28.7-64 64H24c-13.3 0-24 10.7-24 24s10.7 24 24 24H64v56H24c-13.3 0-24 10.7-24 24s10.7 24 24 24H64v56H24c-13.3 0-24 10.7-24 24s10.7 24 24 24H64c0 35.3 28.7 64 64 64v40c0 13.3 10.7 24 24 24s24-10.7 24-24V448h56v40c0 13.3 10.7 24 24 24s24-10.7 24-24V448h56v40c0 13.3 10.7 24 24 24s24-10.7 24-24V448c35.3 0 64-28.7 64-64h40c13.3 0 24-10.7 24-24s-10.7-24-24-24H448V280h40c13.3 0 24-10.7 24-24s-10.7-24-24-24H448V176h40c13.3 0 24-10.7 24-24s-10.7-24-24-24H448c0-35.3-28.7-64-64-64V24c0-13.3-10.7-24-24-24s-24 10.7-24 24V64H280V24c0-13.3-10.7-24-24-24s-24 10.7-24 24V64H176V24zM160 128H352c17.7 0 32 14.3 32 32V352c0 17.7-14.3 32-32 32H160c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32zm192 32H160V352H352V160z"/></svg>
|
|
Before Width: | Height: | Size: 993 B |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15 13V5A3 3 0 0 0 9 5V13A5 5 0 1 0 15 13M12 4A1 1 0 0 1 13 5V8H11V5A1 1 0 0 1 12 4Z" /></svg>
|
|
Before Width: | Height: | Size: 163 B |
17
init.lua
Normal file
17
init.lua
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
require("lua.globals")
|
||||||
|
|
||||||
|
local bar <const> = require('lua.widgets.bar')
|
||||||
|
|
||||||
|
local App <const> = astal.App
|
||||||
|
|
||||||
|
local scss <const> = "./style/style.scss"
|
||||||
|
local css <const> = "./style/style.css"
|
||||||
|
|
||||||
|
astal.exec(string.format("sassc %s %s", scss, css))
|
||||||
|
|
||||||
|
App:start({
|
||||||
|
css = css,
|
||||||
|
main = function()
|
||||||
|
bar:show_all()
|
||||||
|
end,
|
||||||
|
})
|
15
lua/globals.lua
Normal file
15
lua/globals.lua
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
lgi = require('lgi')
|
||||||
|
|
||||||
|
astal = require("astal")
|
||||||
|
|
||||||
|
GLib = astal.GLib
|
||||||
|
Gtk = astal.Gtk
|
||||||
|
Gdk = astal.Gdk
|
||||||
|
Gio = astal.Gio
|
||||||
|
|
||||||
|
Astal = astal.Astal
|
||||||
|
Widget = astal.Widget
|
||||||
|
|
||||||
|
bind = astal.bind
|
||||||
|
|
||||||
|
Variable = astal.Variable
|
23
lua/lib/init.lua
Normal file
23
lua/lib/init.lua
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.map(tbl, f)
|
||||||
|
local t = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
t[k] = f(v)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.sequence(start, stop)
|
||||||
|
local t = {}
|
||||||
|
for i=start,stop do
|
||||||
|
table.insert(t, i)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function ternary(cond, t, f)
|
||||||
|
if cond then return t else return f end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
9
lua/widgets/bar/init.lua
Normal file
9
lua/widgets/bar/init.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
local workspaces <const> = require(... .. '.workspaces')
|
||||||
|
|
||||||
|
return Astal.Window({
|
||||||
|
namespace = "bar",
|
||||||
|
name = "bar",
|
||||||
|
anchor = Astal.WindowAnchor.TOP + Astal.WindowAnchor.LEFT + Astal.WindowAnchor.RIGHT,
|
||||||
|
exclusivity = "EXCLUSIVE",
|
||||||
|
child = workspaces,
|
||||||
|
})
|
48
lua/widgets/bar/workspaces.lua
Normal file
48
lua/widgets/bar/workspaces.lua
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
local Hyprland <const> = astal.require("AstalHyprland")
|
||||||
|
local map, sequence = require('lua.lib').map, require('lua.lib').sequence
|
||||||
|
|
||||||
|
local hypr = Hyprland.get_default()
|
||||||
|
|
||||||
|
local map, sequence = require('lua.lib').map, require('lua.lib').sequence
|
||||||
|
|
||||||
|
local hypr = Hyprland.get_default()
|
||||||
|
|
||||||
|
local function workspace_row(start, stop)
|
||||||
|
return Widget.Box({
|
||||||
|
children = map(sequence(start, stop), function(i)
|
||||||
|
return Widget.Button({
|
||||||
|
setup = function(self)
|
||||||
|
self:hook(hypr, 'notify::focused-workspace', function(self, workspace)
|
||||||
|
self:toggle_class_name('focused', workspace:get_id() == i)
|
||||||
|
end)
|
||||||
|
self:hook(hypr, 'notify::workspaces', function(self, workspaces)
|
||||||
|
map(workspaces, function(workspace)
|
||||||
|
if workspace:get_id() == i then
|
||||||
|
-- ick
|
||||||
|
local count = 0
|
||||||
|
for _ in ipairs(workspace:get_clients()) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
self:toggle_class_name('occupied', workspace:get_id() == i and count > 0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
on_click_release = function()
|
||||||
|
hypr:message_async(string.format("dispatch workspace %d", i))
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return Widget.Box({
|
||||||
|
class_name = 'workspaces',
|
||||||
|
vertical = true,
|
||||||
|
hexpand = false,
|
||||||
|
halign = 'START',
|
||||||
|
children = {
|
||||||
|
workspace_row(1, 5),
|
||||||
|
workspace_row(6, 10),
|
||||||
|
}
|
||||||
|
})
|
@ -1,44 +0,0 @@
|
|||||||
class Brightness extends Service {
|
|
||||||
static {
|
|
||||||
Service.register(
|
|
||||||
this,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
screen: ["float", "rw"],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_screen = 0;
|
|
||||||
|
|
||||||
get screen() {
|
|
||||||
return this._screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
set screen(percent) {
|
|
||||||
if (percent < 0) percent = 0;
|
|
||||||
|
|
||||||
if (percent > 1) percent = 1;
|
|
||||||
|
|
||||||
Utils.execAsync(`brightnessctl s ${percent * 100}% -q`)
|
|
||||||
.then(() => {
|
|
||||||
this._screen = percent;
|
|
||||||
this.changed("screen");
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
try {
|
|
||||||
this._screen =
|
|
||||||
Number(Utils.exec("brightnessctl g")) /
|
|
||||||
Number(Utils.exec("brightnessctl m"));
|
|
||||||
} catch (error) {
|
|
||||||
console.error("missing dependancy: brightnessctl");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = new Brightness();
|
|
||||||
export default service;
|
|
361
services/mpd.js
361
services/mpd.js
@ -1,361 +0,0 @@
|
|||||||
import Gio from "gi://Gio";
|
|
||||||
|
|
||||||
Gio._promisify(Gio.DataInputStream.prototype, "read_line_async");
|
|
||||||
|
|
||||||
class Mpd extends Service {
|
|
||||||
static {
|
|
||||||
Service.register(
|
|
||||||
this,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
//TODO: parse some properties like duration into number?
|
|
||||||
partition: ["string", "r"],
|
|
||||||
volume: ["string", "r"],
|
|
||||||
repeat: ["string", "r"],
|
|
||||||
random: ["string", "r"],
|
|
||||||
single: ["string", "r"],
|
|
||||||
consume: ["string", "r"],
|
|
||||||
playlist: ["string", "r"],
|
|
||||||
playlistlength: ["string", "r"],
|
|
||||||
state: ["string", "r"],
|
|
||||||
song: ["string", "r"],
|
|
||||||
songid: ["string", "r"],
|
|
||||||
nextsong: ["string", "r"],
|
|
||||||
nextsongid: ["string", "r"],
|
|
||||||
elapsed: ["string", "r"],
|
|
||||||
duration: ["string", "r"],
|
|
||||||
bitrate: ["string", "r"],
|
|
||||||
mixrampdb: ["string", "r"],
|
|
||||||
audio: ["string", "r"],
|
|
||||||
|
|
||||||
file: ["string", "r"],
|
|
||||||
"Last-Modified": ["string", "r"],
|
|
||||||
Artist: ["string", "r"],
|
|
||||||
Title: ["string", "r"],
|
|
||||||
Album: ["string", "r"],
|
|
||||||
Pos: ["string", "r"],
|
|
||||||
Id: ["string", "r"],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#socket;
|
|
||||||
|
|
||||||
#inputStream;
|
|
||||||
#outputStream;
|
|
||||||
|
|
||||||
_decoder = new TextDecoder();
|
|
||||||
_encoder = new TextEncoder();
|
|
||||||
_messageHandlerQueue = [];
|
|
||||||
|
|
||||||
//TODO: more properties?
|
|
||||||
|
|
||||||
// Status
|
|
||||||
_partition;
|
|
||||||
_volume;
|
|
||||||
_repeat;
|
|
||||||
_random;
|
|
||||||
_single;
|
|
||||||
_consume;
|
|
||||||
_playlist;
|
|
||||||
_playlistlength;
|
|
||||||
_state;
|
|
||||||
_song;
|
|
||||||
_songid;
|
|
||||||
_nextsong;
|
|
||||||
_nextsongid;
|
|
||||||
_elapsed;
|
|
||||||
_duration;
|
|
||||||
_bitrate;
|
|
||||||
_mixrampdb;
|
|
||||||
_audio;
|
|
||||||
|
|
||||||
_file;
|
|
||||||
_LastModified;
|
|
||||||
_Artist;
|
|
||||||
_Title;
|
|
||||||
_Album;
|
|
||||||
_Pos;
|
|
||||||
_Id;
|
|
||||||
|
|
||||||
get partition() {
|
|
||||||
return this._partition;
|
|
||||||
}
|
|
||||||
|
|
||||||
get volume() {
|
|
||||||
return this._volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
get repeat() {
|
|
||||||
return this._repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
get random() {
|
|
||||||
return this._random;
|
|
||||||
}
|
|
||||||
|
|
||||||
get single() {
|
|
||||||
return this._single;
|
|
||||||
}
|
|
||||||
|
|
||||||
get consume() {
|
|
||||||
return this._consume;
|
|
||||||
}
|
|
||||||
|
|
||||||
get playlist() {
|
|
||||||
return this._playlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
get playlistlength() {
|
|
||||||
return this._playlistlength;
|
|
||||||
}
|
|
||||||
|
|
||||||
get state() {
|
|
||||||
return this._state;
|
|
||||||
}
|
|
||||||
|
|
||||||
get song() {
|
|
||||||
return this._song;
|
|
||||||
}
|
|
||||||
|
|
||||||
get songid() {
|
|
||||||
return this._songid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get nextsong() {
|
|
||||||
return this._nextsong;
|
|
||||||
}
|
|
||||||
|
|
||||||
get nextsongid() {
|
|
||||||
return this._nextsongid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get elapsed() {
|
|
||||||
return this._elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
get duration() {
|
|
||||||
return this._duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
get bitrate() {
|
|
||||||
return this._bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
get mixrampdb() {
|
|
||||||
return this._mixrampdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
get audio() {
|
|
||||||
return this._audio;
|
|
||||||
}
|
|
||||||
|
|
||||||
get file() {
|
|
||||||
return this._file;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Last_Modified() {
|
|
||||||
return this._LastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Artist() {
|
|
||||||
return this._Artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Title() {
|
|
||||||
return this._Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Album() {
|
|
||||||
return this._Album;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Pos() {
|
|
||||||
return this._Pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Id() {
|
|
||||||
return this._Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this._initSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
async _initSocket() {
|
|
||||||
try {
|
|
||||||
this.#socket = new Gio.SocketClient().connect_to_host(
|
|
||||||
"localhost",
|
|
||||||
6600,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.#inputStream = new Gio.DataInputStream({
|
|
||||||
base_stream: this.#socket.get_input_stream(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.#outputStream = new Gio.DataOutputStream({
|
|
||||||
base_stream: this.#socket.get_output_stream(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this._watchSocket();
|
|
||||||
|
|
||||||
//init properties
|
|
||||||
//[TODO): init more properties?
|
|
||||||
|
|
||||||
this.send("status")
|
|
||||||
.then(this._updateProperties.bind(this))
|
|
||||||
.catch(logError);
|
|
||||||
this.send("currentsong")
|
|
||||||
.then(this._updateProperties.bind(this))
|
|
||||||
.catch(logError);
|
|
||||||
} catch (e) {
|
|
||||||
logError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _watchSocket() {
|
|
||||||
let bufferedLines = [];
|
|
||||||
while (true) {
|
|
||||||
const [rawData] = await this.#inputStream.read_line_async(0, null);
|
|
||||||
const data = this._decoder.decode(rawData);
|
|
||||||
if (data == null) continue;
|
|
||||||
bufferedLines.push(data);
|
|
||||||
const { response, remain } = this._parseResponse(bufferedLines);
|
|
||||||
bufferedLines = remain;
|
|
||||||
|
|
||||||
if (!response) continue;
|
|
||||||
switch (response.type) {
|
|
||||||
case "version":
|
|
||||||
console.log(`MPD Server Version ${response.payload}`);
|
|
||||||
break;
|
|
||||||
case "error":
|
|
||||||
this._handleMessage(new Error(response.payload), null);
|
|
||||||
break;
|
|
||||||
case "data":
|
|
||||||
this._handleMessage(null, response.payload);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_parseResponse(lines) {
|
|
||||||
let response;
|
|
||||||
let beginLine = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
const line = lines[i];
|
|
||||||
|
|
||||||
const version = line.match(/^OK MPD (.+)/);
|
|
||||||
const error = line.match(/^ACK \[.*] {.*} (.+)/);
|
|
||||||
|
|
||||||
if (version) {
|
|
||||||
response = { type: "version", payload: version[1] };
|
|
||||||
beginLine = i + 1;
|
|
||||||
} else if (error) {
|
|
||||||
response = { type: "error", payload: error[1] };
|
|
||||||
beginLine = i + 1;
|
|
||||||
} else if (line === "OK") {
|
|
||||||
response = {
|
|
||||||
type: "data",
|
|
||||||
payload: lines.slice(beginLine, i).join("\n"),
|
|
||||||
};
|
|
||||||
beginLine = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { response, remain: lines.slice(beginLine) };
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMessage(err, msg) {
|
|
||||||
const { func } = this._messageHandlerQueue.shift();
|
|
||||||
func(err, msg);
|
|
||||||
if (this._messageHandlerQueue.length === 0) {
|
|
||||||
this._idle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async send(data) {
|
|
||||||
data = data.trim();
|
|
||||||
const isIdle = data === "idle";
|
|
||||||
|
|
||||||
if (this._messageHandlerQueue[0]?.isIdle) {
|
|
||||||
this.#outputStream.write(this._encoder.encode("noidle\n"), null);
|
|
||||||
}
|
|
||||||
this.#outputStream.write(this._encoder.encode(`${data}\n`), null);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this._messageHandlerQueue.push({
|
|
||||||
isIdle,
|
|
||||||
func: (err, msg) => {
|
|
||||||
if (err != null) reject(err);
|
|
||||||
resolve(msg);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_idle() {
|
|
||||||
this.send("idle")
|
|
||||||
.then((msg) => {
|
|
||||||
for (const line of msg.split("\n")) {
|
|
||||||
const subsystem = /changed: (\w+)/.exec(line);
|
|
||||||
if (subsystem == null) continue;
|
|
||||||
|
|
||||||
//TODO: only update those things that could have
|
|
||||||
//changed using a switch over the subsystems
|
|
||||||
this.send("status")
|
|
||||||
.then(this._updateProperties.bind(this))
|
|
||||||
.catch(logError);
|
|
||||||
this.send("currentsong")
|
|
||||||
.then(this._updateProperties.bind(this))
|
|
||||||
.catch(logError);
|
|
||||||
/*
|
|
||||||
switch(subsystem[1]) {
|
|
||||||
case "player":
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(logError);
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateProperties(msg) {
|
|
||||||
for (const line of msg.split("\n")) {
|
|
||||||
const keyValue = line.match(/(.*): (.*)/);
|
|
||||||
if (keyValue == null) continue;
|
|
||||||
const deprecatedKeys = [
|
|
||||||
"time",
|
|
||||||
"Time", //deprecated
|
|
||||||
"Format", //same as audio
|
|
||||||
];
|
|
||||||
if (deprecatedKeys.includes(keyValue[1])) continue;
|
|
||||||
if (!this.hasOwnProperty(`_${keyValue[1]}`)) continue;
|
|
||||||
this.updateProperty(keyValue[1], keyValue[2]);
|
|
||||||
this.emit("changed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCrossfade = (seconds) => this.send(`crossfade ${seconds}`);
|
|
||||||
setVolume = (volume) => this.send(`setvol ${volume}`);
|
|
||||||
|
|
||||||
toggleShuffle = () => this.send(`random ${+this._random ? "0" : "1"}`);
|
|
||||||
toggleRepeat = () => this.send(`repeat ${+this._repeat ? "0" : "1"}`);
|
|
||||||
|
|
||||||
next = () => this.send("next");
|
|
||||||
playPause = () => this.send(`pause ${this._state === "pause" ? "0" : "1"}`);
|
|
||||||
pause = () => this.send("pause 1");
|
|
||||||
play = () => this.send("pause 0");
|
|
||||||
playSong = (songpos) => this.send(`play ${songpos}`);
|
|
||||||
playSongId = (songid) => this.send(`playid ${songid}`);
|
|
||||||
seekSong = (songpos, time) => this.send(`seek ${songpos} ${time}`);
|
|
||||||
seekSongId = (songid, time) => this.send(`seekid ${songid} ${time}`);
|
|
||||||
seekCur = (time) => this.send(`seekcur ${time}`);
|
|
||||||
previous = () => this.send("previous");
|
|
||||||
stop = () => this.send("stop");
|
|
||||||
|
|
||||||
clearQueue = () => this.send("clear");
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = new Mpd;
|
|
||||||
export default service;
|
|
@ -1,68 +0,0 @@
|
|||||||
#status-bar .Menu {
|
|
||||||
background-color: $bg-alt-1;
|
|
||||||
min-width: 640px;
|
|
||||||
|
|
||||||
.mpd-controls {
|
|
||||||
background-color: $bg;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
font-size: 24px;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background-color: $bg-alt-1;
|
|
||||||
border-color: $bg-alt-1;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover {
|
|
||||||
min-width: 250px;
|
|
||||||
min-height: 250px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background: $bg;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
padding-top: 10px;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.position {
|
|
||||||
margin: 10px;
|
|
||||||
margin-right: 20px;
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
trough {
|
|
||||||
background: $bg-alt-1;
|
|
||||||
border-radius: 5px;
|
|
||||||
min-height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
slider {
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: $mpd-progress-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.position-label {
|
|
||||||
margin-top: 75px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dial-parent {
|
|
||||||
@include panel-dial($resource-dial-fg-cpu, $bg);
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
|
|
||||||
.dial-icon {
|
|
||||||
color: $fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
|||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status-bar {
|
|
||||||
background-color: $bg;
|
|
||||||
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
|
|
||||||
.dial-container {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
.battery-dial {
|
|
||||||
@include bar-dial($battery-dial-bg, $battery-dial-fg)
|
|
||||||
}
|
|
||||||
|
|
||||||
.volume-dial {
|
|
||||||
@include bar-dial($volume-dial-bg, $volume-dial-fg)
|
|
||||||
}
|
|
||||||
|
|
||||||
.brightness-dial {
|
|
||||||
@include bar-dial($brightness-dial-bg, $brightness-dial-fg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspace-container {
|
|
||||||
background-color: $bg-alt-1;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 6px 0px;
|
|
||||||
margin: 0px 12px;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.ws-norm {
|
|
||||||
min-width: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 2px 5px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
background-color: $ws-inactive;
|
|
||||||
color: $fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ws-active {
|
|
||||||
color: $bg;
|
|
||||||
background-color: $ws-active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-visibility-button {
|
|
||||||
border-radius: 0px 10px 10px 0px;
|
|
||||||
padding: 10px 0px 10px 2px;
|
|
||||||
background-color: $bg-alt-1
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock {
|
|
||||||
background-color: $bg-alt-1;
|
|
||||||
margin: 8px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 6px 2px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mpd-controls {
|
|
||||||
.button {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 2px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $button-hover-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
.launcher {
|
|
||||||
min-width: 640px;
|
|
||||||
background: $bg-alt-1;
|
|
||||||
|
|
||||||
.search {
|
|
||||||
> image {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> entry {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 10px;
|
|
||||||
background: $bg;
|
|
||||||
border: 5px solid $bg;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry {
|
|
||||||
margin: 0 10px 0 10px;
|
|
||||||
border: 10px solid $bg;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: $bg;
|
|
||||||
|
|
||||||
> box {
|
|
||||||
> image {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px 10px 5px 10px;
|
|
||||||
background: $bg-alt-1;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> label {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> scrolledwindow > * > box:last-child {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
.lock-ready > box{
|
|
||||||
box {
|
|
||||||
background: $bg;
|
|
||||||
padding: 20px;
|
|
||||||
border: 10px solid $hl-alt-1;
|
|
||||||
min-width: 300px;
|
|
||||||
margin: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img {
|
|
||||||
background: url('/home/catalie/.config/wallpaper');
|
|
||||||
min-width: 300px;
|
|
||||||
min-height: 300px;
|
|
||||||
background-position: 25% 12.5%;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
box > entry {
|
|
||||||
background: $bg-alt-1;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 5px;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lock {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
#notifications {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications {
|
|
||||||
opacity: 1;
|
|
||||||
min-width: 24rem;
|
|
||||||
.revealer {
|
|
||||||
+ .revealer {
|
|
||||||
>*>box {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
min-width: 24rem;
|
|
||||||
padding: 0px;
|
|
||||||
border: 1px solid $fg;
|
|
||||||
border-right: none;
|
|
||||||
background: $bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.urgency-indicator {
|
|
||||||
min-width: 15px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
|
|
||||||
&.normal, &.low {
|
|
||||||
background: $hl-alt-2;
|
|
||||||
border: 1px solid $hl-alt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.urgent {
|
|
||||||
background: $hl;
|
|
||||||
border: 1px solid $hl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeout-bar {
|
|
||||||
background: $bg-alt-1;
|
|
||||||
|
|
||||||
> trough > progress {
|
|
||||||
background-image: none;
|
|
||||||
background-color: $hl-alt-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-right: 5px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $hl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon image {
|
|
||||||
font-size: 58px;
|
|
||||||
margin: 5px;
|
|
||||||
color: $fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon box {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 28px;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions .action-button {
|
|
||||||
margin: 0 .4em;
|
|
||||||
margin-top: .8em;
|
|
||||||
border: 2px solid $fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions .action-button:first-child {
|
|
||||||
margin-left: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions .action-button:last-child {
|
|
||||||
margin-right: .5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
$height: 50px;
|
|
||||||
|
|
||||||
#status-bar {
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
.workspaces {
|
|
||||||
min-height: $height;
|
|
||||||
background: $bg;
|
|
||||||
padding: 5px;
|
|
||||||
padding-top: 15px;
|
|
||||||
|
|
||||||
.workspace {
|
|
||||||
min-width: 10px;
|
|
||||||
min-height: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
background-color: $bg-alt-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.occupied {
|
|
||||||
background-color: $bg-alt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.focused {
|
|
||||||
background-color: $hl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-controls {
|
|
||||||
margin-top: 20px;
|
|
||||||
min-width: 220px;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
margin-top: 20px;
|
|
||||||
min-width: 200px;
|
|
||||||
min-height: 2px;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
trough {
|
|
||||||
min-height: 2px;
|
|
||||||
background: $bg-alt-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight {
|
|
||||||
min-height: 2px;
|
|
||||||
background: $mpd-progress-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
min-height: $height;
|
|
||||||
background: $bg;
|
|
||||||
|
|
||||||
|
|
||||||
.battery-container {
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
.battery-dial {
|
|
||||||
@include bar-dial($battery-dial-bg, $battery-dial-fg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sliderbox {
|
|
||||||
margin: 5px;
|
|
||||||
.volume {
|
|
||||||
.slider {
|
|
||||||
trough {
|
|
||||||
min-height: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
background: $bg-alt-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight {
|
|
||||||
background: $hl-alt-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.brightness {
|
|
||||||
.slider {
|
|
||||||
trough {
|
|
||||||
min-height: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
background: $bg-alt-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight {
|
|
||||||
background: $hl-alt-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
separator {
|
|
||||||
background: $bg-alt-1;
|
|
||||||
padding: 1px;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock {
|
|
||||||
background: $bg;
|
|
||||||
|
|
||||||
.datetime {
|
|
||||||
margin: 1px;
|
|
||||||
font-size: 10pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
212
style/css
212
style/css
@ -1,212 +0,0 @@
|
|||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset; }
|
|
||||||
|
|
||||||
#status-bar {
|
|
||||||
background-color: #161616;
|
|
||||||
border-radius: 0 10px 10px 0; }
|
|
||||||
#status-bar .dial-container {
|
|
||||||
margin-top: 10px; }
|
|
||||||
#status-bar .dial-container .battery-dial {
|
|
||||||
color: #33B1FF;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .battery-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .dial-container .volume-dial {
|
|
||||||
color: #33B1FF;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .volume-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .dial-container .brightness-dial {
|
|
||||||
color: #33B1FF;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .brightness-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .workspace-container {
|
|
||||||
background-color: #262626;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 6px 0px;
|
|
||||||
margin: 0px 12px;
|
|
||||||
border: none; }
|
|
||||||
#status-bar .workspace-container .ws-norm {
|
|
||||||
min-width: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 2px 5px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
background-color: #161616;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .workspace-container .ws-active {
|
|
||||||
color: #161616;
|
|
||||||
background-color: #FF7EB6; }
|
|
||||||
#status-bar .menu-visibility-button {
|
|
||||||
border-radius: 0px 10px 10px 0px;
|
|
||||||
padding: 10px 0px 10px 2px;
|
|
||||||
background-color: #262626; }
|
|
||||||
#status-bar .clock {
|
|
||||||
background-color: #262626;
|
|
||||||
margin: 8px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 6px 2px;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .mpd-controls .button {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 2px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-family: 'Symbols Nerd Font Mono'; }
|
|
||||||
#status-bar .mpd-controls .button:hover {
|
|
||||||
color: #FF7EB6; }
|
|
||||||
|
|
||||||
#status-bar .Menu {
|
|
||||||
background-color: #262626;
|
|
||||||
min-width: 640px; }
|
|
||||||
#status-bar .Menu .mpd-controls {
|
|
||||||
background-color: #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .button {
|
|
||||||
font-size: 24px;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background-color: #262626;
|
|
||||||
border-color: #262626;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .cover {
|
|
||||||
min-width: 250px;
|
|
||||||
min-height: 250px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background: #161616;
|
|
||||||
background-size: cover; }
|
|
||||||
#status-bar .Menu .mpd-controls .title {
|
|
||||||
padding-top: 10px;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold; }
|
|
||||||
#status-bar .Menu .mpd-controls .position {
|
|
||||||
margin: 10px;
|
|
||||||
margin-right: 20px;
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px; }
|
|
||||||
#status-bar .Menu .mpd-controls .position trough {
|
|
||||||
background: #262626;
|
|
||||||
border-radius: 5px;
|
|
||||||
min-height: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .position highlight {
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: #FF7Eb6; }
|
|
||||||
#status-bar .Menu .mpd-controls .position-label {
|
|
||||||
margin-top: 75px; }
|
|
||||||
#status-bar .Menu .dial-parent {
|
|
||||||
color: #FF7eb6;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 8px;
|
|
||||||
margin-left: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .Menu .dial-parent .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 32px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .Menu .dial-parent .resource-dial {
|
|
||||||
min-width: 96px;
|
|
||||||
min-height: 96px; }
|
|
||||||
#status-bar .Menu .dial-parent .dial-icon {
|
|
||||||
color: #f2f4f8; }
|
|
||||||
|
|
||||||
#notifications {
|
|
||||||
background: transparent; }
|
|
||||||
|
|
||||||
.notifications {
|
|
||||||
opacity: 1;
|
|
||||||
min-width: 24rem; }
|
|
||||||
.notifications .revealer + .revealer > * > box {
|
|
||||||
margin-top: 0; }
|
|
||||||
.notifications .revealer .notification {
|
|
||||||
margin: 25px;
|
|
||||||
min-width: 24rem;
|
|
||||||
padding: 0px;
|
|
||||||
border: 2px solid #f2f4f8;
|
|
||||||
background: #161616; }
|
|
||||||
.notifications .revealer .notification.critical {
|
|
||||||
border: 2px solid #FF7EB6; }
|
|
||||||
.notifications .revealer .body {
|
|
||||||
margin-right: 1em; }
|
|
||||||
.notifications .revealer .timeout-bar {
|
|
||||||
margin: 5px 0px 0;
|
|
||||||
margin-top: 5px;
|
|
||||||
background: #262626; }
|
|
||||||
.notifications .revealer .timeout-bar > trough > progress {
|
|
||||||
background-image: none;
|
|
||||||
background-color: #33B1FF; }
|
|
||||||
.notifications .revealer .button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-right: 5px; }
|
|
||||||
.notifications .revealer .button:hover {
|
|
||||||
color: #FF7EB6; }
|
|
||||||
.notifications .revealer .icon {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
margin-right: 1em;
|
|
||||||
margin-left: 1em; }
|
|
||||||
.notifications .revealer .icon image {
|
|
||||||
font-size: 58px;
|
|
||||||
margin: 5px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
.notifications .revealer .icon box {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px; }
|
|
||||||
.notifications .revealer .title {
|
|
||||||
font-size: 24px; }
|
|
||||||
.notifications .revealer .actions .action-button {
|
|
||||||
margin: 0 .4em;
|
|
||||||
margin-top: .8em;
|
|
||||||
border: 2px solid #f2f4f8; }
|
|
||||||
.notifications .revealer .actions .action-button:first-child {
|
|
||||||
margin-left: .5em; }
|
|
||||||
.notifications .revealer .actions .action-button:last-child {
|
|
||||||
margin-right: .5em; }
|
|
||||||
|
|
||||||
.launcher {
|
|
||||||
min-width: 640px;
|
|
||||||
background: #262626; }
|
|
||||||
.launcher .search > image {
|
|
||||||
margin-left: 10px; }
|
|
||||||
.launcher .search > entry {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 10px;
|
|
||||||
background: #161616;
|
|
||||||
border: 5px solid #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 32px; }
|
|
||||||
.launcher .entry {
|
|
||||||
margin: 0 10px 0 10px;
|
|
||||||
border: 10px solid #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #161616; }
|
|
||||||
.launcher .entry > box > image {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px 10px 5px 10px;
|
|
||||||
background: #262626;
|
|
||||||
border-radius: 5px; }
|
|
||||||
.launcher > scrolledwindow > * > box:last-child {
|
|
||||||
margin-bottom: 10px; }
|
|
||||||
|
|
||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset; }
|
|
@ -1,27 +0,0 @@
|
|||||||
@mixin dial($_fg, $_bg, $margin, $padding, $font-size, $icon-font-size) {
|
|
||||||
color: $_fg;
|
|
||||||
background-color: $_bg;
|
|
||||||
margin: $margin;
|
|
||||||
padding: $padding;
|
|
||||||
font-size: $font-size;
|
|
||||||
|
|
||||||
.dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: $icon-font-size;
|
|
||||||
color: $icon-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin bar-dial($_fg, $_bg) {
|
|
||||||
@include dial($_fg, $_bg, 0px, 0px, 5px, 16px)
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin panel-dial($_fg, $_bg) {
|
|
||||||
@include dial($_fg, $_bg, 0px, 10px, 8px, 32px);
|
|
||||||
margin-left: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
.resource-dial {
|
|
||||||
min-width: 96px;
|
|
||||||
min-height: 96px;
|
|
||||||
}
|
|
||||||
}
|
|
335
style/style.css
335
style/style.css
@ -1,320 +1,19 @@
|
|||||||
.lock-ready > box box {
|
* {
|
||||||
|
all: unset; }
|
||||||
|
|
||||||
|
bar {
|
||||||
|
background: transparent; }
|
||||||
|
|
||||||
|
box.workspaces {
|
||||||
|
min-height: 50px;
|
||||||
background: #161616;
|
background: #161616;
|
||||||
padding: 20px;
|
padding: 5px; }
|
||||||
border: 10px solid #33B1FF;
|
box.workspaces box > button {
|
||||||
min-width: 300px;
|
min-width: 10px;
|
||||||
margin: 15px; }
|
min-height: 10px;
|
||||||
|
|
||||||
.lock-ready > box .img {
|
|
||||||
background: url("/home/catalie/.config/wallpaper");
|
|
||||||
min-width: 300px;
|
|
||||||
min-height: 300px;
|
|
||||||
background-position: 25% 12.5%;
|
|
||||||
background-size: cover; }
|
|
||||||
|
|
||||||
.lock-ready > box box > entry {
|
|
||||||
background: #262626;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 5px;
|
|
||||||
margin: 10px; }
|
|
||||||
|
|
||||||
.lock {
|
|
||||||
background: transparent; }
|
|
||||||
|
|
||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset; }
|
|
||||||
|
|
||||||
#status-bar {
|
|
||||||
background-color: #161616;
|
|
||||||
border-radius: 0 10px 10px 0; }
|
|
||||||
#status-bar .dial-container {
|
|
||||||
margin-top: 10px; }
|
|
||||||
#status-bar .dial-container .battery-dial {
|
|
||||||
color: #42be65;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .battery-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .dial-container .volume-dial {
|
|
||||||
color: #33B1FF;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .volume-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .dial-container .brightness-dial {
|
|
||||||
color: #33B1FF;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .dial-container .brightness-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .workspace-container {
|
|
||||||
background-color: #262626;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 6px 0px;
|
|
||||||
margin: 0px 12px;
|
|
||||||
border: none; }
|
|
||||||
#status-bar .workspace-container .ws-norm {
|
|
||||||
min-width: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 2px 5px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
background-color: #161616;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .workspace-container .ws-active {
|
|
||||||
color: #161616;
|
|
||||||
background-color: #FF7EB6; }
|
|
||||||
#status-bar .menu-visibility-button {
|
|
||||||
border-radius: 0px 10px 10px 0px;
|
|
||||||
padding: 10px 0px 10px 2px;
|
|
||||||
background-color: #262626; }
|
|
||||||
#status-bar .clock {
|
|
||||||
background-color: #262626;
|
|
||||||
margin: 8px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 6px 2px;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .mpd-controls .button {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 2px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-family: 'Symbols Nerd Font Mono'; }
|
|
||||||
#status-bar .mpd-controls .button:hover {
|
|
||||||
color: #FF7EB6; }
|
|
||||||
|
|
||||||
#status-bar {
|
|
||||||
background: transparent; }
|
|
||||||
#status-bar .workspaces {
|
|
||||||
min-height: 50px;
|
|
||||||
background: #161616;
|
|
||||||
padding: 5px;
|
|
||||||
padding-top: 15px; }
|
|
||||||
#status-bar .workspaces .workspace {
|
|
||||||
min-width: 10px;
|
|
||||||
min-height: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
background-color: #262626; }
|
|
||||||
#status-bar .workspaces .occupied {
|
|
||||||
background-color: #393939; }
|
|
||||||
#status-bar .workspaces .focused {
|
|
||||||
background-color: #FF7EB6; }
|
|
||||||
#status-bar .media-controls {
|
|
||||||
margin-top: 20px;
|
|
||||||
min-width: 220px; }
|
|
||||||
#status-bar .media-controls .button {
|
|
||||||
font-size: 20px; }
|
|
||||||
#status-bar .progress-bar {
|
|
||||||
margin-top: 20px;
|
|
||||||
min-width: 200px;
|
|
||||||
min-height: 2px;
|
|
||||||
background: transparent; }
|
|
||||||
#status-bar .progress-bar trough {
|
|
||||||
min-height: 2px;
|
|
||||||
background: #262626; }
|
|
||||||
#status-bar .progress-bar highlight {
|
|
||||||
min-height: 2px;
|
|
||||||
background: #FF7Eb6; }
|
|
||||||
#status-bar .right {
|
|
||||||
min-height: 50px;
|
|
||||||
background: #161616; }
|
|
||||||
#status-bar .right .battery-container {
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px; }
|
|
||||||
#status-bar .right .battery-container .battery-dial {
|
|
||||||
color: #42be65;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 5px; }
|
|
||||||
#status-bar .right .battery-container .battery-dial .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 16px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .right .sliderbox {
|
|
||||||
margin: 5px; }
|
|
||||||
#status-bar .right .sliderbox .volume .slider trough {
|
|
||||||
min-height: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
background: #262626; }
|
|
||||||
#status-bar .right .sliderbox .volume .slider highlight {
|
|
||||||
background: #33B1FF; }
|
|
||||||
#status-bar .right .sliderbox .volume button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 5px; }
|
|
||||||
#status-bar .right .sliderbox .brightness .slider trough {
|
|
||||||
min-height: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
background: #262626; }
|
|
||||||
#status-bar .right .sliderbox .brightness .slider highlight {
|
|
||||||
background: #33B1FF; }
|
|
||||||
#status-bar .right .sliderbox .brightness button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 5px; }
|
|
||||||
#status-bar .right separator {
|
|
||||||
background: #262626;
|
|
||||||
padding: 1px;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px; }
|
|
||||||
#status-bar .right .clock {
|
|
||||||
background: #161616; }
|
|
||||||
#status-bar .right .clock .datetime {
|
|
||||||
margin: 1px;
|
|
||||||
font-size: 10pt; }
|
|
||||||
|
|
||||||
#status-bar .Menu {
|
|
||||||
background-color: #262626;
|
|
||||||
min-width: 640px; }
|
|
||||||
#status-bar .Menu .mpd-controls {
|
|
||||||
background-color: #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .button {
|
|
||||||
font-size: 24px;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background-color: #262626;
|
|
||||||
border-color: #262626;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .cover {
|
|
||||||
min-width: 250px;
|
|
||||||
min-height: 250px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px;
|
|
||||||
background: #161616;
|
|
||||||
background-size: cover; }
|
|
||||||
#status-bar .Menu .mpd-controls .title {
|
|
||||||
padding-top: 10px;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold; }
|
|
||||||
#status-bar .Menu .mpd-controls .position {
|
|
||||||
margin: 10px;
|
|
||||||
margin-right: 20px;
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px; }
|
|
||||||
#status-bar .Menu .mpd-controls .position trough {
|
|
||||||
background: #262626;
|
|
||||||
border-radius: 5px;
|
|
||||||
min-height: 10px; }
|
|
||||||
#status-bar .Menu .mpd-controls .position slider {
|
|
||||||
min-height: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: #FF7Eb6; }
|
|
||||||
#status-bar .Menu .mpd-controls .position-label {
|
|
||||||
margin-top: 75px; }
|
|
||||||
#status-bar .Menu .dial-parent {
|
|
||||||
color: #FF7eb6;
|
|
||||||
background-color: #161616;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 8px;
|
|
||||||
margin-left: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border-radius: 10px; }
|
|
||||||
#status-bar .Menu .dial-parent .dial-icon {
|
|
||||||
font-family: 'Symbols Nerd Font Mono';
|
|
||||||
font-size: 32px;
|
|
||||||
color: #f2f4f8; }
|
|
||||||
#status-bar .Menu .dial-parent .resource-dial {
|
|
||||||
min-width: 96px;
|
|
||||||
min-height: 96px; }
|
|
||||||
#status-bar .Menu .dial-parent .dial-icon {
|
|
||||||
color: #f2f4f8; }
|
|
||||||
|
|
||||||
#notifications {
|
|
||||||
background: transparent; }
|
|
||||||
|
|
||||||
.notifications {
|
|
||||||
opacity: 1;
|
|
||||||
min-width: 24rem; }
|
|
||||||
.notifications .revealer + .revealer > * > box {
|
|
||||||
margin-top: 0; }
|
|
||||||
.notifications .revealer .notification {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
min-width: 24rem;
|
|
||||||
padding: 0px;
|
|
||||||
border: 1px solid #f2f4f8;
|
|
||||||
border-right: none;
|
|
||||||
background: #161616; }
|
|
||||||
.notifications .revealer .urgency-indicator {
|
|
||||||
min-width: 15px;
|
|
||||||
margin-bottom: 25px; }
|
|
||||||
.notifications .revealer .urgency-indicator.normal, .notifications .revealer .urgency-indicator.low {
|
|
||||||
background: #42be65;
|
|
||||||
border: 1px solid #42be65; }
|
|
||||||
.notifications .revealer .urgency-indicator.urgent {
|
|
||||||
background: #FF7EB6;
|
|
||||||
border: 1px solid #FF7EB6; }
|
|
||||||
.notifications .revealer .body {
|
|
||||||
margin-right: 1em; }
|
|
||||||
.notifications .revealer .timeout-bar {
|
|
||||||
background: #262626; }
|
|
||||||
.notifications .revealer .timeout-bar > trough > progress {
|
|
||||||
background-image: none;
|
|
||||||
background-color: #33B1FF; }
|
|
||||||
.notifications .revealer .button {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-right: 5px; }
|
|
||||||
.notifications .revealer .button:hover {
|
|
||||||
color: #FF7EB6; }
|
|
||||||
.notifications .revealer .icon {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
margin: 5px; }
|
|
||||||
.notifications .revealer .icon image {
|
|
||||||
font-size: 58px;
|
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
color: #f2f4f8; }
|
background-color: #262626; }
|
||||||
.notifications .revealer .icon box {
|
box.workspaces box > button.occupied {
|
||||||
min-width: 68px;
|
background-color: #393939; }
|
||||||
min-height: 68px; }
|
box.workspaces box > button.focused {
|
||||||
.notifications .revealer .title {
|
background-color: #FF7EB6; }
|
||||||
font-size: 28px;
|
|
||||||
padding-right: 5px; }
|
|
||||||
.notifications .revealer .actions .action-button {
|
|
||||||
margin: 0 .4em;
|
|
||||||
margin-top: .8em;
|
|
||||||
border: 2px solid #f2f4f8; }
|
|
||||||
.notifications .revealer .actions .action-button:first-child {
|
|
||||||
margin-left: .5em; }
|
|
||||||
.notifications .revealer .actions .action-button:last-child {
|
|
||||||
margin-right: .5em; }
|
|
||||||
|
|
||||||
.launcher {
|
|
||||||
min-width: 640px;
|
|
||||||
background: #262626; }
|
|
||||||
.launcher .search > image {
|
|
||||||
margin-left: 10px; }
|
|
||||||
.launcher .search > entry {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 10px;
|
|
||||||
background: #161616;
|
|
||||||
border: 5px solid #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 32px; }
|
|
||||||
.launcher .entry {
|
|
||||||
margin: 0 10px 0 10px;
|
|
||||||
border: 10px solid #161616;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #161616; }
|
|
||||||
.launcher .entry > box > image {
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px 10px 5px 10px;
|
|
||||||
background: #262626;
|
|
||||||
border-radius: 5px; }
|
|
||||||
.launcher > scrolledwindow > * > box:last-child {
|
|
||||||
margin-bottom: 10px; }
|
|
||||||
|
|
||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset; }
|
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
@import 'colors.scss';
|
* {
|
||||||
@import 'mixins.scss';
|
all: unset;
|
||||||
|
|
||||||
// components
|
|
||||||
@import './components/lock.scss';
|
|
||||||
@import './components/bar.scss';
|
|
||||||
@import './components/top-bar.scss';
|
|
||||||
@import './components/bar-control-center.scss';
|
|
||||||
@import './components/notification-popups.scss';
|
|
||||||
@import './components/launcher.scss';
|
|
||||||
|
|
||||||
* :not(selection) :not(tooltip) {
|
|
||||||
all: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import 'colors.scss';
|
||||||
|
|
||||||
|
@import './widgets/bar.scss'
|
||||||
|
26
style/widgets/bar.scss
Normal file
26
style/widgets/bar.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$height: 50px;
|
||||||
|
|
||||||
|
bar {
|
||||||
|
background: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
box.workspaces {
|
||||||
|
min-height: $height;
|
||||||
|
background: $bg;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
box > button {
|
||||||
|
min-width: 10px;
|
||||||
|
min-height: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
background-color: $bg-alt-1;
|
||||||
|
|
||||||
|
&.occupied {
|
||||||
|
background-color: $bg-alt-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.focused {
|
||||||
|
background-color: $hl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
export default ({
|
|
||||||
name,
|
|
||||||
child,
|
|
||||||
transition = "slide_up",
|
|
||||||
transitionDuration = 250,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const reveal = Variable(false)
|
|
||||||
const window = Widget.Window({
|
|
||||||
name,
|
|
||||||
visible: false,
|
|
||||||
...props,
|
|
||||||
|
|
||||||
child: Widget.Box({
|
|
||||||
css: `min-height: 2px;
|
|
||||||
min-width: 2px;`,
|
|
||||||
child: Widget.Revealer({
|
|
||||||
transition,
|
|
||||||
transitionDuration,
|
|
||||||
hexpand: true,
|
|
||||||
vexpand: true,
|
|
||||||
child: child,
|
|
||||||
revealChild: reveal.bind()
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return window, reveal;
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
const battery = await Service.import('battery')
|
|
||||||
|
|
||||||
const battery_dial = Widget.CircularProgress({
|
|
||||||
className: 'battery-dial',
|
|
||||||
rounded: false,
|
|
||||||
inverted: false,
|
|
||||||
startAt: 0.75,
|
|
||||||
value: battery.bind('percent').as(p => p / 100),
|
|
||||||
child: Widget.Label({
|
|
||||||
className: "dial-icon",
|
|
||||||
hexpand: true,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(battery, (self) => {
|
|
||||||
console.log(battery)
|
|
||||||
const icons = [
|
|
||||||
["", "", "", "", "", "", "", "", "", "", ""],
|
|
||||||
["", "", "", "", "", "", "", "", "", "", ""],
|
|
||||||
];
|
|
||||||
self.label = icons[Number(battery.charging)][Math.floor(battery.percent / 10)];
|
|
||||||
self.tooltip_text = 'Battery ' + String(battery.percent) + '%';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(battery, (self) => {
|
|
||||||
if (battery.percent <= 30 && battery.charging === false) {
|
|
||||||
self.toggleClassName("battery-low", true);
|
|
||||||
} else {
|
|
||||||
self.toggleClassName("battery-low", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export {
|
|
||||||
battery_dial
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
|||||||
const { exec, execAsync } = Utils;
|
|
||||||
|
|
||||||
import Brightness from '../services/brightness.js'
|
|
||||||
|
|
||||||
const brightness_dial = Widget.EventBox({
|
|
||||||
className: 'eventbox-hide-pointer',
|
|
||||||
'on-primary-click': () => {execAsync('hyprshade toggle blue-light-filter')},
|
|
||||||
'on-scroll-up': () => {Brightness.screen += 0.01},
|
|
||||||
'on-scroll-down': () => {Brightness.screen -= 0.01},
|
|
||||||
child: Widget.CircularProgress({
|
|
||||||
rounded: false,
|
|
||||||
className: 'brightness-dial',
|
|
||||||
inverted: false,
|
|
||||||
startAt: 0.75,
|
|
||||||
value: Brightness.bind('screen'),
|
|
||||||
child: Widget.Label({
|
|
||||||
className: "dial-icon",
|
|
||||||
hexpand: true,
|
|
||||||
hpack: 'center',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Brightness, (self => {
|
|
||||||
const brightness = Brightness.screen * 100;
|
|
||||||
|
|
||||||
self.label = ["", "", "", "", "", "", ""][Math.floor(brightness/15)]
|
|
||||||
self.tooltip_text = `Brightness ${Math.floor(brightness)}%`;
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const brightness_slider = Widget.Box({
|
|
||||||
className: 'brightness',
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
on_clicked: () => execAsync('hyprshade toggle blue-light-filter'),
|
|
||||||
child: Widget.Icon().hook(Brightness, self => {
|
|
||||||
const brightness = Brightness.screen * 100;
|
|
||||||
const icon = [
|
|
||||||
[80, 'display-brightness-high-symbolic'],
|
|
||||||
[50, 'display-brightness-medium-symbolic'],
|
|
||||||
[20, 'display-brightness-low-symbolic'],
|
|
||||||
[0, 'display-brightness-off-symbolic']
|
|
||||||
].find(([threshold]) => brightness >= threshold)?.[1];
|
|
||||||
|
|
||||||
self.icon = icon;
|
|
||||||
self.tooltip_text = `Brightness ${Math.floor(brightness)}%`;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Widget.Slider({
|
|
||||||
className: 'slider',
|
|
||||||
hexpand: true,
|
|
||||||
drawValue: false,
|
|
||||||
onChange: ({ value }) => Brightness.screen = value,
|
|
||||||
value: Brightness.bind('screen'),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
export {
|
|
||||||
brightness_dial,
|
|
||||||
brightness_slider
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
const { exec, execAsync } = Utils;
|
|
||||||
|
|
||||||
const bar_clock = Widget.Box({
|
|
||||||
className: 'clock',
|
|
||||||
vpack: 'end',
|
|
||||||
vertical: true,
|
|
||||||
setup: (self) => {
|
|
||||||
var month_and_date, hours_and_minutes, seconds;
|
|
||||||
self.poll(1000, self => {
|
|
||||||
execAsync("date +'%m/%d %H:%M %S'").then((time) => {
|
|
||||||
[month_and_date, hours_and_minutes, seconds] = time.split(' ');
|
|
||||||
});
|
|
||||||
self.children = [
|
|
||||||
Widget.Label({
|
|
||||||
className: 'datetime',
|
|
||||||
label: month_and_date
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'datetime',
|
|
||||||
label: hours_and_minutes
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'datetime',
|
|
||||||
label: seconds
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const horizontal_clock = Widget.Box({
|
|
||||||
className: 'clock',
|
|
||||||
vpack: 'center',
|
|
||||||
vertical: true,
|
|
||||||
setup: (self) => {
|
|
||||||
var date_time, unix_seconds;
|
|
||||||
self.poll(1000, self => {
|
|
||||||
execAsync("date +'%d %b %H:%M:%S %s'").then((time) => {
|
|
||||||
let parts = time.split(' ');
|
|
||||||
date_time = `${parts[0]} ${parts[1]} ${parts[2]}`;
|
|
||||||
unix_seconds = parts[3];
|
|
||||||
});
|
|
||||||
self.children = [
|
|
||||||
Widget.Label({
|
|
||||||
className: 'datetime',
|
|
||||||
label: date_time
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'datetime',
|
|
||||||
label: unix_seconds
|
|
||||||
})
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export {
|
|
||||||
bar_clock,
|
|
||||||
horizontal_clock
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
const hyprland = await Service.import('hyprland')
|
|
||||||
|
|
||||||
const active_window = Widget.Label({
|
|
||||||
className: 'active-window',
|
|
||||||
label: '',//hyprland.bind('active').as(c => c.client.title),
|
|
||||||
})
|
|
||||||
|
|
||||||
export {
|
|
||||||
active_window
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
const hyprland = await Service.import('hyprland')
|
|
||||||
|
|
||||||
|
|
||||||
const goto_workspace = (ws) => hyprland.messageAsync(`dispatch workspace ${ws}`)
|
|
||||||
|
|
||||||
const hyprworkspaces = Widget.EventBox({
|
|
||||||
onScrollUp: () => goto_workspace('+1'),
|
|
||||||
onScrollDown: () => goto_workspace('-1'),
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'workspace-container',
|
|
||||||
children: Array.from({ length: 10 }, (_, i) => i + 1).map(i => Widget.Button({
|
|
||||||
className: 'ws-norm',
|
|
||||||
attribute: i,
|
|
||||||
child: Widget.Label(String(i)),
|
|
||||||
onClicked: () => goto_workspace(i),
|
|
||||||
setup: self => self.hook(hyprland, self => self.attribute == hyprland.active.workspace.id ?
|
|
||||||
self.toggleClassName('ws-active', true)
|
|
||||||
: self.toggleClassName('ws-active', false))
|
|
||||||
})),
|
|
||||||
|
|
||||||
setup: self => self.hook(hyprland, () => self.children.forEach(btn => {
|
|
||||||
btn.visible = hyprland.workspaces.some(ws => ws.id === btn.attribute);
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
function workspace_row(start, length) {
|
|
||||||
return Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'workspace-row',
|
|
||||||
children: Array.from({ length: length }, (_, i) => i + 1 + start).map(i => Widget.Button({
|
|
||||||
className: 'workspace',
|
|
||||||
attribute: i,
|
|
||||||
onClicked: () => goto_workspace(i),
|
|
||||||
setup: self => {
|
|
||||||
self.hook(hyprland, self => {
|
|
||||||
self.attribute == hyprland.active.workspace.id ? self.toggleClassName('focused', true) : self.toggleClassName('focused', false)
|
|
||||||
hyprland.workspaces.map(w => w.id).includes(self.attribute) ? self.toggleClassName('occupied', true) : self.toggleClassName('occupied', false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const hyprworkspaces_grid = Widget.Box({
|
|
||||||
className: 'workspaces',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
workspace_row(0, 5),
|
|
||||||
workspace_row(5, 5)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export {
|
|
||||||
hyprworkspaces,
|
|
||||||
hyprworkspaces_grid
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
const { Gtk, cairo } = imports.gi;
|
|
||||||
const register = Widget.register;
|
|
||||||
|
|
||||||
class MarqueeLabel extends Gtk.DrawingArea {
|
|
||||||
static {
|
|
||||||
register(this, {
|
|
||||||
properties: {
|
|
||||||
label: ["string", "rw"],
|
|
||||||
"scroll-speed": ["int", "rw"],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#xOffset;
|
|
||||||
#scrollDirection;
|
|
||||||
|
|
||||||
get label() {
|
|
||||||
return this._label;
|
|
||||||
}
|
|
||||||
|
|
||||||
set label(label) {
|
|
||||||
this._label = label;
|
|
||||||
this.queue_draw();
|
|
||||||
this.notify("label");
|
|
||||||
}
|
|
||||||
|
|
||||||
get scroll_speed() {
|
|
||||||
return this._scrollSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
set scroll_speed(speed) {
|
|
||||||
this._scrollSpeed = speed;
|
|
||||||
this.notify("scroll-speed");
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this._reset();
|
|
||||||
|
|
||||||
this.poll(this._scrollSpeed * 11, () => this.queue_draw());
|
|
||||||
|
|
||||||
this.on("size-allocate", () => this._reset());
|
|
||||||
this.on("notify::label", () => this._reset());
|
|
||||||
}
|
|
||||||
|
|
||||||
_reset() {
|
|
||||||
this.#xOffset = 1;
|
|
||||||
this.#scrollDirection = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_draw(cr) {
|
|
||||||
const allocation = this.get_allocation();
|
|
||||||
const styles = this.get_style_context();
|
|
||||||
const width = allocation.width;
|
|
||||||
const height = allocation.height;
|
|
||||||
const color = styles.get_color(Gtk.StateFlags.NORMAL);
|
|
||||||
const [fontFamily] = styles.get_property(
|
|
||||||
"font-family",
|
|
||||||
Gtk.StateFlags.NORMAL,
|
|
||||||
);
|
|
||||||
const fontSize = Math.floor(
|
|
||||||
styles.get_property("font-size", Gtk.StateFlags.NORMAL),
|
|
||||||
);
|
|
||||||
|
|
||||||
cr.setSourceRGB(color.red, color.green, color.blue);
|
|
||||||
cr.selectFontFace(fontFamily, null, null);
|
|
||||||
cr.setFontSize(fontSize);
|
|
||||||
|
|
||||||
const labelWidth = cr.textExtents(this._label).width;
|
|
||||||
|
|
||||||
if (labelWidth > width) {
|
|
||||||
this.#xOffset += this.#scrollDirection * this._scrollSpeed;
|
|
||||||
|
|
||||||
if (this.#xOffset >= 1 || this.#xOffset <= width - labelWidth) {
|
|
||||||
this.#scrollDirection *= 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.#xOffset = (width - labelWidth) / 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
cr.moveTo(this.#xOffset*1.475, fontSize);
|
|
||||||
cr.showText(this._label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MarqueeLabel;
|
|
182
widgets/mpd.js
182
widgets/mpd.js
@ -1,182 +0,0 @@
|
|||||||
const { Gtk } = imports.gi;
|
|
||||||
|
|
||||||
import Mpd from '../services/mpd.js';
|
|
||||||
import MarqueeLabel from './marqueeLabel.js'
|
|
||||||
|
|
||||||
const Mpris = await Service.import("mpris");
|
|
||||||
|
|
||||||
const AspectFrame = Widget.subclass(Gtk.AspectFrame);
|
|
||||||
|
|
||||||
function lengthString(length) {
|
|
||||||
return (
|
|
||||||
`${Math.floor(length / 60)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}:` +
|
|
||||||
`${Math.floor(length % 60)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const albumCover = Widget.Box({
|
|
||||||
className: "cover",
|
|
||||||
setup: (self) => self.hook(Mpris, () => {
|
|
||||||
const mpd = Mpris.getPlayer("mpd");
|
|
||||||
self.css = `background-image: url("${mpd?.coverPath}");`;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const positionLabel = Widget.Label({
|
|
||||||
className: 'position-label',
|
|
||||||
setup: (self) => self.poll(500, () => {
|
|
||||||
Mpd.send("status")
|
|
||||||
.then((msg) => {
|
|
||||||
const elapsed = msg?.match(/elapsed: (\d+\.\d+)/)?.[1];
|
|
||||||
self.label = `${lengthString(elapsed || 0)} / ${lengthString(Mpd.duration || 0)}`;
|
|
||||||
})
|
|
||||||
.catch((error) => logError(error));
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const positionSlider = Widget.Slider({
|
|
||||||
className: 'position',
|
|
||||||
vpack: 'end',
|
|
||||||
drawValue: false,
|
|
||||||
onChange: ({ value }) => {
|
|
||||||
Mpd.seekCur(value * Mpd.duration);
|
|
||||||
},
|
|
||||||
setup: (self) => {
|
|
||||||
self.poll(500, () => {
|
|
||||||
Mpd.send("status")
|
|
||||||
.then((msg) => {
|
|
||||||
const elapsed = msg?.match(/elapsed: (\d+\.\d+)/)?.[1];
|
|
||||||
self.value = elapsed / Mpd.duration || 0;
|
|
||||||
})
|
|
||||||
.catch((error) => logError(error));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const songTitle = Widget.Box({
|
|
||||||
className: 'title',
|
|
||||||
children: [
|
|
||||||
new MarqueeLabel({
|
|
||||||
heightRequest: 30,
|
|
||||||
widthRequest: 350,
|
|
||||||
scrollSpeed: 1,
|
|
||||||
label: 'No Title',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Mpd, () => {
|
|
||||||
self.label = `${Mpd.Title || "No Title"}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const songArtist = Widget.Box({
|
|
||||||
className: 'artist',
|
|
||||||
children: [
|
|
||||||
new MarqueeLabel({
|
|
||||||
heightRequest: 30,
|
|
||||||
widthRequest: 350,
|
|
||||||
scrollSpeed: 1,
|
|
||||||
label: 'No Artist',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Mpd, () => {
|
|
||||||
self.label = `${Mpd.Artist || "No Artist"}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const mediaControls = Widget.Box()
|
|
||||||
|
|
||||||
const mpd_controls = () => Widget.CenterBox({
|
|
||||||
className: 'mpd-controls',
|
|
||||||
hpack: 'center',
|
|
||||||
hexpand: true,
|
|
||||||
startWidget: Widget.Button({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => Mpd.previous(),
|
|
||||||
child: Widget.Icon('media-skip-backward-symbolic')
|
|
||||||
}),
|
|
||||||
centerWidget: Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => Mpd.playPause(),
|
|
||||||
child: Widget.Icon({
|
|
||||||
setup: self => self.hook(Mpd, () => (Mpd.state === 'play')
|
|
||||||
? self.icon = 'media-playback-pause-symbolic'
|
|
||||||
: self.icon = 'media-playback-start-symbolic')
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
endWidget: Widget.Button({
|
|
||||||
hpack: 'end',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => Mpd.next(),
|
|
||||||
child: Widget.Icon('media-skip-forward-symbolic')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mpd_bar_controls = mpd_controls()
|
|
||||||
|
|
||||||
export const mpd_menu_controls = Widget.Box({
|
|
||||||
className: 'mpd-controls',
|
|
||||||
children: [
|
|
||||||
albumCover,
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
hpack: 'end',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
songTitle,
|
|
||||||
songArtist
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
mpd_controls(),
|
|
||||||
positionLabel,
|
|
||||||
positionSlider
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export const cover_with_controls = Widget.Box({
|
|
||||||
className: 'cover',
|
|
||||||
hexpand: true,
|
|
||||||
vexpand: true,
|
|
||||||
child: Widget.Box({
|
|
||||||
className: "cover",
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
mpd_controls(),
|
|
||||||
Widget.Slider({
|
|
||||||
vpack: 'end',
|
|
||||||
className: 'progress-bar',
|
|
||||||
drawValue: false,
|
|
||||||
hexpand: false,
|
|
||||||
onChange: ({ value }) => Mpd.seekCur(value * Mpd.duration),
|
|
||||||
setup: (self) => {
|
|
||||||
self.poll(500, () => {
|
|
||||||
Mpd.send("status")
|
|
||||||
.then((msg) => {
|
|
||||||
const elapsed = msg?.match(/elapsed: (\d+\.\d+)/)?.[1];
|
|
||||||
self.value = elapsed / Mpd.duration || 0;
|
|
||||||
})
|
|
||||||
.catch((error) => logError(error));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: (self) =>
|
|
||||||
self.hook(Mpris, () => {
|
|
||||||
const mpd = Mpris.getPlayer("mpd");
|
|
||||||
self.css = `background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url("${mpd?.coverPath}"); min-height: 60px; min-width: 250px; background-position: 50% 50%; background-size: cover; border: 5px solid #161616`;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
114
widgets/mpris.js
114
widgets/mpris.js
@ -1,114 +0,0 @@
|
|||||||
const { Gtk, Gdk } = imports.gi;
|
|
||||||
const Mpris = await Service.import('mpris')
|
|
||||||
|
|
||||||
const media_controls = (player) => Widget.CenterBox({
|
|
||||||
className: 'media-controls',
|
|
||||||
hpack: 'center',
|
|
||||||
hexpand: true,
|
|
||||||
startWidget: Widget.Button({
|
|
||||||
hpack: 'start',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => player.previous(),
|
|
||||||
child: Widget.Icon('media-skip-backward-symbolic')
|
|
||||||
}),
|
|
||||||
centerWidget: Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => player.playPause(),
|
|
||||||
child: Widget.Icon({
|
|
||||||
setup: self => self.hook(Mpris, () => (player.playBackStatus === 'Playing')
|
|
||||||
? self.icon = 'media-playback-pause-symbolic'
|
|
||||||
: self.icon = 'media-playback-start-symbolic')
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
endWidget: Widget.Button({
|
|
||||||
hpack: 'end',
|
|
||||||
className: 'button',
|
|
||||||
onClicked: () => player.next(),
|
|
||||||
child: Widget.Icon('media-skip-forward-symbolic')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const cover_with_controls = (player) => Widget.Box({
|
|
||||||
className: "media",
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
media_controls(player),
|
|
||||||
Widget.Slider({
|
|
||||||
vpack: 'end',
|
|
||||||
className: 'progress-bar',
|
|
||||||
drawValue: false,
|
|
||||||
hexpand: false,
|
|
||||||
onChange: ({ value }) => {player.position = (value * player.length)},
|
|
||||||
setup: self => self.poll(500, self => {
|
|
||||||
if (!player) return
|
|
||||||
self.value = player.position/player.length
|
|
||||||
})
|
|
||||||
})
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Mpris, () => {
|
|
||||||
self.queue_draw()
|
|
||||||
self.css = `background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url("${player.coverPath}"); min-height: 60px; min-width: 250px; background-position: 50% 50%; background-size: cover; border: 5px solid #161616`;
|
|
||||||
})
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
export const players = Widget.Stack({
|
|
||||||
transition: "slide_up_down",
|
|
||||||
transitionDuration: 125,
|
|
||||||
children: {},
|
|
||||||
setup: (self) => {
|
|
||||||
self.add_events(Gdk.EventMask.SCROLL_MASK);
|
|
||||||
self.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK);
|
|
||||||
|
|
||||||
let currentDeltaY = 0;
|
|
||||||
|
|
||||||
self.on("scroll-event", (_, event) => {
|
|
||||||
const childNames = Object.keys(self.children);
|
|
||||||
|
|
||||||
const length = Object.keys(self.children).length
|
|
||||||
|
|
||||||
const prevChild = childNames[((n)=>n>=0?n:length-1)((childNames.indexOf(self.get_visible_child_name()) - 1) % length)];
|
|
||||||
const nextChild = childNames[(childNames.indexOf(self.get_visible_child_name()) + 1) % length];
|
|
||||||
|
|
||||||
const deltaY = event.get_scroll_deltas()[2];
|
|
||||||
|
|
||||||
currentDeltaY += deltaY;
|
|
||||||
|
|
||||||
if (currentDeltaY > 10 && prevChild) {
|
|
||||||
self.set_visible_child_name(prevChild);
|
|
||||||
currentDeltaY = 0;
|
|
||||||
}
|
|
||||||
if (currentDeltaY < -10 && nextChild) {
|
|
||||||
self.set_visible_child_name(nextChild);
|
|
||||||
currentDeltaY = 0;
|
|
||||||
}
|
|
||||||
console.log(self.children)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.hook(Mpris, (_, name) => {
|
|
||||||
if (!name) return;
|
|
||||||
self.add_named(cover_with_controls(Mpris.getPlayer(name)), name);
|
|
||||||
}, "player-added");
|
|
||||||
self.hook(Mpris, (_, name) => {
|
|
||||||
if (!name) return;
|
|
||||||
|
|
||||||
self.get_child_by_name(name).destroy();
|
|
||||||
delete children[name]
|
|
||||||
console.log('destroyed')
|
|
||||||
}, "player-closed");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const media = Widget.Revealer({
|
|
||||||
revealChild: Mpris.bind("players").as((players) => players.length > 0),
|
|
||||||
transition: "slide_up",
|
|
||||||
transitionDuration: 125,
|
|
||||||
child: players,
|
|
||||||
});
|
|
@ -1,24 +0,0 @@
|
|||||||
const { Gio, GioUnix } = imports.gi;
|
|
||||||
|
|
||||||
export const resource_dial = (label, icon, timeout, command, transformer) => {
|
|
||||||
let poll = Variable(100, {
|
|
||||||
poll: [timeout, command, v => Number(v)]
|
|
||||||
})
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
className: 'dial-parent',
|
|
||||||
children: [
|
|
||||||
Widget.CircularProgress({
|
|
||||||
startAt: 0.75,
|
|
||||||
className: 'resource-dial',
|
|
||||||
value: poll.bind().as(v => transformer(v)),
|
|
||||||
tooltipText: poll.bind().as(v => label(v)),
|
|
||||||
child: Widget.Icon({
|
|
||||||
className: 'dial-icon',
|
|
||||||
icon: icon
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
const systemtray = await Service.import('systemtray')
|
|
||||||
|
|
||||||
const SysTrayItem = item => Widget.Button({
|
|
||||||
child: Widget.Icon().bind('icon', item, 'icon'),
|
|
||||||
tooltipMarkup: item.bind('tooltip_markup'),
|
|
||||||
onPrimaryClick: (_, event) => item.activate(event),
|
|
||||||
onSecondaryClick: (_, event) => item.openMenu(event),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const systray = Widget.Box({
|
|
||||||
className: 'systray',
|
|
||||||
vertical: true,
|
|
||||||
children: systemtray.bind('items').as(i => i.map(SysTrayItem))
|
|
||||||
})
|
|
@ -1,70 +0,0 @@
|
|||||||
const { exec, execAsync } = Utils
|
|
||||||
|
|
||||||
const audio = await Service.import('audio')
|
|
||||||
|
|
||||||
const volume_dial = Widget.EventBox({
|
|
||||||
className: 'eventbox-hide-pointer',
|
|
||||||
'on-scroll-up': () => {audio.speaker.volume += 0.01},
|
|
||||||
'on-scroll-down': () => {audio.speaker.volume -= 0.01},
|
|
||||||
'on-primary-click': () => {audio.speaker.is_muted = !audio.speaker.is_muted},
|
|
||||||
child: Widget.CircularProgress({
|
|
||||||
className: 'volume-dial',
|
|
||||||
rounded: false,
|
|
||||||
inverted: false,
|
|
||||||
startAt: 0.75,
|
|
||||||
value: audio.speaker.bind('volume'),
|
|
||||||
child: Widget.Icon({
|
|
||||||
className: "dial-icon",
|
|
||||||
hexpand: true,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(audio, (self => {
|
|
||||||
const vol = audio.speaker.volume * 100;
|
|
||||||
const icon = [
|
|
||||||
[101, 'overamplified'],
|
|
||||||
[67, 'high'],
|
|
||||||
[34, 'medium'],
|
|
||||||
[1, 'low'],
|
|
||||||
[0, 'muted'],
|
|
||||||
].find(([threshold]) => threshold <= vol)?.[1];
|
|
||||||
|
|
||||||
self.icon = `audio-volume-${icon}-symbolic`;
|
|
||||||
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const volume_slider = Widget.Box({
|
|
||||||
className: 'volume',
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
on_clicked: () => audio.speaker.is_muted = !audio.speaker.is_muted,
|
|
||||||
child: Widget.Icon().hook(audio.speaker, self => {
|
|
||||||
const vol = audio.speaker.volume * 100;
|
|
||||||
const icon = [
|
|
||||||
[101, 'overamplified'],
|
|
||||||
[67, 'high'],
|
|
||||||
[34, 'medium'],
|
|
||||||
[1, 'low'],
|
|
||||||
[0, 'muted'],
|
|
||||||
].find(([threshold]) => threshold <= vol)?.[1];
|
|
||||||
|
|
||||||
self.icon = `audio-volume-${icon}-symbolic`;
|
|
||||||
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Widget.Slider({
|
|
||||||
className: 'slider',
|
|
||||||
hexpand: true,
|
|
||||||
drawValue: false,
|
|
||||||
onChange: ({ value }) => audio['speaker'].volume = value,
|
|
||||||
value: audio['speaker'].bind('volume'),
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export {
|
|
||||||
volume_dial,
|
|
||||||
volume_slider
|
|
||||||
}
|
|
141
windows/bar.js
141
windows/bar.js
@ -1,141 +0,0 @@
|
|||||||
import { battery_dial } from '../widgets/battery.js'
|
|
||||||
import { volume_dial } from '../widgets/volume.js'
|
|
||||||
import { brightness_dial } from '../widgets/brightness.js'
|
|
||||||
import { hyprworkspaces } from '../widgets/hyprworkspaces.js'
|
|
||||||
import { mpd_bar_controls } from '../widgets/mpd.js'
|
|
||||||
// import { systray } from '../widgets/systray.js'
|
|
||||||
|
|
||||||
import { bar_clock} from '../widgets/clock.js'
|
|
||||||
|
|
||||||
import { MenuWidget } from './menu.js'
|
|
||||||
import { reveal_launcher, launcher } from './launcher.js'
|
|
||||||
|
|
||||||
export const FakeBar = Widget.Window({
|
|
||||||
name: 'FakeBar',
|
|
||||||
exclusivity: 'exclusive',
|
|
||||||
anchor: ['left'],
|
|
||||||
margins: [0, 35],
|
|
||||||
child: Widget.Box({css: 'min-width: 1px;'})
|
|
||||||
})
|
|
||||||
|
|
||||||
let reveal_menu = Variable(false)
|
|
||||||
let reveal_menu_button = Variable(false)
|
|
||||||
|
|
||||||
const MenuRevealButton = Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
vexpand: false,
|
|
||||||
vpack: 'center',
|
|
||||||
className: 'menu-visibility-button',
|
|
||||||
'on-primary-click': (self) => reveal_menu.value = !reveal_menu.value,
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: reveal_menu.bind().as(reveal => !reveal ? 'go-next-symbolic' : 'go-previous-symbolic')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarTopWidget = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
vpack: 'start',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'dial-container',
|
|
||||||
vertical: true,
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
battery_dial,
|
|
||||||
volume_dial,
|
|
||||||
brightness_dial
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
css: 'margin-left: 35px; margin-right: 35px'
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarMiddleWidget = Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.EventBox({
|
|
||||||
'on-hover': () => reveal_menu_button.value = true,
|
|
||||||
child: Widget.Box({
|
|
||||||
css: 'min-width: 1px; min-height: 40px;'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
hyprworkspaces
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarEndWidget = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
vpack: 'end',
|
|
||||||
children: [
|
|
||||||
// systray,
|
|
||||||
mpd_bar_controls,
|
|
||||||
bar_clock
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const BarWidget = Widget.CenterBox({
|
|
||||||
homogeneous: false,
|
|
||||||
vertical: true,
|
|
||||||
spacing: 10,
|
|
||||||
startWidget: BarTopWidget,
|
|
||||||
centerWidget: BarMiddleWidget,
|
|
||||||
endWidget: BarEndWidget
|
|
||||||
})
|
|
||||||
|
|
||||||
const MenuButtonRevealer = Widget.Revealer({
|
|
||||||
revealChild: reveal_menu_button.bind(),
|
|
||||||
transition: 'slide_right',
|
|
||||||
child: MenuRevealButton
|
|
||||||
})
|
|
||||||
|
|
||||||
const MenuRevealer = Widget.Revealer({
|
|
||||||
revealChild: reveal_menu.bind(),
|
|
||||||
transition: 'slide_right',
|
|
||||||
transitionDuration: 500,
|
|
||||||
child: MenuWidget,
|
|
||||||
})
|
|
||||||
|
|
||||||
const LauncherRevealer = Widget.Revealer({
|
|
||||||
revealChild: reveal_launcher.bind(),
|
|
||||||
transition: 'slide_right',
|
|
||||||
transitionDuration: 500,
|
|
||||||
child: launcher,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Bar = Widget.Window({
|
|
||||||
name: 'status-bar',
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
keymode: reveal_launcher.bind().as(v => v ? 'exclusive' : 'on-demand'),
|
|
||||||
anchor: ['top', 'left', 'bottom'],
|
|
||||||
child: Widget.Overlay({
|
|
||||||
'pass-through': true,
|
|
||||||
overlay: Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.EventBox({
|
|
||||||
'on-hover': () => reveal_menu_button.value = true,
|
|
||||||
'on-hover-lost': () => reveal_menu_button.value = false,
|
|
||||||
child: Widget.Box({
|
|
||||||
css: 'margin-right: 4px;',
|
|
||||||
children: [MenuButtonRevealer]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
LauncherRevealer,
|
|
||||||
MenuRevealer,
|
|
||||||
BarWidget
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export {
|
|
||||||
reveal_menu,
|
|
||||||
reveal_launcher
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
const Applications = await Service.import("applications")
|
|
||||||
|
|
||||||
const reveal_launcher = Variable(false)
|
|
||||||
|
|
||||||
function appItem(app) {
|
|
||||||
return Widget.Button({
|
|
||||||
className: 'entry',
|
|
||||||
onClicked: () => {
|
|
||||||
reveal_launcher.value = false
|
|
||||||
app.launch()
|
|
||||||
},
|
|
||||||
attribute: { app },
|
|
||||||
child: Widget.Box([
|
|
||||||
Widget.Icon({
|
|
||||||
icon: app.icon_name || '',
|
|
||||||
size: 42,
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: 'app-title',
|
|
||||||
label: app.name,
|
|
||||||
xalign: 0,
|
|
||||||
vpack: 'center',
|
|
||||||
truncate: 'end'
|
|
||||||
})
|
|
||||||
])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function _launcher() {
|
|
||||||
let applications = Applications.query('').map(appItem)
|
|
||||||
|
|
||||||
const list = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: applications,
|
|
||||||
spacing: 12
|
|
||||||
})
|
|
||||||
|
|
||||||
function repopulate() {
|
|
||||||
applications = Applications.query('').map(appItem)
|
|
||||||
list.children = applications
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = Widget.Box({
|
|
||||||
className: 'search',
|
|
||||||
children: [
|
|
||||||
Widget.Icon({
|
|
||||||
icon: 'edit-find-symbolic',
|
|
||||||
size: 42,
|
|
||||||
}),
|
|
||||||
Widget.Entry({
|
|
||||||
hexpand: true,
|
|
||||||
className: 'search',
|
|
||||||
on_accept: () => {
|
|
||||||
applications.filter((item) => item.visible)[0]?.attribute.app.launch()
|
|
||||||
reveal_launcher.value = false
|
|
||||||
},
|
|
||||||
on_change: ({ text }) => applications.forEach(item => {
|
|
||||||
item.visible = item.attribute.app.match(text ?? '')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'launcher',
|
|
||||||
children: [
|
|
||||||
entry,
|
|
||||||
Widget.Scrollable({
|
|
||||||
hscroll: 'never',
|
|
||||||
vexpand: true,
|
|
||||||
hexpand: true,
|
|
||||||
child: list
|
|
||||||
})
|
|
||||||
],
|
|
||||||
setup: self => self.hook(reveal_launcher, () => {
|
|
||||||
entry.text = ''
|
|
||||||
if (reveal_launcher.value) {
|
|
||||||
repopulate()
|
|
||||||
console.log('nya')
|
|
||||||
entry.text = ''
|
|
||||||
entry.grab_focus()
|
|
||||||
}
|
|
||||||
}, "changed")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const launcher = _launcher()
|
|
||||||
|
|
||||||
export {
|
|
||||||
reveal_launcher,
|
|
||||||
launcher
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
import { show_notification_popups } from './notifications.js'
|
|
||||||
|
|
||||||
// TODO: const { Gdk, GtkSessionLock } = imports.gi;
|
|
||||||
const { authenticateUser, exec } = Utils
|
|
||||||
|
|
||||||
export const show_lock = Variable(false)
|
|
||||||
const lock_ready = Variable(false)
|
|
||||||
|
|
||||||
const username = Variable()
|
|
||||||
|
|
||||||
const password_entry = Widget.Entry({
|
|
||||||
visibility: false,
|
|
||||||
xalign: 0.5,
|
|
||||||
onAccept: (self) => {
|
|
||||||
authenticateUser(username.value, self.text)
|
|
||||||
.then(() => {
|
|
||||||
show_lock.value = false
|
|
||||||
exec('rm /tmp/lock-pixelated.png')
|
|
||||||
show_notification_popups.value = true
|
|
||||||
})
|
|
||||||
.catch(() => self.text = '')
|
|
||||||
self.text = ''
|
|
||||||
username_entry.text = ''
|
|
||||||
username_entry.grab_focus()
|
|
||||||
},
|
|
||||||
setup: self => self.text = ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const username_entry = Widget.Entry({
|
|
||||||
xalign: 0.5,
|
|
||||||
onAccept: (self) => {
|
|
||||||
username.value = self.text
|
|
||||||
password_entry.grab_focus()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const login_container = Widget.Box({
|
|
||||||
child: Widget.Box({
|
|
||||||
className: lock_ready.bind().as(b => b ? 'lock-ready' : 'lock'),
|
|
||||||
vpack: 'center',
|
|
||||||
hpack: 'center',
|
|
||||||
hexpand: 'true',
|
|
||||||
vertical: true,
|
|
||||||
child: Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({className: 'img'}),
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
username_entry,
|
|
||||||
password_entry
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
setup: self => self.hook(show_lock, () => {
|
|
||||||
self.css = 'min-width: 2560px; min-height: 1600px; background: url("/tmp/lock-pixelated.png");'
|
|
||||||
lock_ready.value = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Lock = Widget.Window({
|
|
||||||
name: 'lock',
|
|
||||||
visible: show_lock.bind().as(b => {
|
|
||||||
if (b) {
|
|
||||||
exec(`bash -c "grim - | convert - -scale 12.5% -scale 800% -filter point /tmp/lock-pixelated.png"`)
|
|
||||||
show_notification_popups.value = false
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}),
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
keymode: 'exclusive',
|
|
||||||
child: login_container,
|
|
||||||
})
|
|
@ -1,49 +0,0 @@
|
|||||||
import { mpd_menu_controls } from '../widgets/mpd.js'
|
|
||||||
import { resource_dial } from '../widgets/resourceDial.js'
|
|
||||||
|
|
||||||
export const MenuWidget = Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
className: 'Menu',
|
|
||||||
children: [
|
|
||||||
mpd_menu_controls,
|
|
||||||
Widget.Box({
|
|
||||||
children: [
|
|
||||||
resource_dial(
|
|
||||||
(v) => `cpu: ${v}%`,
|
|
||||||
'microchip-solid',
|
|
||||||
1000,
|
|
||||||
"bash -c \"top -bn1 | grep 'Cpu(s)' | awk '{print \$2 + \$4}'\"",
|
|
||||||
v => v/100
|
|
||||||
),
|
|
||||||
resource_dial(
|
|
||||||
(v) => `mem: ${Math.round(100*v/31396)}%`,
|
|
||||||
'memory-solid',
|
|
||||||
1000,
|
|
||||||
"bash -c \"free -m | grep Mem | awk '{print $3}'\"",
|
|
||||||
v => v/31396
|
|
||||||
),
|
|
||||||
resource_dial(
|
|
||||||
(v) => `igpu: ${v}%`,
|
|
||||||
'expansion-card',
|
|
||||||
1000,
|
|
||||||
"bash -c \"cat /sys/class/drm/card1/device/gpu_busy_percent\"",
|
|
||||||
v => v/100
|
|
||||||
),
|
|
||||||
resource_dial(
|
|
||||||
(v) => `fan: ${v}rpm`,
|
|
||||||
'fan',
|
|
||||||
1000,
|
|
||||||
"bash -c \"sudo ectool pwmgetfanrpm | awk '{a+=\$4} END {print a/2}'\"",
|
|
||||||
v => v/5000
|
|
||||||
),
|
|
||||||
resource_dial(
|
|
||||||
(v) => `temp (cpu): ${v}`,
|
|
||||||
'thermometer',
|
|
||||||
1000,
|
|
||||||
"bash -c \"sudo ectool temps all | grep -E 'cpu' | awk '{print \$5}'\"",
|
|
||||||
v => v/100
|
|
||||||
),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
@ -1,185 +0,0 @@
|
|||||||
const Notifications = await Service.import('notifications')
|
|
||||||
const { Pango } = imports.gi;
|
|
||||||
|
|
||||||
Notifications.popupTimeout = 3000;
|
|
||||||
|
|
||||||
function notification_icon({ app_entry, app_icon, image }) {
|
|
||||||
if (image) {
|
|
||||||
return Widget.Box({
|
|
||||||
css: `background-image: url("${image}");`
|
|
||||||
+ 'background-size: contain;'
|
|
||||||
+ 'background-repeat: no-repeat;'
|
|
||||||
+ 'background-position: center;',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon = 'dialog-information-symbolic'
|
|
||||||
if (Utils.lookUpIcon(app_icon))
|
|
||||||
icon = app_icon
|
|
||||||
|
|
||||||
if (app_entry && Utils.lookUpIcon(app_entry))
|
|
||||||
icon = app_entry
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
child: Widget.Icon(icon),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const notification = (n) => {
|
|
||||||
const icon = Widget.Box({
|
|
||||||
vpack: 'center',
|
|
||||||
hpack: 'start',
|
|
||||||
class_name: 'icon',
|
|
||||||
child: notification_icon(n),
|
|
||||||
})
|
|
||||||
|
|
||||||
const title = Widget.Label({
|
|
||||||
className: 'title',
|
|
||||||
label: n.summary,
|
|
||||||
xalign: 0,
|
|
||||||
maxWidthChars: 40,
|
|
||||||
justify: 'left',
|
|
||||||
})
|
|
||||||
|
|
||||||
const body = Widget.Label({
|
|
||||||
className: 'body',
|
|
||||||
label: n.body,
|
|
||||||
xalign: 0,
|
|
||||||
justification: 'left',
|
|
||||||
maxWidthChars: 26,
|
|
||||||
wrap: true,
|
|
||||||
wrapMode: Pango.WrapMode.WORD_CHAR,
|
|
||||||
useMarkup: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = Widget.Box({
|
|
||||||
class_name: "actions",
|
|
||||||
children: n.actions.map(({ id, label }) => Widget.Button({
|
|
||||||
class_name: "action-button",
|
|
||||||
on_clicked: () => {
|
|
||||||
n.invoke(id)
|
|
||||||
n.dismiss()
|
|
||||||
},
|
|
||||||
hexpand: true,
|
|
||||||
child: Widget.Label(label),
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
// const buttons = Widget.Box({
|
|
||||||
// hpack: 'end',
|
|
||||||
// children: [
|
|
||||||
// Widget.Button({
|
|
||||||
// className: 'button',
|
|
||||||
// onClicked: () => n.dismiss(),
|
|
||||||
// child: Widget.Label('')
|
|
||||||
// })
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
|
|
||||||
const timeout_progress = Widget.ProgressBar({
|
|
||||||
className: 'timeout-bar',
|
|
||||||
hexpand: true,
|
|
||||||
vpack: 'end',
|
|
||||||
value: 1,
|
|
||||||
setup: (self) => {
|
|
||||||
self.poll(n.timeout/100, () => {
|
|
||||||
if (self.value > 0.01) {
|
|
||||||
self.value = self.value - .01
|
|
||||||
}
|
|
||||||
else { n.dismiss() } // Notifications.forceTimeout doesn't work for notifications that are bugged.
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const layout = Widget.Button({
|
|
||||||
child: Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'notification',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box([
|
|
||||||
icon,
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
title, body
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
actions,
|
|
||||||
timeout_progress
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
vexpand: false,
|
|
||||||
classNames: ['urgency-indicator', n.urgency]
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
onPrimaryClick: n.dismiss,
|
|
||||||
})
|
|
||||||
|
|
||||||
return Widget.Revealer({
|
|
||||||
className: 'revealer',
|
|
||||||
attribute: { id: n.id },
|
|
||||||
vpack: 'start',
|
|
||||||
transition: 'slide_down',
|
|
||||||
transitionDuration: 250,
|
|
||||||
setup: (self) => {
|
|
||||||
Utils.timeout(1, () => self.set_reveal_child(true));
|
|
||||||
self.on('notify::reveal-child', () => {
|
|
||||||
if (self.reveal_child) Utils.timeout(125, () => self.child.set_reveal_child(true))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
child: Widget.Revealer({
|
|
||||||
className: 'revealer',
|
|
||||||
hpack: 'end',
|
|
||||||
transition: 'slide_left',
|
|
||||||
transitionDuration: 250,
|
|
||||||
setup: (self) => self.on('notify::reveal-child', () => {
|
|
||||||
if (!self.reveal_child) Utils.timeout(250, () => self.parent.set_reveal_child(false))
|
|
||||||
}),
|
|
||||||
child: layout
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const show_notification_popups = Variable(true)
|
|
||||||
export const NotificationPopups = Widget.Window({
|
|
||||||
visible: show_notification_popups.bind(),
|
|
||||||
name: 'notifications',
|
|
||||||
anchor: ['top', 'right'],
|
|
||||||
layer: 'overlay',
|
|
||||||
margins: [15, 0, 0, 0],
|
|
||||||
child: Widget.Box({
|
|
||||||
className: 'notifications',
|
|
||||||
vertical: true,
|
|
||||||
widthRequest: 2,
|
|
||||||
heightRequest: 2,
|
|
||||||
children: Notifications.popups.map(notification),
|
|
||||||
setup: (self) => self.hook(Notifications, (_, id) => {
|
|
||||||
if (!id || Notifications.dnd) return;
|
|
||||||
|
|
||||||
const n = Notifications.getNotification(id)
|
|
||||||
|
|
||||||
if (!n) return;
|
|
||||||
|
|
||||||
self.children = [...self.children, notification(n)]
|
|
||||||
}, 'notified')
|
|
||||||
.hook(Notifications, (_, id) => {
|
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
const n = self.children.find(
|
|
||||||
(child) => child.attribute.id === id,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!n) return;
|
|
||||||
|
|
||||||
n.child.set_reveal_child(false)
|
|
||||||
|
|
||||||
Utils.timeout(n.child.transition_duration, () => n.destroy())
|
|
||||||
}, 'dismissed')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
|||||||
import { hyprworkspaces_grid } from '../widgets/hyprworkspaces.js'
|
|
||||||
import { volume_slider } from '../widgets/volume.js'
|
|
||||||
import { brightness_slider } from '../widgets/brightness.js'
|
|
||||||
import { battery_dial } from '../widgets/battery.js'
|
|
||||||
import { horizontal_clock } from '../widgets/clock.js'
|
|
||||||
import { active_window } from '../widgets/hypractive.js'
|
|
||||||
import { media } from '../widgets/mpris.js'
|
|
||||||
|
|
||||||
export const FakeBar = Widget.Window({
|
|
||||||
name: 'FakeBar',
|
|
||||||
exclusivity: 'exclusive',
|
|
||||||
anchor: ['left', 'top', 'right'],
|
|
||||||
margins: [35, 0],
|
|
||||||
child: Widget.Box({css: 'min-height: 1px;'})
|
|
||||||
})
|
|
||||||
|
|
||||||
const left_box = Widget.Box({
|
|
||||||
className: 'left',
|
|
||||||
hpack: 'start',
|
|
||||||
children: [
|
|
||||||
hyprworkspaces_grid,
|
|
||||||
media
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const middle_box = Widget.Box({
|
|
||||||
className: 'middle',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
active_window
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const right_box = Widget.Box({
|
|
||||||
className: 'right',
|
|
||||||
hpack: 'end',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: 'sliderbox',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
volume_slider,
|
|
||||||
brightness_slider
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
className: 'battery-container',
|
|
||||||
children: [battery_dial]
|
|
||||||
}),
|
|
||||||
Widget.Separator({vertical: true}),
|
|
||||||
horizontal_clock
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const bar = Widget.Box({
|
|
||||||
className: 'bar',
|
|
||||||
children: [
|
|
||||||
left_box,
|
|
||||||
middle_box,
|
|
||||||
right_box
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Bar = Widget.Window({
|
|
||||||
name: 'status-bar',
|
|
||||||
exclusivity: 'ignore',
|
|
||||||
// margins: [5, 5, 5, 5],
|
|
||||||
anchor: ['left', 'top', 'right'],
|
|
||||||
child: bar
|
|
||||||
})
|
|
Loading…
Reference in New Issue
Block a user