Extract indicators bar widget
This commit is contained in:
@@ -0,0 +1,446 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "indicators"
|
||||
|
||||
readonly property bool vertical: bar ? bar.vertical : false
|
||||
readonly property var indicatorEntries: indicatorEntriesFromSettings(settings)
|
||||
property var activeIndicatorIds: []
|
||||
property var indicatorActiveStates: ({})
|
||||
property bool indicatorAreaHovered: false
|
||||
property bool indicatorItemHovered: false
|
||||
readonly property bool revealInactiveIndicators: indicatorAreaHovered || indicatorItemHovered
|
||||
|
||||
signal refreshRequested()
|
||||
|
||||
ListModel { id: activeIndicatorModel }
|
||||
|
||||
function entryId(entry) {
|
||||
if (typeof entry === "string") return entry
|
||||
if (Util.isPlainObject(entry)) {
|
||||
var id = entry["id"]
|
||||
if (id !== undefined && id !== null && String(id) !== "") return String(id)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function entrySettings(entry) {
|
||||
if (!Util.isPlainObject(entry)) return {}
|
||||
var copy = {}
|
||||
for (var key in entry) {
|
||||
if (key === "id") continue
|
||||
copy[key] = entry[key]
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
function indicatorEntriesFromSettings(settings) {
|
||||
var source = []
|
||||
if (settings.items && typeof settings.items.length === "number") source = settings.items
|
||||
else if (settings.indicators && typeof settings.indicators.length === "number") source = settings.indicators
|
||||
|
||||
var result = []
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
var item = source[i]
|
||||
if (typeof item !== "string" && item !== null && typeof item === "object") {
|
||||
try {
|
||||
item = JSON.parse(JSON.stringify(item))
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
var id = entryId(item)
|
||||
if (id !== "") result.push(item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function setIndicatorAreaHovered(hovered) {
|
||||
indicatorAreaHovered = hovered
|
||||
if (hovered) indicatorHideTimer.stop()
|
||||
else indicatorHideTimer.restart()
|
||||
}
|
||||
|
||||
function setIndicatorItemHovered(hovered) {
|
||||
if (hovered) {
|
||||
indicatorItemHovered = true
|
||||
indicatorHideTimer.stop()
|
||||
} else {
|
||||
indicatorHideTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
function hasIndicatorId(id) {
|
||||
for (var i = 0; i < indicatorEntries.length; i++) {
|
||||
if (entryId(indicatorEntries[i]) === id) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function entryForId(id) {
|
||||
for (var i = 0; i < indicatorEntries.length; i++) {
|
||||
var entry = indicatorEntries[i]
|
||||
if (entryId(entry) === id) return entry
|
||||
}
|
||||
|
||||
return { id: id }
|
||||
}
|
||||
|
||||
function activeModelIndex(id) {
|
||||
for (var i = 0; i < activeIndicatorModel.count; i++) {
|
||||
if (activeIndicatorModel.get(i).activeId === id) return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
function copyActiveStates() {
|
||||
var states = {}
|
||||
for (var id in indicatorActiveStates) {
|
||||
if (indicatorActiveStates[id] === true) states[id] = true
|
||||
}
|
||||
return states
|
||||
}
|
||||
|
||||
function orderedActiveIds(states, preferredOrder) {
|
||||
var ids = []
|
||||
|
||||
for (var i = 0; i < preferredOrder.length; i++) {
|
||||
var id = preferredOrder[i]
|
||||
if (ids.indexOf(id) === -1 && hasIndicatorId(id) && states[id] === true) ids.push(id)
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
function syncActiveIndicatorModel() {
|
||||
for (var i = activeIndicatorModel.count - 1; i >= 0; i--) {
|
||||
if (activeIndicatorIds.indexOf(activeIndicatorModel.get(i).activeId) === -1)
|
||||
activeIndicatorModel.remove(i)
|
||||
}
|
||||
|
||||
for (var j = 0; j < activeIndicatorIds.length; j++) {
|
||||
var id = activeIndicatorIds[j]
|
||||
var index = activeModelIndex(id)
|
||||
if (index === -1) activeIndicatorModel.insert(j, { activeId: id })
|
||||
else if (index !== j) activeIndicatorModel.move(index, j, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function setIndicatorActive(entry, active) {
|
||||
var id = entryId(entry)
|
||||
if (id === "") return
|
||||
|
||||
var states = copyActiveStates()
|
||||
if (active) states[id] = true
|
||||
else delete states[id]
|
||||
|
||||
indicatorActiveStates = states
|
||||
|
||||
var ids = orderedActiveIds(states, activeIndicatorIds)
|
||||
if (active && ids.indexOf(id) === -1 && hasIndicatorId(id)) ids.push(id)
|
||||
activeIndicatorIds = ids
|
||||
syncActiveIndicatorModel()
|
||||
}
|
||||
|
||||
function syncActiveIndicatorOrder() {
|
||||
activeIndicatorIds = orderedActiveIds(indicatorActiveStates, activeIndicatorIds)
|
||||
syncActiveIndicatorModel()
|
||||
}
|
||||
|
||||
onIndicatorEntriesChanged: syncActiveIndicatorOrder()
|
||||
|
||||
implicitWidth: root.vertical ? verticalIndicators.implicitWidth : horizontalIndicators.implicitWidth
|
||||
implicitHeight: root.vertical ? verticalIndicators.implicitHeight : horizontalIndicators.implicitHeight
|
||||
|
||||
IpcHandler {
|
||||
target: "indicators"
|
||||
|
||||
function refresh(): void {
|
||||
root.refreshRequested()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: indicatorHideTimer
|
||||
interval: 120
|
||||
onTriggered: {
|
||||
if (!root.indicatorAreaHovered)
|
||||
root.indicatorItemHovered = false
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 2000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.refreshRequested()
|
||||
}
|
||||
|
||||
Row {
|
||||
id: horizontalIndicators
|
||||
|
||||
visible: !root.vertical
|
||||
spacing: 0
|
||||
|
||||
ActiveIndicatorBlock {
|
||||
indicatorsModule: root
|
||||
indicatorModel: activeIndicatorModel
|
||||
horizontal: true
|
||||
reportActiveState: !root.vertical
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inactiveHorizontalArea
|
||||
|
||||
implicitWidth: inactiveHorizontalBlock.implicitWidth
|
||||
implicitHeight: inactiveHorizontalBlock.implicitHeight
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
|
||||
IndicatorBlock {
|
||||
id: inactiveHorizontalBlock
|
||||
anchors.fill: parent
|
||||
indicatorsModule: root
|
||||
indicatorEntries: root.indicatorEntries
|
||||
indicatorBlock: "inactive"
|
||||
horizontal: true
|
||||
reportActiveState: !root.vertical
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.setIndicatorAreaHovered(hovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: verticalIndicators
|
||||
|
||||
visible: root.vertical
|
||||
spacing: 0
|
||||
|
||||
ActiveIndicatorBlock {
|
||||
indicatorsModule: root
|
||||
indicatorModel: activeIndicatorModel
|
||||
horizontal: false
|
||||
reportActiveState: root.vertical
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inactiveVerticalArea
|
||||
|
||||
implicitWidth: inactiveVerticalBlock.implicitWidth
|
||||
implicitHeight: inactiveVerticalBlock.implicitHeight
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
|
||||
IndicatorBlock {
|
||||
id: inactiveVerticalBlock
|
||||
anchors.fill: parent
|
||||
indicatorsModule: root
|
||||
indicatorEntries: root.indicatorEntries
|
||||
indicatorBlock: "inactive"
|
||||
horizontal: false
|
||||
reportActiveState: root.vertical
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.setIndicatorAreaHovered(hovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ActiveIndicatorBlock: Item {
|
||||
id: activeIndicatorBlockRoot
|
||||
|
||||
property var indicatorModel: null
|
||||
property var indicatorsModule: null
|
||||
property bool horizontal: true
|
||||
property bool reportActiveState: false
|
||||
|
||||
implicitWidth: blockLoader.item ? blockLoader.item.childrenRect.width : 0
|
||||
implicitHeight: blockLoader.item ? blockLoader.item.childrenRect.height : 0
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
|
||||
Loader {
|
||||
id: blockLoader
|
||||
|
||||
anchors.fill: parent
|
||||
sourceComponent: activeIndicatorBlockRoot.horizontal ? horizontalActiveIndicatorBlock : verticalActiveIndicatorBlock
|
||||
}
|
||||
|
||||
Component {
|
||||
id: horizontalActiveIndicatorBlock
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: activeIndicatorBlockRoot.indicatorModel
|
||||
|
||||
IndicatorLoader {
|
||||
required property string activeId
|
||||
indicatorsModule: activeIndicatorBlockRoot.indicatorsModule
|
||||
entry: activeIndicatorBlockRoot.indicatorsModule.entryForId(activeId)
|
||||
indicatorBlock: "active"
|
||||
reportActiveState: activeIndicatorBlockRoot.reportActiveState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: verticalActiveIndicatorBlock
|
||||
|
||||
Column {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: activeIndicatorBlockRoot.indicatorModel
|
||||
|
||||
IndicatorLoader {
|
||||
required property string activeId
|
||||
indicatorsModule: activeIndicatorBlockRoot.indicatorsModule
|
||||
entry: activeIndicatorBlockRoot.indicatorsModule.entryForId(activeId)
|
||||
indicatorBlock: "active"
|
||||
reportActiveState: activeIndicatorBlockRoot.reportActiveState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IndicatorBlock: Item {
|
||||
id: indicatorBlockRoot
|
||||
|
||||
property var indicatorEntries: []
|
||||
property var indicatorsModule: null
|
||||
property string indicatorBlock: "active"
|
||||
property bool horizontal: true
|
||||
property bool reportActiveState: false
|
||||
|
||||
implicitWidth: blockLoader.item ? blockLoader.item.childrenRect.width : 0
|
||||
implicitHeight: blockLoader.item ? blockLoader.item.childrenRect.height : 0
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
|
||||
Loader {
|
||||
id: blockLoader
|
||||
|
||||
anchors.fill: parent
|
||||
sourceComponent: indicatorBlockRoot.horizontal ? horizontalIndicatorBlock : verticalIndicatorBlock
|
||||
}
|
||||
|
||||
Component {
|
||||
id: horizontalIndicatorBlock
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: indicatorBlockRoot.indicatorEntries
|
||||
|
||||
IndicatorLoader {
|
||||
required property var modelData
|
||||
indicatorsModule: indicatorBlockRoot.indicatorsModule
|
||||
entry: modelData
|
||||
indicatorBlock: indicatorBlockRoot.indicatorBlock
|
||||
reportActiveState: indicatorBlockRoot.reportActiveState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: verticalIndicatorBlock
|
||||
|
||||
Column {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: indicatorBlockRoot.indicatorEntries
|
||||
|
||||
IndicatorLoader {
|
||||
required property var modelData
|
||||
indicatorsModule: indicatorBlockRoot.indicatorsModule
|
||||
entry: modelData
|
||||
indicatorBlock: indicatorBlockRoot.indicatorBlock
|
||||
reportActiveState: indicatorBlockRoot.reportActiveState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IndicatorLoader: Item {
|
||||
id: indicatorSlot
|
||||
|
||||
required property var entry
|
||||
property var indicatorsModule: null
|
||||
required property string indicatorBlock
|
||||
property bool reportActiveState: false
|
||||
property bool activeStateObserved: false
|
||||
readonly property string indicatorId: root.entryId(entry)
|
||||
readonly property var indicatorSettings: root.entrySettings(entry)
|
||||
|
||||
implicitWidth: indicatorSource.item && indicatorSource.item.visible ? indicatorSource.item.implicitWidth : 0
|
||||
implicitHeight: indicatorSource.item && indicatorSource.item.visible ? indicatorSource.item.implicitHeight : 0
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
onEntryChanged: {
|
||||
activeStateObserved = false
|
||||
injectProps()
|
||||
syncActiveState()
|
||||
}
|
||||
onIndicatorBlockChanged: injectProps()
|
||||
onIndicatorSettingsChanged: injectProps()
|
||||
onIndicatorsModuleChanged: syncActiveState()
|
||||
onReportActiveStateChanged: syncActiveState()
|
||||
|
||||
Loader {
|
||||
id: indicatorSource
|
||||
|
||||
anchors.fill: parent
|
||||
source: indicatorSlot.indicatorId ? Qt.resolvedUrl("../indicators/" + indicatorSlot.indicatorId + ".qml") : ""
|
||||
onLoaded: {
|
||||
indicatorSlot.injectProps()
|
||||
indicatorSlot.syncActiveState()
|
||||
}
|
||||
onStatusChanged: if (status === Loader.Error) console.warn("Indicator loader error", indicatorSlot.indicatorId, source)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: indicatorSource.item
|
||||
ignoreUnknownSignals: true
|
||||
function onActiveChanged() { indicatorSlot.syncActiveState() }
|
||||
}
|
||||
|
||||
function injectProps() {
|
||||
var target = indicatorSource.item
|
||||
if (!target) return
|
||||
if ("bar" in target) target.bar = root.bar
|
||||
if ("moduleName" in target) target.moduleName = indicatorId
|
||||
if ("settings" in target) target.settings = indicatorSettings
|
||||
if ("indicatorBlock" in target) target.indicatorBlock = indicatorBlock
|
||||
if ("indicatorHost" in target) target.indicatorHost = root
|
||||
if ("activeOverride" in target) target.activeOverride = indicatorBlock === "active" ? true : null
|
||||
}
|
||||
|
||||
function syncActiveState() {
|
||||
if (!reportActiveState || !indicatorsModule || !indicatorsModule.setIndicatorActive) return
|
||||
|
||||
var active = !!indicatorSource.item && indicatorSource.item.active === true
|
||||
if (indicatorBlock === "active") {
|
||||
if (active) activeStateObserved = true
|
||||
else if (!activeStateObserved) return
|
||||
}
|
||||
|
||||
indicatorsModule.setIndicatorActive(entry, active)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user