Compare commits
10 Commits
f246b7e658
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b00e013752 | |||
| 3c7f2c2b75 | |||
| 4a813121c7 | |||
| f90e62d25b | |||
| e33041b66c | |||
| 88d867f1de | |||
| b09039cabb | |||
| dc82f5a0ae | |||
| 802b077362 | |||
| 9baa312d0e |
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 90,
|
||||||
|
"useTabs": false,
|
||||||
|
"proseWrap": "always",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"embeddedLanguageFormatting": "auto"
|
||||||
|
}
|
||||||
20
Bar.qml
20
Bar.qml
@@ -1,9 +1,12 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.Wayland
|
||||||
import "./Bar"
|
import "./Bar"
|
||||||
|
import "./Services/"
|
||||||
import "Color.js" as Colors
|
import "Color.js" as Colors
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
@@ -25,6 +28,8 @@ Scope {
|
|||||||
required property var modelData
|
required property var modelData
|
||||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||||
|
|
||||||
|
WlrLayershell.keyboardFocus: monitor.focused == true ? IpcManager.focusState :
|
||||||
|
WlrKeyboardFocus.None
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
exclusionMode: ExclusionMode.Normal
|
exclusionMode: ExclusionMode.Normal
|
||||||
exclusiveZone: barExclusionZone
|
exclusiveZone: barExclusionZone
|
||||||
@@ -32,6 +37,21 @@ Scope {
|
|||||||
mask: maskRegion
|
mask: maskRegion
|
||||||
screen: modelData
|
screen: modelData
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: blurOverlay
|
||||||
|
|
||||||
|
color: Colors.base
|
||||||
|
implicitHeight: win.modelData.height
|
||||||
|
implicitWidth: win.modelData.width
|
||||||
|
opacity: IpcManager.logoutMenuOpen == true ? 0.8 : 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Region {
|
Region {
|
||||||
id: maskRegion
|
id: maskRegion
|
||||||
|
|
||||||
|
|||||||
@@ -18,61 +18,16 @@ Container {
|
|||||||
|
|
||||||
animOffset: 150
|
animOffset: 150
|
||||||
boxColor: Colors.green
|
boxColor: Colors.green
|
||||||
|
boxHeight: 25
|
||||||
|
boxHeightOpened: root.batteryDisplayHeight
|
||||||
boxRadius: radius
|
boxRadius: radius
|
||||||
|
boxWidth: 60
|
||||||
|
boxWidthOpened: 410
|
||||||
defaultItem: icon
|
defaultItem: icon
|
||||||
exclusiveMonitor: root.monitor
|
exclusiveMonitor: root.monitor
|
||||||
exclusiveToScreen: true
|
exclusiveToScreen: true
|
||||||
forceHidden: !BatteryManager.hasLaptopBattery
|
forceHidden: !BatteryManager.hasLaptopBattery
|
||||||
|
openedItem: batteryDisplay
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "closed"
|
|
||||||
when: root.hovered == false && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 25
|
|
||||||
root.boxRadius: root.radius
|
|
||||||
root.boxWidth: 60
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: root.hovered == true && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 28
|
|
||||||
root.boxWidth: 64
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "opened"
|
|
||||||
when: root.opened == true && root.hovered == true
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: root.batteryDisplayHeight
|
|
||||||
root.boxRadius: 12
|
|
||||||
root.boxWidth: 450
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChangeScript {
|
|
||||||
script: {
|
|
||||||
stack.replace(batteryDisplay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
hover.onHoveredChanged: {
|
|
||||||
if (hover.hovered == false) {
|
|
||||||
root.opened = false;
|
|
||||||
if (root.state != "icon") {
|
|
||||||
root.stack.replace(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tap.onTapped: {
|
|
||||||
root.opened = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: icon
|
id: icon
|
||||||
|
|||||||
@@ -14,67 +14,19 @@ Container {
|
|||||||
property double devicePlaqueHeight: 60
|
property double devicePlaqueHeight: 60
|
||||||
property double devicePlaqueSpacing: 4
|
property double devicePlaqueSpacing: 4
|
||||||
required property HyprlandMonitor monitor
|
required property HyprlandMonitor monitor
|
||||||
property bool opened: false
|
|
||||||
|
|
||||||
animOffset: 200
|
animOffset: 200
|
||||||
boxColor: Colors.peach
|
boxColor: Colors.peach
|
||||||
boxHeight: 25
|
boxHeight: 25
|
||||||
|
boxHeightOpened: (root.devicePlaqueHeight * BluetoothManager.getConnectedDevicesList().length) + (
|
||||||
|
root.devicePlaqueSpacing * (BluetoothManager.getConnectedDevicesList().length + 1)) + 2
|
||||||
boxWidth: 33
|
boxWidth: 33
|
||||||
|
boxWidthOpened: 350
|
||||||
defaultItem: icon
|
defaultItem: icon
|
||||||
exclusiveMonitor: root.monitor
|
exclusiveMonitor: root.monitor
|
||||||
exclusiveToScreen: true
|
exclusiveToScreen: true
|
||||||
forceHidden: !BluetoothManager.anyConnected
|
forceHidden: !BluetoothManager.anyConnected
|
||||||
|
openedItem: deviceList
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "closed"
|
|
||||||
when: root.hovered == false && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 25
|
|
||||||
root.boxRadius: 9
|
|
||||||
root.boxWidth: 33
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: root.hovered == true && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 28
|
|
||||||
root.boxWidth: 35
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "opened"
|
|
||||||
when: root.opened == true && root.hovered == true
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: (root.devicePlaqueHeight * BluetoothManager.getConnectedDevicesList().length) + (
|
|
||||||
root.devicePlaqueSpacing * (BluetoothManager.getConnectedDevicesList().length + 1)) + 2
|
|
||||||
root.boxRadius: 12
|
|
||||||
root.boxWidth: 350
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChangeScript {
|
|
||||||
script: {
|
|
||||||
stack.replace(deviceList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
hover.onHoveredChanged: {
|
|
||||||
if (hover.hovered == false) {
|
|
||||||
root.opened = false;
|
|
||||||
if (root.state != "icon") {
|
|
||||||
root.stack.replace(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tap.onTapped: {
|
|
||||||
root.opened = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: icon
|
id: icon
|
||||||
|
|||||||
@@ -13,66 +13,18 @@ Container {
|
|||||||
|
|
||||||
required property HyprlandMonitor monitor
|
required property HyprlandMonitor monitor
|
||||||
property double networkDisplayHeight: 80
|
property double networkDisplayHeight: 80
|
||||||
property bool opened: false
|
|
||||||
|
|
||||||
animOffset: 250
|
animOffset: 250
|
||||||
boxColor: Colors.red
|
boxColor: Colors.red
|
||||||
boxHeight: 25
|
boxHeight: 25
|
||||||
|
boxHeightOpened: root.networkDisplayHeight
|
||||||
boxWidth: 33
|
boxWidth: 33
|
||||||
|
boxWidthOpened: 350
|
||||||
defaultItem: icon
|
defaultItem: icon
|
||||||
exclusiveMonitor: root.monitor
|
exclusiveMonitor: root.monitor
|
||||||
exclusiveToScreen: true
|
exclusiveToScreen: true
|
||||||
forceHidden: !NetworkManager.connectedWifi
|
forceHidden: !NetworkManager.connectedWifi
|
||||||
|
openedItem: networkDisplay
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "closed"
|
|
||||||
when: root.hovered == false && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 25
|
|
||||||
root.boxRadius: 9
|
|
||||||
root.boxWidth: 33
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: root.hovered == true && root.opened == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 28
|
|
||||||
root.boxWidth: 35
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "opened"
|
|
||||||
when: root.opened == true && root.hovered == true
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: root.networkDisplayHeight
|
|
||||||
root.boxRadius: 12
|
|
||||||
root.boxWidth: 350
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChangeScript {
|
|
||||||
script: {
|
|
||||||
stack.replace(wifiInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
hover.onHoveredChanged: {
|
|
||||||
if (hover.hovered == false) {
|
|
||||||
root.opened = false;
|
|
||||||
if (root.state != "icon") {
|
|
||||||
root.stack.replace(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tap.onTapped: {
|
|
||||||
root.opened = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: icon
|
id: icon
|
||||||
@@ -86,7 +38,7 @@ Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: wifiInfo
|
id: networkDisplay
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: networkDisplayRoot
|
id: networkDisplayRoot
|
||||||
@@ -142,7 +94,8 @@ Container {
|
|||||||
color: Colors.subtext1
|
color: Colors.subtext1
|
||||||
fontSize: 12
|
fontSize: 12
|
||||||
fontWeight: 5
|
fontWeight: 5
|
||||||
text: "Connected on: " + networkColumn.networkInfo["adapterName"]
|
text: networkColumn.networkInfo["ipAddress"] + " on "
|
||||||
|
+ networkColumn.networkInfo["adapterName"]
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|||||||
180
Bar/Time.qml
180
Bar/Time.qml
@@ -15,6 +15,10 @@ Container {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property Notification latestNotif
|
property Notification latestNotif
|
||||||
|
property bool logout: false
|
||||||
|
property double logoutHeight: 100
|
||||||
|
property double logoutMargin: 7
|
||||||
|
property double logoutSpacing: 5
|
||||||
required property HyprlandMonitor monitor
|
required property HyprlandMonitor monitor
|
||||||
property bool mpris: false
|
property bool mpris: false
|
||||||
property double mprisHeight: 60
|
property double mprisHeight: 60
|
||||||
@@ -22,7 +26,6 @@ Container {
|
|||||||
property double notificationHeight: 100
|
property double notificationHeight: 100
|
||||||
property double notificationWidth: 350
|
property double notificationWidth: 350
|
||||||
property bool notified: false
|
property bool notified: false
|
||||||
property bool overriden: false
|
|
||||||
|
|
||||||
boxColor: Colors.mauve
|
boxColor: Colors.mauve
|
||||||
defaultItem: time
|
defaultItem: time
|
||||||
@@ -30,43 +33,6 @@ Container {
|
|||||||
exclusiveToScreen: true
|
exclusiveToScreen: true
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
|
||||||
name: "closed"
|
|
||||||
when: root.hovered == false && root.overriden == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: 28
|
|
||||||
root.boxRadius: 9
|
|
||||||
root.boxWidth: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChangeScript {
|
|
||||||
script: {
|
|
||||||
if (!root.stack.currentItem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (root.stack.currentItem.component != "time") {
|
|
||||||
root.stack.replace(time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: root.hovered == true && root.overriden == false
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.boxHeight: MprisManager.getPlaying() == false ? 32 : root.mprisHeight
|
|
||||||
root.boxRadius: 12
|
|
||||||
root.boxWidth: MprisManager.getPlaying() == false ? 107 : root.mprisWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChangeScript {
|
|
||||||
script: {
|
|
||||||
root.stack.replace(MprisManager.getPlaying() == true ? mprisToast : time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
State {
|
||||||
name: "notified"
|
name: "notified"
|
||||||
when: root.notified == true
|
when: root.notified == true
|
||||||
@@ -93,7 +59,7 @@ Container {
|
|||||||
root.boxHeight: root.mprisHeight
|
root.boxHeight: root.mprisHeight
|
||||||
root.boxRadius: 12
|
root.boxRadius: 12
|
||||||
root.boxWidth: root.mprisWidth
|
root.boxWidth: root.mprisWidth
|
||||||
root.visibleTopMargin: 5
|
root.visibleTopMargin: root.hovered == true ? 0 : 5
|
||||||
}
|
}
|
||||||
|
|
||||||
StateChangeScript {
|
StateChangeScript {
|
||||||
@@ -102,9 +68,51 @@ Container {
|
|||||||
mprisTimer.restart();
|
mprisTimer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "logout"
|
||||||
|
when: root.logout == true
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.boxHeight: root.logoutHeight
|
||||||
|
root.boxRadius: 13
|
||||||
|
root.boxWidth: ((root.logoutHeight - (root.logoutMargin * 2)) * 5) + (root.logoutMargin * 2) + (
|
||||||
|
root.logoutSpacing * 4)
|
||||||
|
root.visibleTopMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: {
|
||||||
|
root.stack.replace(logoutMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (root.hovered == true && MprisManager.getPlaying() == true) {
|
||||||
|
root.overriden = true;
|
||||||
|
root.mpris = true;
|
||||||
|
} else {
|
||||||
|
root.overriden = false;
|
||||||
|
root.mpris = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onCloseLogoutMenu() {
|
||||||
|
root.overriden = false;
|
||||||
|
root.logout = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLogoutMenu() {
|
||||||
|
root.overriden = true;
|
||||||
|
root.logout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
target: IpcManager
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewNotification() {
|
function onNewNotification() {
|
||||||
notification => {
|
notification => {
|
||||||
@@ -155,6 +163,98 @@ Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: logoutMenu
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: logoutMenuRoot
|
||||||
|
|
||||||
|
property var logoutModel: [
|
||||||
|
{
|
||||||
|
color: Colors.red,
|
||||||
|
text: "s",
|
||||||
|
key: Qt.Key_S
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: Colors.peach,
|
||||||
|
text: "e",
|
||||||
|
key: Qt.Key_E
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: Colors.yellow,
|
||||||
|
text: "r",
|
||||||
|
key: Qt.Key_R
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: Colors.green,
|
||||||
|
text: "l",
|
||||||
|
key: Qt.Key_L
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: Colors.sky,
|
||||||
|
text: "u",
|
||||||
|
key: Qt.Key_U
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
forceActiveFocus();
|
||||||
|
}
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.key == Qt.Key_Escape) {
|
||||||
|
IpcManager.closeLogoutMenu();
|
||||||
|
} else {
|
||||||
|
for (let button of logoutMenuRoot.logoutModel) {
|
||||||
|
if (button.key == event.key) {
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: root.logoutSpacing
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: root.logoutMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
delegate: logoutButton
|
||||||
|
model: logoutMenuRoot.logoutModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: logoutButton
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: logoutButtonRoot
|
||||||
|
|
||||||
|
// Key yen isn't on my keyboard, acts as an errorless analog for no bind
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: logoutButtonRoot.modelData.color
|
||||||
|
radius: 7
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.fill: parent
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
text: logoutButtonRoot.modelData.text
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: time
|
id: time
|
||||||
|
|
||||||
@@ -162,7 +262,7 @@ Container {
|
|||||||
property string component: "time"
|
property string component: "time"
|
||||||
|
|
||||||
horizontalAlignment: Qt.AlignCenter
|
horizontalAlignment: Qt.AlignCenter
|
||||||
text: Time.time
|
text: TimeManager.time
|
||||||
verticalAlignment: Qt.AlignVCenter
|
verticalAlignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,18 +25,6 @@ Container {
|
|||||||
defaultItem: placeholder
|
defaultItem: placeholder
|
||||||
exclusiveMonitor: monitor
|
exclusiveMonitor: monitor
|
||||||
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: root.hovered
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
root.barHeight: 30
|
|
||||||
root.workspaceWidth: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: placeholder
|
id: placeholder
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ Item {
|
|||||||
property double animOffset: 0
|
property double animOffset: 0
|
||||||
property color boxColor: Colors.surface0
|
property color boxColor: Colors.surface0
|
||||||
property double boxHeight: 28
|
property double boxHeight: 28
|
||||||
|
property double boxHeightOpened: 150
|
||||||
|
property bool boxOpened: false
|
||||||
property double boxRadius: 9
|
property double boxRadius: 9
|
||||||
|
property double boxRadiusOpened: 12
|
||||||
property double boxWidth: 100
|
property double boxWidth: 100
|
||||||
|
property double boxWidthOpened: 200
|
||||||
readonly property double calculatedTopMargin: {
|
readonly property double calculatedTopMargin: {
|
||||||
if (!root.exclusiveToScreen) {
|
if (!root.exclusiveToScreen) {
|
||||||
return visibleTopMargin;
|
return visibleTopMargin;
|
||||||
@@ -38,6 +42,8 @@ Item {
|
|||||||
property alias hover: hoverHandler
|
property alias hover: hoverHandler
|
||||||
property bool hoverableWhenHidden: false
|
property bool hoverableWhenHidden: false
|
||||||
property bool hovered: false
|
property bool hovered: false
|
||||||
|
property Component openedItem
|
||||||
|
property bool overriden: false
|
||||||
property alias rect: container
|
property alias rect: container
|
||||||
property alias stack: containerContent
|
property alias stack: containerContent
|
||||||
property alias tap: tapHandler
|
property alias tap: tapHandler
|
||||||
@@ -50,13 +56,13 @@ Item {
|
|||||||
Behavior on anchors.topMargin {
|
Behavior on anchors.topMargin {
|
||||||
animation: defaultCurve
|
animation: defaultCurve
|
||||||
}
|
}
|
||||||
Behavior on boxHeight {
|
Behavior on boxRadius {
|
||||||
SpringAnimation {
|
SpringAnimation {
|
||||||
damping: 0.3
|
damping: 0.3
|
||||||
spring: 4
|
spring: 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Behavior on boxRadius {
|
Behavior on implicitHeight {
|
||||||
SpringAnimation {
|
SpringAnimation {
|
||||||
damping: 0.3
|
damping: 0.3
|
||||||
spring: 4
|
spring: 4
|
||||||
@@ -68,6 +74,56 @@ Item {
|
|||||||
spring: 4
|
spring: 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "closed"
|
||||||
|
when: root.hovered == false && root.boxOpened == false && root.overriden == false
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
container.radius: root.boxRadius
|
||||||
|
root.implicitHeight: root.boxHeight
|
||||||
|
root.implicitWidth: root.boxWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: {
|
||||||
|
stack.replace(root.defaultItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hovered"
|
||||||
|
when: root.hovered == true && root.boxOpened == false && root.overriden == false
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
container.radius: root.boxRadius + 3
|
||||||
|
root.implicitHeight: root.boxHeight + 2
|
||||||
|
root.implicitWidth: root.boxWidth + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: {
|
||||||
|
stack.replace(root.defaultItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "opened"
|
||||||
|
when: root.boxOpened == true && root.hovered == true && root.overriden == false
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
container.radius: root.boxRadiusOpened
|
||||||
|
root.implicitHeight: root.boxHeightOpened
|
||||||
|
root.implicitWidth: root.boxWidthOpened
|
||||||
|
}
|
||||||
|
|
||||||
|
StateChangeScript {
|
||||||
|
script: {
|
||||||
|
stack.replace(root.openedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
onForceHiddenChanged: {
|
onForceHiddenChanged: {
|
||||||
if (root.forceHidden == true) {
|
if (root.forceHidden == true) {
|
||||||
@@ -125,12 +181,22 @@ Item {
|
|||||||
root.hovered = true;
|
root.hovered = true;
|
||||||
} else {
|
} else {
|
||||||
root.hovered = false;
|
root.hovered = false;
|
||||||
|
root.boxOpened = false;
|
||||||
|
if (root.state == "closed") {
|
||||||
|
root.stack.replace(root.defaultItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
id: tapHandler
|
id: tapHandler
|
||||||
|
|
||||||
|
onTapped: {
|
||||||
|
if (root.openedItem) {
|
||||||
|
root.boxOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -141,6 +207,12 @@ Item {
|
|||||||
color: root.boxColor
|
color: root.boxColor
|
||||||
radius: root.boxRadius
|
radius: root.boxRadius
|
||||||
|
|
||||||
|
Behavior on radius {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: containerContent
|
id: containerContent
|
||||||
|
|
||||||
|
|||||||
169
README.md
Normal file
169
README.md
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# VoidBar
|
||||||
|
|
||||||
|
_Minimal, Verbose, Clean_
|
||||||
|
|
||||||
|
## Ethos
|
||||||
|
|
||||||
|
This bar is a retort to all of the "dynamic island" style bars that have been popular
|
||||||
|
recently. I'm sure you know the ones, with a cramped, forced "minimalistic" aesthetic,
|
||||||
|
that, even though marketed toward window manager users such as myself, usually end up
|
||||||
|
being mouse driven spring animation slop.
|
||||||
|
|
||||||
|
Nobody wants that.
|
||||||
|
|
||||||
|
If you use something like Caelestia or Noctalia, sure, whatever. I'm not going to be some
|
||||||
|
elitist and say they aren't without their uses. If you want a cohesive shell with theme
|
||||||
|
switching and everything working perfectly out of the box, then they're good enough
|
||||||
|
solutions for new users, or even experienced ones that just claim they don't want to waste
|
||||||
|
their time writing from scratch.
|
||||||
|
|
||||||
|
However, everything outside of those shells is bullshit. Lately, all the shells I've seen
|
||||||
|
have been "dynamic islands", bars that only show the time when dormant and have a shitload
|
||||||
|
of features that you're never going to use effectively. They have no concept of workspace
|
||||||
|
identifiers, instead opting for nondescript nerd icon blobs rather than the tried and true
|
||||||
|
numbers that seem to always be more useful. Not that it matters, they're hidden anyway,
|
||||||
|
until you change workspaces and they get shown for a second or two after the point that
|
||||||
|
you actually needed to see them.
|
||||||
|
|
||||||
|
Then come the theme switchers, a sorry excuse for a feature that just ends up being a
|
||||||
|
party trick at the end of the day. Changing wallpapers every so often I can get behind,
|
||||||
|
but the whole theme? Really? The prophecy is borderline self fulfilling, you're going to
|
||||||
|
pick a favorite eventually and end up never touching the switcher again, no matter how
|
||||||
|
many variations of gruvbox you have configured. On top of that, are you really going to be
|
||||||
|
switching wallpapers at runtime that often? I for one never even considered implementing a
|
||||||
|
feature so distracting, opting instead to actually getting shit done, to the behest of
|
||||||
|
everyone on r/unixporn.
|
||||||
|
|
||||||
|
The final gripe I have with these so called "minimal bars" is, ironically, the
|
||||||
|
over-abundance of features. No, you don't need an android-style pull down menu to adjust
|
||||||
|
your brightness, no, you don't need a button to change your screen scaling on the fly, and
|
||||||
|
no, you don't need to be able to authenticate with your fucking desktop shell. Just use
|
||||||
|
sudo like the rest of us. Many such features are jammed into bars like these, it's as if
|
||||||
|
they need some standout feature in order to actually get used (oh wait). Bars shouldn't
|
||||||
|
need a whole custom settings app. Bars shouldn't need tutorials for what keybinds to use
|
||||||
|
to access some obscure feature. Bars shouldn't implement entire bluetooth management
|
||||||
|
interfaces. Bars should show the time, what workspace you're on, and maybe, at a stretch,
|
||||||
|
how much battery you have left. However, none of the geniuses over in the quickshell
|
||||||
|
discussions tab can figure out how to put numbers where the workspace blobs are.
|
||||||
|
|
||||||
|
So, I had no choice to fix the problem.
|
||||||
|
|
||||||
|
### The problem
|
||||||
|
|
||||||
|
I like minimalistic apps, and minimalism in general. But there comes a point where being
|
||||||
|
"minimal" either wraps around to being over the top and useless, or practically unusable.
|
||||||
|
Theoretically, the most minimal bar is no bar at all, but that suffers from not existing
|
||||||
|
by definition, so I did my best to come up with something that looks good, has utility,
|
||||||
|
and, most of the time, stays out of your way when you don't care about what it says.
|
||||||
|
|
||||||
|
The first thing I did was to expand from a magical single pill to the more classical
|
||||||
|
layout, a left, center and right section, ripped straight from waybar. If you only have a
|
||||||
|
left or right section, then your screen looks lopsided, and if you only have a center
|
||||||
|
section, then your bar either looks weirdly wide, or becomes just another useless capsule.
|
||||||
|
As I have ordained, the left section is solely for the workspace switcher, the center is
|
||||||
|
for the clock, and the right is for optional information, like bluetooth connection and
|
||||||
|
battery level, which you wouldn't need on a desktop machine.
|
||||||
|
|
||||||
|
The second innovation I came up with (fr fr no cap) was removing useless information from
|
||||||
|
the bar entirely. When wifi is disconnected, what good is it to have a "no wifi" icon
|
||||||
|
taking up space on the screen? None at all, in fact. Instead, I have opted to hide unused
|
||||||
|
modules entirely. For example, if no bluetooth devices are connected, instead of leaving a
|
||||||
|
useless logo in the top right, the module slides up off of the screen, telling you that
|
||||||
|
there is nothing connected simply through its absence. Plus, it looks better. That means
|
||||||
|
that on a system with no battery, no wifi connection (ethernet hides the module), and no
|
||||||
|
bluetooth devices connected, like my main pc most of the time, there are no needless icons
|
||||||
|
cluttering the top right of the screen.
|
||||||
|
|
||||||
|
The third and final main feature that I implemented is monitor-exclusive modules. If you
|
||||||
|
have multiple monitors, you most likely won't be looking at the second if you're doing
|
||||||
|
work on the first. If that's the case, then why would you need a ticking clock on your
|
||||||
|
second monitor? Why would you need an entire shell over there taking up space and being a
|
||||||
|
potential distraction (yes I know I'm being melodramatic but stfu)? This was one of my
|
||||||
|
main gripes with waybar, the fact that the same bar with the same information was
|
||||||
|
plastered across all of your screens, regardless of whether the information was needed or
|
||||||
|
not. A simple check can be done within an instance of the bar in quickshell, as to whether
|
||||||
|
that instance of the bar is on the currently focused monitor. If it is? Display the
|
||||||
|
modules. If not? Hide them away to keep the desktop clean. It's as simple as that.
|
||||||
|
|
||||||
|
No repeated information, no unnecesarry "disconnected" icons, and, most importantly, no
|
||||||
|
useless features. Speaking of...
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Tl;Dr
|
||||||
|
|
||||||
|
- _Workspace switcher:_ Displaying only the workspaces on that monitor. When on a
|
||||||
|
different monitor, highlights the focused workspace as to distinguish it from unfocused
|
||||||
|
workspaces
|
||||||
|
- _Clock:_ It's a clock. When a notification comes in, the clock is substituted for a
|
||||||
|
notification popup. If music is playing (any playerctld compatible player), when the
|
||||||
|
track changes a toast will appear, and when hovered over, the clock will be replaced
|
||||||
|
with a music information screen.
|
||||||
|
- _Network:_ Displays whether the machine is connected to wifi or not. Ethernet is assumed
|
||||||
|
a permanent feature, and the module is thus hidden even though a network is connected.
|
||||||
|
When clicked, shows the network name, local ip address, interface that is being used to
|
||||||
|
connect, and whether the network has saved login information.
|
||||||
|
- _Bluetooth:_ Displays whether there are any connected bluetooth devices. Does NOT
|
||||||
|
function as a full bluetooth interface because I have bluetui for that. When clicked,
|
||||||
|
displays connected devices, appropriate icons, and their mac addresses. The box
|
||||||
|
containing the icon doubles as a battery indicator, and when the icon is hovered over it
|
||||||
|
is replaced with a numerical percentage referring to the device's battery level
|
||||||
|
- _Battery:_ Displays battery level, and state through coloring and icons. When unplugged,
|
||||||
|
battery icon is reactive to every 10%. When plugged in but not recieving charge, the
|
||||||
|
module is highlighted orange. when plugged in and recieving charge, the module is
|
||||||
|
highlighted purple (the best color). When plugged in and full, the module is highlighted
|
||||||
|
blue. Icons also change to reflect the battery state, using the `nf-md-battery` class of
|
||||||
|
icons, as labeled on the nerd font website. When clicked on, The module shows the
|
||||||
|
battery's UPower reference (eg `BAT0`), reported charge in Wh, change rate in watts for
|
||||||
|
both charging and discharging, the time remaining until the battery is either fully
|
||||||
|
charged or empty, and a numerical percentage alongside another vertical battery meter
|
||||||
|
(same component as the bluetooth devices)
|
||||||
|
|
||||||
|
- Planned features (when I get around to it)
|
||||||
|
- Logout menu (wlogout is still broken lol)
|
||||||
|
- Notification menu/management
|
||||||
|
- Lock screen
|
||||||
|
- Volume control? (probably not, way-edges clears)
|
||||||
|
- Interactible mpris player (skip and rewind)
|
||||||
|
- Music indicator on the left of the clock when music is playing
|
||||||
|
- Hover to show mpris thing when music is paused
|
||||||
|
- Mpris firefox exclusion (I had to disable it in about:config lol)
|
||||||
|
|
||||||
|
### More rambling
|
||||||
|
|
||||||
|
If you read the above section, you will notice there is no mention of a theme switcher or
|
||||||
|
launcher functionality. That is because I made a bar, not a theme switcher or a launcher.
|
||||||
|
Other, more knowledgeable people have made those things already, and they are almost
|
||||||
|
definately more feature rich and polished than anything you or I could make in qml. Once
|
||||||
|
again, this bar has NO BULLSHIT!!! NONE!!!
|
||||||
|
|
||||||
|
One thing I found cool while making this bar is all the interesting information that most
|
||||||
|
other bars will hide from you. Instead of what my cpu temperature is, I can see how fast
|
||||||
|
my battery is charging down to 8 decimal places of precision. On top of this, because qml
|
||||||
|
is just fancy javascript under the hood, you can do surprisingly complex data manipulation
|
||||||
|
if needs be. Of course, you can pull out an old trusty `toFixed(2)` so that there aren't
|
||||||
|
so many decimals, but beyond that, for example, the mpris api gives you track info in
|
||||||
|
miliseconds, which means you have to make a utility function to convert from that format
|
||||||
|
into a readable one, and then manipulate those values again, because they don't have
|
||||||
|
leading zeros by default.
|
||||||
|
|
||||||
|
Because of how qml works, I worried less about how inefficient my polling code was, and
|
||||||
|
how things were going to react with eachother, and update order, and instead focused on
|
||||||
|
how I wanted the bar to act. I'll tell you a little secret, qml is a really really easy
|
||||||
|
language to learn. That's how it's supposed to be, and why there's a quick in the name.
|
||||||
|
Qml is to desktop apps what tailwind is to html, almost. You can throw together something
|
||||||
|
that looks cohesive in a few minutes, and because of how state management works, adding
|
||||||
|
reactivity like a hover animation is stupidly easy. Everything is predictable, and because
|
||||||
|
you don't have to worry about how to update components, you can just throw in a few js
|
||||||
|
functions here and there regardless of whether you need them. I have to tell you, having a
|
||||||
|
bunch of heights and margins and then using a bunch of maths in a function to dynamically
|
||||||
|
calculate the height of a window or a textbox is the coolest shit ever.
|
||||||
|
|
||||||
|
I will actually use this bar on my main machine. There might be some weird shit going on
|
||||||
|
with the battery module, because the only machine I had while making this was a laptop,
|
||||||
|
but once I get home everything will be resolved. This was not made to post on reddit. This
|
||||||
|
wasn't made on a whim. I hate the state of places like r/unixporn, where, for every decent
|
||||||
|
and original rice, there are 300 copycats or people just running a prefab shell with a
|
||||||
|
custom wallpaper. And don't even get me going on the damn islands again. If I can make
|
||||||
|
something like this in a few days, then so can you. Go make something, and thanks for
|
||||||
|
reading <3
|
||||||
31
Services/IpcManager.qml
Normal file
31
Services/IpcManager.qml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
import Quickshell.Wayland
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var focusState: WlrKeyboardFocus.None
|
||||||
|
property bool logoutMenuOpen: false
|
||||||
|
|
||||||
|
signal closeLogoutMenu
|
||||||
|
signal logoutMenu
|
||||||
|
|
||||||
|
onCloseLogoutMenu: {
|
||||||
|
root.focusState = WlrKeyboardFocus.None;
|
||||||
|
root.logoutMenuOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function openLogoutMenu(): void {
|
||||||
|
root.logoutMenu();
|
||||||
|
root.focusState = WlrKeyboardFocus.Exclusive;
|
||||||
|
root.logoutMenuOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "root"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
import Quickshell.Networking
|
import Quickshell.Networking
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
@@ -9,6 +10,7 @@ Singleton {
|
|||||||
property bool connectedWifi: root.defaultAdapter != null && root.defaultAdapter.connected
|
property bool connectedWifi: root.defaultAdapter != null && root.defaultAdapter.connected
|
||||||
&& root.defaultAdapter.type == DeviceType.Wifi
|
&& root.defaultAdapter.type == DeviceType.Wifi
|
||||||
property NetworkDevice defaultAdapter: Networking.devices.values[0] || null
|
property NetworkDevice defaultAdapter: Networking.devices.values[0] || null
|
||||||
|
property string ipAddress: "0.0.0.0"
|
||||||
|
|
||||||
function getConnectedNetworks(networksArr) {
|
function getConnectedNetworks(networksArr) {
|
||||||
let connectedNetworks = [];
|
let connectedNetworks = [];
|
||||||
@@ -61,7 +63,8 @@ Singleton {
|
|||||||
"networkName": networkName,
|
"networkName": networkName,
|
||||||
"networkStrength": networkStrength,
|
"networkStrength": networkStrength,
|
||||||
"icon": networkIcon,
|
"icon": networkIcon,
|
||||||
"saved": primaryNetwork.known
|
"saved": primaryNetwork.known,
|
||||||
|
"ipAddress": root.ipAddress
|
||||||
};
|
};
|
||||||
return outputDict;
|
return outputDict;
|
||||||
}
|
}
|
||||||
@@ -96,4 +99,24 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshIpAddress() {
|
||||||
|
ipProcess.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDefaultAdapterChanged: {
|
||||||
|
refreshIpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: ipProcess
|
||||||
|
|
||||||
|
command: ["sh", "-c", "ip -4 -o addr show dev " + root.defaultAdapter.name
|
||||||
|
+ " scope global | awk '{sub(\"/.*\", \"\", $4); printf $4}'"]
|
||||||
|
running: true
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: root.ipAddress = this.text
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
module Services
|
module Services
|
||||||
singleton Time 1.0 Time.qml
|
singleton TimeManager 1.0 TimeManager.qml
|
||||||
singleton NotificationManager 1.0 NotificationManager.qml
|
singleton NotificationManager 1.0 NotificationManager.qml
|
||||||
singleton WorkspaceManager 1.0 WorkspaceManager.qml
|
singleton WorkspaceManager 1.0 WorkspaceManager.qml
|
||||||
singleton BluetoothManager 1.0 BluetoothManager.qml
|
singleton BluetoothManager 1.0 BluetoothManager.qml
|
||||||
singleton MprisManager 1.0 MprisManager.qml
|
singleton MprisManager 1.0 MprisManager.qml
|
||||||
singleton BatteryManager 1.0 BatteryManager.qml
|
singleton BatteryManager 1.0 BatteryManager.qml
|
||||||
singleton NetworkManager 1.0 NetworkManager.qml
|
singleton NetworkManager 1.0 NetworkManager.qml
|
||||||
|
singleton IpcManager 1.0 IpcManager.qml
|
||||||
|
|||||||
Reference in New Issue
Block a user