initial commit, you are welcome odette
This commit is contained in:
141
windows/bar.js
Normal file
141
windows/bar.js
Normal file
@ -0,0 +1,141 @@
|
||||
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
|
||||
}
|
93
windows/launcher.js
Normal file
93
windows/launcher.js
Normal file
@ -0,0 +1,93 @@
|
||||
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
|
||||
}
|
76
windows/lock.js
Normal file
76
windows/lock.js
Normal file
@ -0,0 +1,76 @@
|
||||
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,
|
||||
})
|
49
windows/menu.js
Normal file
49
windows/menu.js
Normal file
@ -0,0 +1,49 @@
|
||||
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
|
||||
),
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
175
windows/notifications.js
Normal file
175
windows/notifications.js
Normal file
@ -0,0 +1,175 @@
|
||||
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: 'start',
|
||||
class_name: 'icon',
|
||||
child: notification_icon(n),
|
||||
})
|
||||
|
||||
const title = Widget.Label({
|
||||
className: 'title',
|
||||
label: n.summary,
|
||||
xalign: 0,
|
||||
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.Box({
|
||||
className: `notification ${n.urgency}`,
|
||||
vertical: true,
|
||||
children: [
|
||||
buttons,
|
||||
Widget.Box([
|
||||
icon,
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
title, body
|
||||
]
|
||||
})
|
||||
]),
|
||||
actions,
|
||||
timeout_progress
|
||||
]
|
||||
})
|
||||
|
||||
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: [5, 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')
|
||||
})
|
||||
})
|
||||
|
70
windows/top-bar.js
Normal file
70
windows/top-bar.js
Normal file
@ -0,0 +1,70 @@
|
||||
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
|
||||
})
|
Reference in New Issue
Block a user