mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-28 23:42:51 -05:00
Compare commits
13 Commits
d769300137
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ddea836a1 | ||
|
|
208d92aa06 | ||
|
|
6ef9ddd4f3 | ||
|
|
1c92d39185 | ||
|
|
c0f072217c | ||
|
|
542562f988 | ||
|
|
4e6f0d5e87 | ||
|
|
10639a5ead | ||
|
|
06d668e710 | ||
|
|
d1472dfcba | ||
|
|
ccb4da3cd8 | ||
|
|
46e96b49f0 | ||
|
|
984cfe7f98 |
@@ -183,6 +183,10 @@ For documentation contributions, see [DankLinux-Docs](https://github.com/AvengeM
|
||||
- [soramanew](https://github.com/soramanew) - [Caelestia](https://github.com/caelestia-dots/shell) inspiration
|
||||
- [end-4](https://github.com/end-4) - [dots-hyprland](https://github.com/end-4/dots-hyprland) inspiration
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#AvengeMedia/DankMaterialShell&type=date&legend=top-left)
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See [LICENSE](LICENSE) for details.
|
||||
|
||||
@@ -165,12 +165,11 @@ func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, ente
|
||||
log.Infof("[BluezAgent] DisplayPasskey: device=%s, passkey=%06d, entered=%d", device, passkey, entered)
|
||||
|
||||
if entered == 0 {
|
||||
pk := passkey
|
||||
_, err := a.promptFor(device, "display-passkey", []string{}, nil)
|
||||
passkeyStr := strconv.FormatUint(uint64(passkey), 10)
|
||||
_, err := a.promptFor(device, "display-passkey", []string{}, &passkeyStr)
|
||||
if err != nil {
|
||||
log.Warnf("[BluezAgent] DisplayPasskey acknowledgment failed: %v", err)
|
||||
}
|
||||
_ = pk
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -179,7 +178,8 @@ func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, ente
|
||||
func (a *BluezAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error {
|
||||
log.Infof("[BluezAgent] RequestConfirmation: device=%s, passkey=%06d", device, passkey)
|
||||
|
||||
secrets, err := a.promptFor(device, "confirm", []string{"decision"}, nil)
|
||||
passkeyStr := strconv.FormatUint(uint64(passkey), 10)
|
||||
secrets, err := a.promptFor(device, "confirm", []string{"decision"}, &passkeyStr)
|
||||
if err != nil {
|
||||
log.Warnf("[BluezAgent] RequestConfirmation failed: %v", err)
|
||||
return a.errorFrom(err)
|
||||
|
||||
@@ -354,21 +354,25 @@ func (m *Manager) handleDevicePropertiesChanged(path dbus.ObjectPath, changed ma
|
||||
_, hasTrusted := changed["Trusted"]
|
||||
|
||||
if hasPaired {
|
||||
if paired, ok := pairedVar.Value().(bool); ok && paired {
|
||||
devicePath := string(path)
|
||||
_, wasPending := m.pendingPairings.LoadAndDelete(devicePath)
|
||||
devicePath := string(path)
|
||||
if paired, ok := pairedVar.Value().(bool); ok {
|
||||
if paired {
|
||||
_, wasPending := m.pendingPairings.LoadAndDelete(devicePath)
|
||||
|
||||
if wasPending {
|
||||
select {
|
||||
case m.eventQueue <- func() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath)
|
||||
if err := m.ConnectDevice(devicePath); err != nil {
|
||||
log.Warnf("[Bluetooth] Auto-connect failed: %v", err)
|
||||
if wasPending {
|
||||
select {
|
||||
case m.eventQueue <- func() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath)
|
||||
if err := m.ConnectDevice(devicePath); err != nil {
|
||||
log.Warnf("[Bluetooth] Auto-connect failed: %v", err)
|
||||
}
|
||||
}:
|
||||
default:
|
||||
}
|
||||
}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
m.pendingPairings.Delete(devicePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,35 @@ import (
|
||||
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
|
||||
)
|
||||
|
||||
func CheckCapability() bool {
|
||||
display, err := wlclient.Connect("")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer display.Destroy()
|
||||
|
||||
registry, err := display.GetRegistry()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer registry.Destroy()
|
||||
|
||||
found := false
|
||||
|
||||
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
||||
if e.Interface == ext_workspace.ExtWorkspaceManagerV1InterfaceName {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
|
||||
// Roundtrip to ensure all registry events are processed
|
||||
if err := display.Roundtrip(); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func NewManager(display *wlclient.Display) (*Manager, error) {
|
||||
m := &Manager{
|
||||
display: display,
|
||||
|
||||
@@ -140,8 +140,20 @@ func RouteRequest(conn net.Conn, req models.Request) {
|
||||
|
||||
if strings.HasPrefix(req.Method, "extworkspace.") {
|
||||
if extWorkspaceManager == nil {
|
||||
models.RespondError(conn, req.ID, "extworkspace manager not initialized")
|
||||
return
|
||||
if extWorkspaceAvailable.Load() {
|
||||
extWorkspaceInitMutex.Lock()
|
||||
if extWorkspaceManager == nil {
|
||||
if err := InitializeExtWorkspaceManager(); err != nil {
|
||||
extWorkspaceInitMutex.Unlock()
|
||||
models.RespondError(conn, req.ID, "extworkspace manager not available")
|
||||
return
|
||||
}
|
||||
}
|
||||
extWorkspaceInitMutex.Unlock()
|
||||
} else {
|
||||
models.RespondError(conn, req.ID, "extworkspace manager not initialized")
|
||||
return
|
||||
}
|
||||
}
|
||||
extWorkspaceReq := extworkspace.Request{
|
||||
ID: req.ID,
|
||||
|
||||
@@ -63,6 +63,8 @@ var wlContext *wlcontext.SharedContext
|
||||
var capabilitySubscribers syncmap.Map[string, chan ServerInfo]
|
||||
var cupsSubscribers syncmap.Map[string, bool]
|
||||
var cupsSubscriberCount atomic.Int32
|
||||
var extWorkspaceAvailable atomic.Bool
|
||||
var extWorkspaceInitMutex sync.Mutex
|
||||
|
||||
func getSocketDir() string {
|
||||
if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" {
|
||||
@@ -361,7 +363,7 @@ func getCapabilities() Capabilities {
|
||||
caps = append(caps, "dwl")
|
||||
}
|
||||
|
||||
if extWorkspaceManager != nil {
|
||||
if extWorkspaceAvailable.Load() {
|
||||
caps = append(caps, "extworkspace")
|
||||
}
|
||||
|
||||
@@ -411,7 +413,7 @@ func getServerInfo() ServerInfo {
|
||||
caps = append(caps, "dwl")
|
||||
}
|
||||
|
||||
if extWorkspaceManager != nil {
|
||||
if extWorkspaceAvailable.Load() {
|
||||
caps = append(caps, "extworkspace")
|
||||
}
|
||||
|
||||
@@ -810,12 +812,14 @@ func handleSubscribe(conn net.Conn, req models.Request) {
|
||||
}
|
||||
|
||||
if shouldSubscribe("extworkspace") {
|
||||
if extWorkspaceManager == nil {
|
||||
if err := InitializeExtWorkspaceManager(); err != nil {
|
||||
log.Warnf("Failed to initialize ExtWorkspace manager for subscription: %v", err)
|
||||
} else {
|
||||
notifyCapabilityChange()
|
||||
if extWorkspaceManager == nil && extWorkspaceAvailable.Load() {
|
||||
extWorkspaceInitMutex.Lock()
|
||||
if extWorkspaceManager == nil {
|
||||
if err := InitializeExtWorkspaceManager(); err != nil {
|
||||
log.Warnf("Failed to initialize ExtWorkspace manager for subscription: %v", err)
|
||||
}
|
||||
}
|
||||
extWorkspaceInitMutex.Unlock()
|
||||
}
|
||||
|
||||
if extWorkspaceManager != nil {
|
||||
@@ -1248,6 +1252,14 @@ func Start(printDocs bool) error {
|
||||
log.Debugf("DWL manager unavailable: %v", err)
|
||||
}
|
||||
|
||||
if extworkspace.CheckCapability() {
|
||||
extWorkspaceAvailable.Store(true)
|
||||
log.Info("ExtWorkspace capability detected and will be available on subscription")
|
||||
} else {
|
||||
log.Debug("ExtWorkspace capability not available")
|
||||
extWorkspaceAvailable.Store(false)
|
||||
}
|
||||
|
||||
if err := InitializeWlrOutputManager(); err != nil {
|
||||
log.Debugf("WlrOutput manager unavailable: %v", err)
|
||||
}
|
||||
|
||||
@@ -136,7 +136,9 @@ Singleton {
|
||||
popout.currentTabIndex = tabIndex
|
||||
}
|
||||
|
||||
ModalManager.closeAllModalsExcept(null)
|
||||
if (currentPopout !== popout) {
|
||||
ModalManager.closeAllModalsExcept(null)
|
||||
}
|
||||
TrayMenuManager.closeAllMenus()
|
||||
|
||||
if (justClosedSamePopout) {
|
||||
|
||||
@@ -25,7 +25,9 @@ Singleton {
|
||||
Left,
|
||||
Right,
|
||||
TopCenter,
|
||||
BottomCenter
|
||||
BottomCenter,
|
||||
LeftCenter,
|
||||
RightCenter
|
||||
}
|
||||
|
||||
enum AnimationSpeed {
|
||||
|
||||
@@ -217,6 +217,14 @@ Item {
|
||||
id: polkitAuthModal
|
||||
}
|
||||
|
||||
BluetoothPairingModal {
|
||||
id: bluetoothPairingModal
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.bluetoothPairingModal = bluetoothPairingModal
|
||||
}
|
||||
}
|
||||
|
||||
property string lastCredentialsToken: ""
|
||||
property var lastCredentialsTime: 0
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ DankModal {
|
||||
property string passkeyInput: ""
|
||||
|
||||
function show(pairingData) {
|
||||
console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData))
|
||||
token = pairingData.token || ""
|
||||
deviceName = pairingData.deviceName || ""
|
||||
deviceAddress = pairingData.deviceAddr || ""
|
||||
@@ -26,6 +27,7 @@ DankModal {
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
|
||||
console.log("BluetoothPairingModal: Calling open()")
|
||||
open()
|
||||
Qt.callLater(() => {
|
||||
if (contentLoader.item) {
|
||||
@@ -39,6 +41,8 @@ DankModal {
|
||||
}
|
||||
|
||||
shouldBeVisible: false
|
||||
allowStacking: true
|
||||
keepPopoutsOpen: true
|
||||
width: 420
|
||||
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
|
||||
|
||||
@@ -62,8 +66,11 @@ DankModal {
|
||||
}
|
||||
|
||||
onBackgroundClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
if (token) {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
}
|
||||
close()
|
||||
token = ""
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
@@ -80,8 +87,11 @@ DankModal {
|
||||
implicitHeight: mainColumn.implicitHeight
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
if (token) {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
}
|
||||
close()
|
||||
token = ""
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
event.accepted = true
|
||||
@@ -110,17 +120,22 @@ DankModal {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (requestType === "confirm")
|
||||
switch (requestType) {
|
||||
case "confirm":
|
||||
return I18n.tr("Confirm passkey for ") + deviceName
|
||||
if (requestType === "authorize")
|
||||
case "display-passkey":
|
||||
return I18n.tr("Enter this passkey on ") + deviceName
|
||||
case "authorize":
|
||||
return I18n.tr("Authorize pairing with ") + deviceName
|
||||
if (requestType.startsWith("authorize-service"))
|
||||
return I18n.tr("Authorize service for ") + deviceName
|
||||
if (requestType === "pin")
|
||||
case "pin":
|
||||
return I18n.tr("Enter PIN for ") + deviceName
|
||||
if (requestType === "passkey")
|
||||
case "passkey":
|
||||
return I18n.tr("Enter passkey for ") + deviceName
|
||||
return deviceName
|
||||
default:
|
||||
if (requestType.startsWith("authorize-service"))
|
||||
return I18n.tr("Authorize service for ") + deviceName
|
||||
return deviceName
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceTextMedium
|
||||
@@ -204,7 +219,7 @@ DankModal {
|
||||
height: 56
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency)
|
||||
visible: requestType === "confirm"
|
||||
visible: requestType === "confirm" || requestType === "display-passkey"
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
@@ -261,8 +276,11 @@ DankModal {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
if (token) {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
}
|
||||
close()
|
||||
token = ""
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
@@ -288,11 +306,17 @@ DankModal {
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (requestType === "confirm")
|
||||
switch (requestType) {
|
||||
case "confirm":
|
||||
case "display-passkey":
|
||||
return I18n.tr("Confirm")
|
||||
if (requestType === "authorize" || requestType.startsWith("authorize-service"))
|
||||
case "authorize":
|
||||
return I18n.tr("Authorize")
|
||||
return I18n.tr("Pair")
|
||||
default:
|
||||
if (requestType.startsWith("authorize-service"))
|
||||
return I18n.tr("Authorize")
|
||||
return I18n.tr("Pair")
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.background
|
||||
@@ -331,8 +355,11 @@ DankModal {
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: () => {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
if (token) {
|
||||
DMSService.bluetoothCancelPairing(token)
|
||||
}
|
||||
close()
|
||||
token = ""
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
@@ -343,12 +370,23 @@ DankModal {
|
||||
function submitPairing() {
|
||||
const secrets = {}
|
||||
|
||||
if (requestType === "pin") {
|
||||
switch (requestType) {
|
||||
case "pin":
|
||||
secrets["pin"] = pinInput
|
||||
} else if (requestType === "passkey") {
|
||||
break
|
||||
case "passkey":
|
||||
secrets["passkey"] = passkeyInput
|
||||
} else if (requestType === "confirm" || requestType === "authorize" || requestType.startsWith("authorize-service")) {
|
||||
break
|
||||
case "confirm":
|
||||
case "display-passkey":
|
||||
case "authorize":
|
||||
secrets["decision"] = "yes"
|
||||
break
|
||||
default:
|
||||
if (requestType.startsWith("authorize-service")) {
|
||||
secrets["decision"] = "yes"
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
|
||||
@@ -358,6 +396,7 @@ DankModal {
|
||||
})
|
||||
|
||||
close()
|
||||
token = ""
|
||||
pinInput = ""
|
||||
passkeyInput = ""
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: UserInfoService.hostname || "Linux"
|
||||
text: DgopService.hostname || "DMS"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -12,7 +12,9 @@ Rectangle {
|
||||
|
||||
function resetScroll() {
|
||||
resultsList.contentY = 0
|
||||
resultsGrid.contentY = 0
|
||||
if (gridLoader.item) {
|
||||
gridLoader.item.contentY = 0
|
||||
}
|
||||
}
|
||||
|
||||
radius: Theme.cornerRadius
|
||||
@@ -92,88 +94,106 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
DankGridView {
|
||||
id: resultsGrid
|
||||
Loader {
|
||||
id: gridLoader
|
||||
|
||||
property int currentIndex: appLauncher ? appLauncher.selectedIndex : -1
|
||||
property int columns: appLauncher ? appLauncher.gridColumns : 4
|
||||
property bool adaptiveColumns: false
|
||||
property int minCellWidth: 120
|
||||
property int maxCellWidth: 160
|
||||
property int cellPadding: 8
|
||||
property real iconSizeRatio: 0.55
|
||||
property int maxIconSize: 48
|
||||
property int minIconSize: 32
|
||||
property bool hoverUpdatesSelection: false
|
||||
property bool keyboardNavigationActive: appLauncher ? appLauncher.keyboardNavigationActive : false
|
||||
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
||||
property int baseCellHeight: baseCellWidth + 20
|
||||
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
||||
property int remainingSpace: width - (actualColumns * cellWidth)
|
||||
|
||||
signal keyboardNavigationReset
|
||||
signal itemClicked(int index, var modelData)
|
||||
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
|
||||
|
||||
function ensureVisible(index) {
|
||||
if (index < 0 || index >= count)
|
||||
return
|
||||
|
||||
const itemY = Math.floor(index / actualColumns) * cellHeight
|
||||
const itemBottom = itemY + cellHeight
|
||||
if (itemY < contentY)
|
||||
contentY = itemY
|
||||
else if (itemBottom > contentY + height)
|
||||
contentY = itemBottom - height
|
||||
}
|
||||
property real _lastWidth: 0
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
visible: appLauncher && appLauncher.viewMode === "grid"
|
||||
model: appLauncher ? appLauncher.model : null
|
||||
clip: true
|
||||
cellWidth: baseCellWidth
|
||||
cellHeight: baseCellHeight
|
||||
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
|
||||
rightMargin: leftMargin
|
||||
focus: true
|
||||
interactive: true
|
||||
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
|
||||
reuseItems: true
|
||||
onCurrentIndexChanged: {
|
||||
if (keyboardNavigationActive)
|
||||
ensureVisible(currentIndex)
|
||||
}
|
||||
onItemClicked: (index, modelData) => {
|
||||
if (appLauncher)
|
||||
appLauncher.launchApp(modelData)
|
||||
}
|
||||
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
|
||||
if (contextMenu)
|
||||
contextMenu.show(mouseX, mouseY, modelData)
|
||||
}
|
||||
onKeyboardNavigationReset: () => {
|
||||
if (appLauncher)
|
||||
appLauncher.keyboardNavigationActive = false
|
||||
}
|
||||
|
||||
delegate: AppLauncherGridDelegate {
|
||||
gridView: resultsGrid
|
||||
cellWidth: resultsGrid.cellWidth
|
||||
cellHeight: resultsGrid.cellHeight
|
||||
cellPadding: resultsGrid.cellPadding
|
||||
minIconSize: resultsGrid.minIconSize
|
||||
maxIconSize: resultsGrid.maxIconSize
|
||||
iconSizeRatio: resultsGrid.iconSizeRatio
|
||||
hoverUpdatesSelection: resultsGrid.hoverUpdatesSelection
|
||||
keyboardNavigationActive: resultsGrid.keyboardNavigationActive
|
||||
currentIndex: resultsGrid.currentIndex
|
||||
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
|
||||
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
|
||||
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
|
||||
resultsGrid.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
|
||||
active: appLauncher && appLauncher.viewMode === "grid"
|
||||
onWidthChanged: {
|
||||
if (visible && Math.abs(width - _lastWidth) > 1) {
|
||||
_lastWidth = width
|
||||
active = false
|
||||
Qt.callLater(() => {
|
||||
active = true
|
||||
})
|
||||
}
|
||||
}
|
||||
sourceComponent: Component {
|
||||
DankGridView {
|
||||
id: resultsGrid
|
||||
|
||||
property int currentIndex: appLauncher ? appLauncher.selectedIndex : -1
|
||||
property int columns: appLauncher ? appLauncher.gridColumns : 4
|
||||
property bool adaptiveColumns: false
|
||||
property int minCellWidth: 120
|
||||
property int maxCellWidth: 160
|
||||
property int cellPadding: 8
|
||||
property real iconSizeRatio: 0.55
|
||||
property int maxIconSize: 48
|
||||
property int minIconSize: 32
|
||||
property bool hoverUpdatesSelection: false
|
||||
property bool keyboardNavigationActive: appLauncher ? appLauncher.keyboardNavigationActive : false
|
||||
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
||||
property int baseCellHeight: baseCellWidth + 20
|
||||
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
||||
property int remainingSpace: width - (actualColumns * cellWidth)
|
||||
|
||||
signal keyboardNavigationReset
|
||||
signal itemClicked(int index, var modelData)
|
||||
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
|
||||
|
||||
function ensureVisible(index) {
|
||||
if (index < 0 || index >= count)
|
||||
return
|
||||
|
||||
const itemY = Math.floor(index / actualColumns) * cellHeight
|
||||
const itemBottom = itemY + cellHeight
|
||||
if (itemY < contentY)
|
||||
contentY = itemY
|
||||
else if (itemBottom > contentY + height)
|
||||
contentY = itemBottom - height
|
||||
}
|
||||
|
||||
model: appLauncher ? appLauncher.model : null
|
||||
clip: true
|
||||
cellWidth: baseCellWidth
|
||||
cellHeight: baseCellHeight
|
||||
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
|
||||
rightMargin: leftMargin
|
||||
focus: true
|
||||
interactive: true
|
||||
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
|
||||
reuseItems: true
|
||||
onCurrentIndexChanged: {
|
||||
if (keyboardNavigationActive)
|
||||
ensureVisible(currentIndex)
|
||||
}
|
||||
onItemClicked: (index, modelData) => {
|
||||
if (appLauncher)
|
||||
appLauncher.launchApp(modelData)
|
||||
}
|
||||
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
|
||||
if (contextMenu)
|
||||
contextMenu.show(mouseX, mouseY, modelData)
|
||||
}
|
||||
onKeyboardNavigationReset: () => {
|
||||
if (appLauncher)
|
||||
appLauncher.keyboardNavigationActive = false
|
||||
}
|
||||
|
||||
delegate: AppLauncherGridDelegate {
|
||||
gridView: resultsGrid
|
||||
cellWidth: resultsGrid.cellWidth
|
||||
cellHeight: resultsGrid.cellHeight
|
||||
cellPadding: resultsGrid.cellPadding
|
||||
minIconSize: resultsGrid.minIconSize
|
||||
maxIconSize: resultsGrid.maxIconSize
|
||||
iconSizeRatio: resultsGrid.iconSizeRatio
|
||||
hoverUpdatesSelection: resultsGrid.hoverUpdatesSelection
|
||||
keyboardNavigationActive: resultsGrid.keyboardNavigationActive
|
||||
currentIndex: resultsGrid.currentIndex
|
||||
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
|
||||
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
|
||||
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
|
||||
resultsGrid.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
|
||||
}
|
||||
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
|
||||
}
|
||||
}
|
||||
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,13 @@ Rectangle {
|
||||
if (!device) return
|
||||
|
||||
const deviceAddr = device.address
|
||||
devicesBeingPaired.add(deviceAddr)
|
||||
const pairingSet = devicesBeingPaired
|
||||
|
||||
pairingSet.add(deviceAddr)
|
||||
devicesBeingPairedChanged()
|
||||
|
||||
BluetoothService.pairDevice(device, function(response) {
|
||||
devicesBeingPaired.delete(deviceAddr)
|
||||
pairingSet.delete(deviceAddr)
|
||||
devicesBeingPairedChanged()
|
||||
|
||||
if (response.error) {
|
||||
@@ -625,15 +627,14 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothPairingModal {
|
||||
id: bluetoothPairingModal
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
|
||||
function onBluetoothPairingRequest(data) {
|
||||
bluetoothPairingModal.show(data)
|
||||
const modal = PopoutService.bluetoothPairingModal
|
||||
if (modal && modal.token !== data.token) {
|
||||
modal.show(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var barWindow
|
||||
required property var axis
|
||||
required property var appDrawerLoader
|
||||
required property var dankDashPopoutLoader
|
||||
required property var processListPopoutLoader
|
||||
required property var notificationCenterLoader
|
||||
required property var batteryPopoutLoader
|
||||
required property var layoutPopoutLoader
|
||||
required property var vpnPopoutLoader
|
||||
required property var controlCenterLoader
|
||||
required property var clipboardHistoryModalPopup
|
||||
required property var systemUpdateLoader
|
||||
required property var notepadInstance
|
||||
|
||||
property alias reveal: core.reveal
|
||||
property alias autoHide: core.autoHide
|
||||
property alias backgroundTransparency: core.backgroundTransparency
|
||||
property alias hasActivePopout: core.hasActivePopout
|
||||
property alias mouseArea: topBarMouseArea
|
||||
|
||||
Item {
|
||||
id: inputMask
|
||||
|
||||
readonly property int barThickness: barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing)
|
||||
|
||||
readonly property bool showing: SettingsData.dankBarVisible && (core.reveal
|
||||
|| (CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview)
|
||||
|| !core.autoHide)
|
||||
|
||||
readonly property int maskThickness: showing ? barThickness : 1
|
||||
|
||||
x: {
|
||||
if (!axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Left: return 0
|
||||
case SettingsData.Position.Right: return parent.width - maskThickness
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
y: {
|
||||
if (axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Top: return 0
|
||||
case SettingsData.Position.Bottom: return parent.height - maskThickness
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
width: axis.isVertical ? maskThickness : parent.width
|
||||
height: axis.isVertical ? parent.height : maskThickness
|
||||
}
|
||||
|
||||
Region {
|
||||
id: mask
|
||||
item: inputMask
|
||||
}
|
||||
|
||||
property alias maskRegion: mask
|
||||
|
||||
QtObject {
|
||||
id: core
|
||||
|
||||
property real backgroundTransparency: SettingsData.dankBarTransparency
|
||||
property bool autoHide: SettingsData.dankBarAutoHide
|
||||
property bool revealSticky: false
|
||||
|
||||
property bool notepadInstanceVisible: notepadInstance?.isVisible ?? false
|
||||
|
||||
readonly property bool hasActivePopout: {
|
||||
const loaders = [{
|
||||
"loader": appDrawerLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": dankDashPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": processListPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": notificationCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": batteryPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": layoutPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": vpnPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": controlCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": clipboardHistoryModalPopup,
|
||||
"prop": "visible"
|
||||
}, {
|
||||
"loader": systemUpdateLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}]
|
||||
return notepadInstanceVisible || loaders.some(item => {
|
||||
if (item.loader) {
|
||||
return item.loader?.item?.[item.prop]
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
property bool reveal: {
|
||||
if (CompositorService.isNiri && NiriService.inOverview) {
|
||||
return SettingsData.dankBarOpenOnOverview
|
||||
}
|
||||
return SettingsData.dankBarVisible && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky)
|
||||
}
|
||||
|
||||
onHasActivePopoutChanged: {
|
||||
if (!hasActivePopout && autoHide && !topBarMouseArea.containsMouse) {
|
||||
revealSticky = true
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: revealHold
|
||||
interval: 250
|
||||
repeat: false
|
||||
onTriggered: core.revealSticky = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onDankBarTransparencyChanged() {
|
||||
core.backgroundTransparency = SettingsData.dankBarTransparency
|
||||
}
|
||||
|
||||
target: SettingsData
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: topBarMouseArea
|
||||
function onContainsMouseChanged() {
|
||||
if (topBarMouseArea.containsMouse) {
|
||||
core.revealSticky = true
|
||||
revealHold.stop()
|
||||
} else {
|
||||
if (core.autoHide && !core.hasActivePopout) {
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: topBarMouseArea
|
||||
y: !barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? parent.height - height : 0) : 0
|
||||
x: barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.width - width : 0) : 0
|
||||
height: !barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||
width: barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||
anchors {
|
||||
left: !barWindow.isVertical ? parent.left : (SettingsData.dankBarPosition === SettingsData.Position.Left ? parent.left : undefined)
|
||||
right: !barWindow.isVertical ? parent.right : (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.right : undefined)
|
||||
top: barWindow.isVertical ? parent.top : undefined
|
||||
bottom: barWindow.isVertical ? parent.bottom : undefined
|
||||
}
|
||||
hoverEnabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||
acceptedButtons: Qt.NoButton
|
||||
enabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1009
quickshell/Modules/DankBar/DankBarContent.qml
Normal file
1009
quickshell/Modules/DankBar/DankBarContent.qml
Normal file
File diff suppressed because it is too large
Load Diff
539
quickshell/Modules/DankBar/DankBarWindow.qml
Normal file
539
quickshell/Modules/DankBar/DankBarWindow.qml
Normal file
@@ -0,0 +1,539 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.I3
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import Quickshell.Services.Notifications
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Modules
|
||||
import qs.Modules.DankBar.Widgets
|
||||
import qs.Modules.DankBar.Popouts
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: barWindow
|
||||
|
||||
required property var rootWindow
|
||||
property var modelData: item
|
||||
property var hyprlandOverviewLoader: rootWindow ? rootWindow.hyprlandOverviewLoader : null
|
||||
|
||||
property var controlCenterButtonRef: null
|
||||
property var clockButtonRef: null
|
||||
|
||||
function triggerControlCenter() {
|
||||
controlCenterLoader.active = true
|
||||
if (!controlCenterLoader.item) {
|
||||
return
|
||||
}
|
||||
|
||||
if (controlCenterButtonRef && controlCenterLoader.item.setTriggerPosition) {
|
||||
const globalPos = controlCenterButtonRef.mapToGlobal(0, 0)
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, controlCenterButtonRef.width)
|
||||
const section = controlCenterButtonRef.section || "right"
|
||||
controlCenterLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen)
|
||||
} else {
|
||||
controlCenterLoader.item.triggerScreen = barWindow.screen
|
||||
}
|
||||
|
||||
controlCenterLoader.item.toggle()
|
||||
if (controlCenterLoader.item.shouldBeVisible && NetworkService.wifiEnabled) {
|
||||
NetworkService.scanWifi()
|
||||
}
|
||||
}
|
||||
|
||||
function triggerWallpaperBrowser() {
|
||||
dankDashPopoutLoader.active = true
|
||||
if (!dankDashPopoutLoader.item) {
|
||||
return
|
||||
}
|
||||
|
||||
if (clockButtonRef && clockButtonRef.visualContent && dankDashPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = clockButtonRef.visualContent.mapToGlobal(0, 0)
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, clockButtonRef.visualWidth)
|
||||
const section = clockButtonRef.section || "center"
|
||||
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen)
|
||||
} else {
|
||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen
|
||||
}
|
||||
|
||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 2)
|
||||
}
|
||||
|
||||
readonly property var dBarLayer: {
|
||||
switch (Quickshell.env("DMS_DANKBAR_LAYER")) {
|
||||
case "bottom":
|
||||
return WlrLayer.Bottom
|
||||
case "overlay":
|
||||
return WlrLayer.Overlay
|
||||
case "background":
|
||||
return WlrLayer.background
|
||||
default:
|
||||
return WlrLayer.Top
|
||||
}
|
||||
}
|
||||
|
||||
WlrLayershell.layer: dBarLayer
|
||||
WlrLayershell.namespace: "dms:bar"
|
||||
|
||||
signal colorPickerRequested
|
||||
|
||||
onColorPickerRequested: rootWindow.colorPickerRequested()
|
||||
|
||||
property alias axis: axis
|
||||
|
||||
AxisContext {
|
||||
id: axis
|
||||
edge: {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Top:
|
||||
return "top"
|
||||
case SettingsData.Position.Bottom:
|
||||
return "bottom"
|
||||
case SettingsData.Position.Left:
|
||||
return "left"
|
||||
case SettingsData.Position.Right:
|
||||
return "right"
|
||||
default:
|
||||
return "top"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property bool isVertical: axis.isVertical
|
||||
|
||||
property bool gothCornersEnabled: SettingsData.dankBarGothCornersEnabled
|
||||
property real wingtipsRadius: SettingsData.dankBarGothCornerRadiusOverride ? SettingsData.dankBarGothCornerRadiusValue : Theme.cornerRadius
|
||||
readonly property real _wingR: Math.max(0, wingtipsRadius)
|
||||
readonly property color _surfaceContainer: Theme.surfaceContainer
|
||||
readonly property real _backgroundAlpha: topBarCore?.backgroundTransparency ?? SettingsData.dankBarTransparency
|
||||
readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
|
||||
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
|
||||
|
||||
property string screenName: modelData.name
|
||||
readonly property int notificationCount: NotificationService.notifications.length
|
||||
readonly property real effectiveBarThickness: Math.max(barWindow.widgetThickness + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
|
||||
readonly property real widgetThickness: Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
|
||||
|
||||
screen: modelData
|
||||
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + SettingsData.dankBarSpacing + (SettingsData.dankBarGothCornersEnabled ? _wingR : 0), _dpr) : 0
|
||||
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + SettingsData.dankBarSpacing + (SettingsData.dankBarGothCornersEnabled ? _wingR : 0), _dpr) : 0
|
||||
color: "transparent"
|
||||
|
||||
property var nativeInhibitor: null
|
||||
|
||||
Component.onCompleted: {
|
||||
if (SettingsData.forceStatusBarLayoutRefresh) {
|
||||
SettingsData.forceStatusBarLayoutRefresh.connect(() => {
|
||||
Qt.callLater(() => {
|
||||
stackContainer.visible = false
|
||||
Qt.callLater(() => {
|
||||
stackContainer.visible = true
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
updateGpuTempConfig()
|
||||
|
||||
inhibitorInitTimer.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: inhibitorInitTimer
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (SessionService.nativeInhibitorAvailable) {
|
||||
createNativeInhibitor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PluginService
|
||||
function onPluginLoaded(pluginId) {
|
||||
console.info("DankBar: Plugin loaded:", pluginId)
|
||||
SettingsData.widgetDataChanged()
|
||||
}
|
||||
function onPluginUnloaded(pluginId) {
|
||||
console.info("DankBar: Plugin unloaded:", pluginId)
|
||||
SettingsData.widgetDataChanged()
|
||||
}
|
||||
}
|
||||
|
||||
function updateGpuTempConfig() {
|
||||
const allWidgets = [...(SettingsData.dankBarLeftWidgets || []), ...(SettingsData.dankBarCenterWidgets || []), ...(SettingsData.dankBarRightWidgets || [])]
|
||||
|
||||
const hasGpuTempWidget = allWidgets.some(widget => {
|
||||
const widgetId = typeof widget === "string" ? widget : widget.id
|
||||
const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false)
|
||||
return widgetId === "gpuTemp" && widgetEnabled
|
||||
})
|
||||
|
||||
DgopService.gpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled || SessionData.nonNvidiaGpuTempEnabled
|
||||
DgopService.nvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nvidiaGpuTempEnabled
|
||||
DgopService.nonNvidiaGpuTempEnabled = hasGpuTempWidget || SessionData.nonNvidiaGpuTempEnabled
|
||||
}
|
||||
|
||||
function createNativeInhibitor() {
|
||||
if (!SessionService.nativeInhibitorAvailable) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const qmlString = `
|
||||
import QtQuick
|
||||
import Quickshell.Wayland
|
||||
|
||||
IdleInhibitor {
|
||||
enabled: false
|
||||
}
|
||||
`
|
||||
|
||||
nativeInhibitor = Qt.createQmlObject(qmlString, barWindow, "DankBar.NativeInhibitor")
|
||||
nativeInhibitor.window = barWindow
|
||||
nativeInhibitor.enabled = Qt.binding(() => SessionService.idleInhibited)
|
||||
nativeInhibitor.enabledChanged.connect(function () {
|
||||
console.log("DankBar: Native inhibitor enabled changed to:", nativeInhibitor.enabled)
|
||||
if (SessionService.idleInhibited !== nativeInhibitor.enabled) {
|
||||
SessionService.idleInhibited = nativeInhibitor.enabled
|
||||
SessionService.inhibitorChanged()
|
||||
}
|
||||
})
|
||||
console.log("DankBar: Created native Wayland IdleInhibitor for", barWindow.screenName)
|
||||
} catch (e) {
|
||||
console.warn("DankBar: Failed to create native IdleInhibitor:", e)
|
||||
nativeInhibitor = null
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onDankBarLeftWidgetsChanged() {
|
||||
barWindow.updateGpuTempConfig()
|
||||
}
|
||||
|
||||
function onDankBarCenterWidgetsChanged() {
|
||||
barWindow.updateGpuTempConfig()
|
||||
}
|
||||
|
||||
function onDankBarRightWidgetsChanged() {
|
||||
barWindow.updateGpuTempConfig()
|
||||
}
|
||||
|
||||
target: SettingsData
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onNvidiaGpuTempEnabledChanged() {
|
||||
barWindow.updateGpuTempConfig()
|
||||
}
|
||||
|
||||
function onNonNvidiaGpuTempEnabledChanged() {
|
||||
barWindow.updateGpuTempConfig()
|
||||
}
|
||||
|
||||
target: SessionData
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: barWindow.screen
|
||||
function onGeometryChanged() {
|
||||
Qt.callLater(forceWidgetRefresh)
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 0
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
forceWidgetRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: axis
|
||||
function onChanged() {
|
||||
Qt.application.active
|
||||
refreshTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: !isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Top) : true
|
||||
anchors.bottom: !isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom) : true
|
||||
anchors.left: !isVertical ? true : (SettingsData.dankBarPosition === SettingsData.Position.Left)
|
||||
anchors.right: !isVertical ? true : (SettingsData.dankBarPosition === SettingsData.Position.Right)
|
||||
|
||||
exclusiveZone: (!SettingsData.dankBarVisible || topBarCore.autoHide) ? -1 : (barWindow.effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap)
|
||||
|
||||
Item {
|
||||
id: inputMask
|
||||
|
||||
readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing, barWindow._dpr)
|
||||
|
||||
readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview
|
||||
readonly property bool effectiveVisible: SettingsData.dankBarVisible || inOverviewWithShow
|
||||
readonly property bool showing: effectiveVisible && (topBarCore.reveal || inOverviewWithShow || !topBarCore.autoHide)
|
||||
|
||||
readonly property int maskThickness: showing ? barThickness : 1
|
||||
|
||||
x: {
|
||||
if (!axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Left:
|
||||
return 0
|
||||
case SettingsData.Position.Right:
|
||||
return parent.width - maskThickness
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
y: {
|
||||
if (axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Top:
|
||||
return 0
|
||||
case SettingsData.Position.Bottom:
|
||||
return parent.height - maskThickness
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
width: axis.isVertical ? maskThickness : parent.width
|
||||
height: axis.isVertical ? parent.height : maskThickness
|
||||
}
|
||||
|
||||
mask: Region {
|
||||
item: inputMask
|
||||
}
|
||||
|
||||
Item {
|
||||
id: topBarCore
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
|
||||
property real backgroundTransparency: SettingsData.dankBarTransparency
|
||||
property bool autoHide: SettingsData.dankBarAutoHide
|
||||
property bool revealSticky: false
|
||||
|
||||
Timer {
|
||||
id: revealHold
|
||||
interval: SettingsData.dankBarAutoHideDelay
|
||||
repeat: false
|
||||
onTriggered: topBarCore.revealSticky = false
|
||||
}
|
||||
|
||||
property bool reveal: {
|
||||
if (CompositorService.isNiri && NiriService.inOverview) {
|
||||
return SettingsData.dankBarOpenOnOverview || topBarMouseArea.containsMouse || hasActivePopout || revealSticky
|
||||
}
|
||||
return SettingsData.dankBarVisible && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky)
|
||||
}
|
||||
|
||||
readonly property bool hasActivePopout: {
|
||||
const loaders = [{
|
||||
"loader": appDrawerLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": dankDashPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": processListPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": notificationCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": batteryPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": layoutPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": vpnPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": controlCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": clipboardHistoryModalPopup,
|
||||
"prop": "visible"
|
||||
}, {
|
||||
"loader": systemUpdateLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}]
|
||||
return loaders.some(item => {
|
||||
if (item.loader && item.loader.item) {
|
||||
return item.loader.item[item.prop]
|
||||
}
|
||||
return false
|
||||
}) || rootWindow.systemTrayMenuOpen
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onDankBarTransparencyChanged() {
|
||||
topBarCore.backgroundTransparency = SettingsData.dankBarTransparency
|
||||
}
|
||||
|
||||
target: SettingsData
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: topBarMouseArea
|
||||
function onContainsMouseChanged() {
|
||||
if (topBarMouseArea.containsMouse) {
|
||||
topBarCore.revealSticky = true
|
||||
revealHold.stop()
|
||||
} else {
|
||||
if (topBarCore.autoHide && !topBarCore.hasActivePopout) {
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onHasActivePopoutChanged: {
|
||||
if (hasActivePopout) {
|
||||
revealSticky = true
|
||||
revealHold.stop()
|
||||
} else if (autoHide && !topBarMouseArea.containsMouse) {
|
||||
revealSticky = true
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: topBarMouseArea
|
||||
y: !barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? parent.height - height : 0) : 0
|
||||
x: barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.width - width : 0) : 0
|
||||
height: !barWindow.isVertical ? Theme.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing, barWindow._dpr) : undefined
|
||||
width: barWindow.isVertical ? Theme.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing, barWindow._dpr) : undefined
|
||||
anchors {
|
||||
left: !barWindow.isVertical ? parent.left : (SettingsData.dankBarPosition === SettingsData.Position.Left ? parent.left : undefined)
|
||||
right: !barWindow.isVertical ? parent.right : (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.right : undefined)
|
||||
top: barWindow.isVertical ? parent.top : undefined
|
||||
bottom: barWindow.isVertical ? parent.bottom : undefined
|
||||
}
|
||||
readonly property bool inOverview: CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview
|
||||
hoverEnabled: SettingsData.dankBarAutoHide && !topBarCore.reveal && !inOverview
|
||||
acceptedButtons: Qt.NoButton
|
||||
enabled: SettingsData.dankBarAutoHide && !topBarCore.reveal && !inOverview
|
||||
|
||||
Item {
|
||||
id: topBarContainer
|
||||
anchors.fill: parent
|
||||
|
||||
transform: Translate {
|
||||
id: topBarSlide
|
||||
x: barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (SettingsData.dankBarPosition === SettingsData.Position.Right ? barWindow.implicitWidth : -barWindow.implicitWidth), barWindow._dpr) : 0
|
||||
y: !barWindow.isVertical ? Theme.snap(topBarCore.reveal ? 0 : (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? barWindow.implicitHeight : -barWindow.implicitHeight), barWindow._dpr) : 0
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: barUnitInset
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: !barWindow.isVertical ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : (axis.edge === "left" ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : 0)
|
||||
anchors.rightMargin: !barWindow.isVertical ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : (axis.edge === "right" ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : 0)
|
||||
anchors.topMargin: barWindow.isVertical ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : (axis.outerVisualEdge() === "bottom" ? 0 : Theme.px(SettingsData.dankBarSpacing, barWindow._dpr))
|
||||
anchors.bottomMargin: barWindow.isVertical ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : (axis.outerVisualEdge() === "bottom" ? Theme.px(SettingsData.dankBarSpacing, barWindow._dpr) : 0)
|
||||
|
||||
BarCanvas {
|
||||
id: barBackground
|
||||
barWindow: barWindow
|
||||
axis: axis
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: scrollArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
|
||||
property real scrollAccumulator: 0
|
||||
property real touchpadThreshold: 500
|
||||
property bool actionInProgress: false
|
||||
|
||||
Timer {
|
||||
id: cooldownTimer
|
||||
interval: 100
|
||||
onTriggered: parent.actionInProgress = false
|
||||
}
|
||||
|
||||
onWheel: wheel => {
|
||||
if (actionInProgress) {
|
||||
wheel.accepted = false
|
||||
return
|
||||
}
|
||||
|
||||
const deltaY = wheel.angleDelta.y
|
||||
const deltaX = wheel.angleDelta.x
|
||||
|
||||
if (CompositorService.isNiri && Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
topBarContent.switchApp(deltaX)
|
||||
wheel.accepted = false
|
||||
return
|
||||
}
|
||||
|
||||
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
|
||||
const direction = deltaY < 0 ? 1 : -1
|
||||
|
||||
if (isMouseWheel) {
|
||||
topBarContent.switchWorkspace(direction)
|
||||
actionInProgress = true
|
||||
cooldownTimer.restart()
|
||||
} else {
|
||||
scrollAccumulator += deltaY
|
||||
|
||||
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
|
||||
const touchDirection = scrollAccumulator < 0 ? 1 : -1
|
||||
topBarContent.switchWorkspace(touchDirection)
|
||||
scrollAccumulator = 0
|
||||
actionInProgress = true
|
||||
cooldownTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
wheel.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
DankBarContent {
|
||||
id: topBarContent
|
||||
barWindow: barWindow
|
||||
rootWindow: rootWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,11 +505,6 @@ Item {
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
if (!root.parentWindow) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height / 2)
|
||||
return
|
||||
}
|
||||
|
||||
const globalPos = root.mapToGlobal(0, 0)
|
||||
const screenX = screen.x || 0
|
||||
const screenY = screen.y || 0
|
||||
@@ -958,12 +953,8 @@ Item {
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
if (!root.parentWindow) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height / 2)
|
||||
return
|
||||
}
|
||||
|
||||
const globalPos = root.mapToGlobal(0, 0)
|
||||
const targetItem = (typeof menuRoot !== "undefined" && menuRoot.anchorItem) ? menuRoot.anchorItem : root
|
||||
const globalPos = targetItem.mapToGlobal(0, 0)
|
||||
const screenX = screen.x || 0
|
||||
const screenY = screen.y || 0
|
||||
const relativeX = globalPos.x - screenX
|
||||
@@ -977,12 +968,12 @@ Item {
|
||||
let targetX = edge === "left"
|
||||
? effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance
|
||||
: screen.width - (effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance)
|
||||
anchorPos = Qt.point(targetX, relativeY + root.height / 2)
|
||||
anchorPos = Qt.point(targetX, relativeY + targetItem.height / 2)
|
||||
} else {
|
||||
let targetY = root.isAtBottom
|
||||
? screen.height - (effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance)
|
||||
: effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance
|
||||
anchorPos = Qt.point(relativeX + root.width / 2, targetY)
|
||||
anchorPos = Qt.point(relativeX + targetItem.width / 2, targetY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,12 +316,19 @@ Item {
|
||||
return [{"id": "1", "name": "1", "active": false}]
|
||||
}
|
||||
|
||||
const visible = group.workspaces.filter(ws => !ws.hidden).sort((a, b) => {
|
||||
const coordsA = a.coordinates || [0, 0]
|
||||
const coordsB = b.coordinates || [0, 0]
|
||||
if (coordsA[0] !== coordsB[0]) return coordsA[0] - coordsB[0]
|
||||
return coordsA[1] - coordsB[1]
|
||||
}).map(ws => ({
|
||||
let visible = group.workspaces.filter(ws => !ws.hidden)
|
||||
|
||||
const hasValidCoordinates = visible.some(ws => ws.coordinates && ws.coordinates.length > 0)
|
||||
if (hasValidCoordinates) {
|
||||
visible = visible.sort((a, b) => {
|
||||
const coordsA = a.coordinates || [0, 0]
|
||||
const coordsB = b.coordinates || [0, 0]
|
||||
if (coordsA[0] !== coordsB[0]) return coordsA[0] - coordsB[0]
|
||||
return coordsA[1] - coordsB[1]
|
||||
})
|
||||
}
|
||||
|
||||
visible = visible.map(ws => ({
|
||||
id: ws.id,
|
||||
name: ws.name,
|
||||
coordinates: ws.coordinates,
|
||||
@@ -350,7 +357,7 @@ Item {
|
||||
|
||||
function getRealWorkspaces() {
|
||||
return root.workspaceList.filter(ws => {
|
||||
if (useExtWorkspace) return ws && ws.id !== "" && !ws.hidden
|
||||
if (useExtWorkspace) return ws && (ws.id !== "" || ws.name !== "") && !ws.hidden
|
||||
if (CompositorService.isHyprland) return ws && ws.id !== -1
|
||||
if (CompositorService.isDwl) return ws && ws.tag !== -1
|
||||
if (CompositorService.isSway) return ws && ws.num !== -1
|
||||
@@ -893,7 +900,7 @@ Item {
|
||||
|
||||
if (isPlaceholder) return index + 1
|
||||
|
||||
if (root.useExtWorkspace) return modelData?.name || modelData?.id || ""
|
||||
if (root.useExtWorkspace) return index + 1
|
||||
if (CompositorService.isHyprland) return modelData?.id || ""
|
||||
if (CompositorService.isDwl) return (modelData?.tag !== undefined) ? (modelData.tag + 1) : ""
|
||||
if (CompositorService.isSway) return modelData?.num || ""
|
||||
|
||||
@@ -6,8 +6,10 @@ import qs.Widgets
|
||||
DankOSD {
|
||||
id: root
|
||||
|
||||
osdWidth: Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: 40 + Theme.spacingS * 2
|
||||
readonly property bool useVertical: isVerticalLayout
|
||||
|
||||
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
|
||||
autoHideInterval: 3000
|
||||
enableMouseInteraction: true
|
||||
|
||||
@@ -20,8 +22,13 @@ DankOSD {
|
||||
}
|
||||
}
|
||||
|
||||
content: Item {
|
||||
content: Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: useVertical ? verticalContent : horizontalContent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: horizontalContent
|
||||
|
||||
Item {
|
||||
property int gap: Theme.spacingS
|
||||
@@ -135,4 +142,175 @@ DankOSD {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: verticalContent
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
property int gap: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
radius: Theme.iconSize / 2
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
const deviceInfo = DisplayService.getCurrentDeviceInfo()
|
||||
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") {
|
||||
return "brightness_medium"
|
||||
} else if (deviceInfo.name.includes("kbd")) {
|
||||
return "keyboard"
|
||||
} else {
|
||||
return "lightbulb"
|
||||
}
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: vertSlider
|
||||
width: 12
|
||||
height: parent.height - Theme.iconSize - gap * 3 - 24
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap * 2 + Theme.iconSize
|
||||
|
||||
property bool dragging: false
|
||||
property int value: DisplayService.brightnessAvailable ? DisplayService.brightnessLevel : 0
|
||||
|
||||
readonly property int minimum: {
|
||||
const deviceInfo = DisplayService.getCurrentDeviceInfo()
|
||||
if (!deviceInfo) return 1
|
||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id)
|
||||
if (isExponential) return 1
|
||||
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0
|
||||
}
|
||||
|
||||
readonly property int maximum: {
|
||||
const deviceInfo = DisplayService.getCurrentDeviceInfo()
|
||||
if (!deviceInfo) return 100
|
||||
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id)
|
||||
if (isExponential) return 100
|
||||
return deviceInfo.displayMax || 100
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertTrack
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
color: Theme.outline
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertFill
|
||||
width: parent.width
|
||||
height: {
|
||||
const ratio = (vertSlider.value - vertSlider.minimum) / (vertSlider.maximum - vertSlider.minimum)
|
||||
return ratio * parent.height
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Theme.primary
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertHandle
|
||||
width: 24
|
||||
height: 8
|
||||
radius: Theme.cornerRadius
|
||||
y: {
|
||||
const ratio = (vertSlider.value - vertSlider.minimum) / (vertSlider.maximum - vertSlider.minimum)
|
||||
const travel = parent.height - height
|
||||
return Math.max(0, Math.min(travel, travel * (1 - ratio)))
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Theme.primary
|
||||
border.width: 3
|
||||
border.color: Theme.surfaceContainer
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: vertSliderArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: -12
|
||||
enabled: DisplayService.brightnessAvailable
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onContainsMouseChanged: {
|
||||
setChildHovered(containsMouse)
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
vertSlider.dragging = true
|
||||
updateBrightness(mouse)
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
vertSlider.dragging = false
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (pressed) {
|
||||
updateBrightness(mouse)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
updateBrightness(mouse)
|
||||
}
|
||||
|
||||
function updateBrightness(mouse) {
|
||||
if (DisplayService.brightnessAvailable) {
|
||||
const ratio = 1.0 - (mouse.y / height)
|
||||
const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum))
|
||||
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true)
|
||||
resetHideTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DisplayService
|
||||
|
||||
function onBrightnessChanged(showOsd) {
|
||||
if (!vertSlider.dragging && vertSlider.value !== DisplayService.brightnessLevel) {
|
||||
vertSlider.value = DisplayService.brightnessLevel
|
||||
}
|
||||
}
|
||||
|
||||
function onDeviceSwitched() {
|
||||
if (!vertSlider.dragging && vertSlider.value !== DisplayService.brightnessLevel) {
|
||||
vertSlider.value = DisplayService.brightnessLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottomMargin: gap
|
||||
text: {
|
||||
const deviceInfo = DisplayService.getCurrentDeviceInfo()
|
||||
const isExponential = deviceInfo ? SessionData.getBrightnessExponential(deviceInfo.id) : false
|
||||
const unit = (deviceInfo && deviceInfo.class === "ddc" && !isExponential) ? "" : "%"
|
||||
return vertSlider.value + unit
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
visible: SettingsData.osdAlwaysShowValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import qs.Widgets
|
||||
DankOSD {
|
||||
id: root
|
||||
|
||||
osdWidth: Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: 40 + Theme.spacingS * 2
|
||||
readonly property bool useVertical: isVerticalLayout
|
||||
|
||||
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
|
||||
autoHideInterval: 3000
|
||||
enableMouseInteraction: true
|
||||
|
||||
@@ -37,8 +39,13 @@ DankOSD {
|
||||
}
|
||||
}
|
||||
|
||||
content: Item {
|
||||
content: Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: useVertical ? verticalContent : horizontalContent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: horizontalContent
|
||||
|
||||
Item {
|
||||
property int gap: Theme.spacingS
|
||||
@@ -128,11 +135,161 @@ DankOSD {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: verticalContent
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
property int gap: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
radius: Theme.iconSize / 2
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up"
|
||||
size: Theme.iconSize
|
||||
color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: muteButtonVert
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
AudioService.toggleMute()
|
||||
}
|
||||
onContainsMouseChanged: {
|
||||
setChildHovered(containsMouse || vertSliderArea.containsMouse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: vertSlider
|
||||
width: 12
|
||||
height: parent.height - Theme.iconSize - gap * 3 - 24
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap * 2 + Theme.iconSize
|
||||
|
||||
property bool dragging: false
|
||||
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(100, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
||||
|
||||
Rectangle {
|
||||
id: vertTrack
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
color: Theme.outline
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertFill
|
||||
width: parent.width
|
||||
height: (vertSlider.value / 100) * parent.height
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Theme.primary
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertHandle
|
||||
width: 24
|
||||
height: 8
|
||||
radius: Theme.cornerRadius
|
||||
y: {
|
||||
const ratio = vertSlider.value / 100
|
||||
const travel = parent.height - height
|
||||
return Math.max(0, Math.min(travel, travel * (1 - ratio)))
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Theme.primary
|
||||
border.width: 3
|
||||
border.color: Theme.surfaceContainer
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: vertSliderArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: -12
|
||||
enabled: AudioService.sink && AudioService.sink.audio
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onContainsMouseChanged: {
|
||||
setChildHovered(containsMouse || muteButtonVert.containsMouse)
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
vertSlider.dragging = true
|
||||
updateVolume(mouse)
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
vertSlider.dragging = false
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (pressed) {
|
||||
updateVolume(mouse)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
updateVolume(mouse)
|
||||
}
|
||||
|
||||
function updateVolume(mouse) {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
const ratio = 1.0 - (mouse.y / height)
|
||||
const volume = Math.max(0, Math.min(100, Math.round(ratio * 100)))
|
||||
AudioService.suppressOSD = true
|
||||
AudioService.sink.audio.volume = volume / 100
|
||||
AudioService.suppressOSD = false
|
||||
resetHideTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
||||
|
||||
function onVolumeChanged() {
|
||||
if (!vertSlider.dragging) {
|
||||
vertSlider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottomMargin: gap
|
||||
text: vertSlider.value + "%"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
visible: SettingsData.osdAlwaysShowValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOsdShown: {
|
||||
if (AudioService.sink && AudioService.sink.audio && contentLoader.item) {
|
||||
const slider = contentLoader.item.children[0].children[1]
|
||||
if (slider) {
|
||||
slider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100))
|
||||
if (AudioService.sink && AudioService.sink.audio && contentLoader.item && contentLoader.item.item) {
|
||||
if (!useVertical) {
|
||||
const slider = contentLoader.item.item.children[0].children[1]
|
||||
if (slider && slider.value !== undefined) {
|
||||
slider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ DankPopout {
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
|
||||
onBackgroundClicked: close()
|
||||
|
||||
content: Component {
|
||||
Rectangle {
|
||||
id: popoutContainer
|
||||
|
||||
@@ -760,11 +760,15 @@ Item {
|
||||
return "Bottom Left"
|
||||
case SettingsData.Position.BottomCenter:
|
||||
return "Bottom Center"
|
||||
case SettingsData.Position.LeftCenter:
|
||||
return "Left Center"
|
||||
case SettingsData.Position.RightCenter:
|
||||
return "Right Center"
|
||||
default:
|
||||
return "Bottom Center"
|
||||
}
|
||||
}
|
||||
options: ["Top Right", "Top Left", "Top Center", "Bottom Right", "Bottom Left", "Bottom Center"]
|
||||
options: ["Top Right", "Top Left", "Top Center", "Bottom Right", "Bottom Left", "Bottom Center", "Left Center", "Right Center"]
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case "Top Right":
|
||||
@@ -785,6 +789,12 @@ Item {
|
||||
case "Bottom Center":
|
||||
SettingsData.set("osdPosition", SettingsData.Position.BottomCenter)
|
||||
break
|
||||
case "Left Center":
|
||||
SettingsData.set("osdPosition", SettingsData.Position.LeftCenter)
|
||||
break
|
||||
case "Right Center":
|
||||
SettingsData.set("osdPosition", SettingsData.Position.RightCenter)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "../Common/fzf.js" as Fzf
|
||||
import qs.Common
|
||||
|
||||
Singleton {
|
||||
@@ -12,7 +11,141 @@ Singleton {
|
||||
|
||||
property var applications: DesktopEntries.applications.values.filter(app => !app.noDisplay && !app.runInTerminal)
|
||||
|
||||
readonly property int maxResults: 10
|
||||
readonly property int frecencySampleSize: 10
|
||||
|
||||
readonly property var timeBuckets: [{
|
||||
"maxDays": 4,
|
||||
"weight": 100
|
||||
}, {
|
||||
"maxDays": 14,
|
||||
"weight": 70
|
||||
}, {
|
||||
"maxDays": 31,
|
||||
"weight": 50
|
||||
}, {
|
||||
"maxDays": 90,
|
||||
"weight": 30
|
||||
}, {
|
||||
"maxDays": 99999,
|
||||
"weight": 10
|
||||
}]
|
||||
|
||||
function tokenize(text) {
|
||||
return text.toLowerCase().trim().split(/[\s\-_]+/).filter(w => w.length > 0)
|
||||
}
|
||||
|
||||
function wordBoundaryMatch(text, query) {
|
||||
const textWords = tokenize(text)
|
||||
const queryWords = tokenize(query)
|
||||
|
||||
if (queryWords.length === 0)
|
||||
return false
|
||||
if (queryWords.length > textWords.length)
|
||||
return false
|
||||
|
||||
for (var i = 0; i <= textWords.length - queryWords.length; i++) {
|
||||
let allMatch = true
|
||||
for (var j = 0; j < queryWords.length; j++) {
|
||||
if (!textWords[i + j].startsWith(queryWords[j])) {
|
||||
allMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allMatch)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function levenshteinDistance(s1, s2) {
|
||||
const len1 = s1.length
|
||||
const len2 = s2.length
|
||||
const matrix = []
|
||||
|
||||
for (var i = 0; i <= len1; i++) {
|
||||
matrix[i] = [i]
|
||||
}
|
||||
for (var j = 0; j <= len2; j++) {
|
||||
matrix[0][j] = j
|
||||
}
|
||||
|
||||
for (var i = 1; i <= len1; i++) {
|
||||
for (var j = 1; j <= len2; j++) {
|
||||
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1
|
||||
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost)
|
||||
}
|
||||
}
|
||||
return matrix[len1][len2]
|
||||
}
|
||||
|
||||
function fuzzyMatchScore(text, query) {
|
||||
const queryLower = query.toLowerCase()
|
||||
const maxDistance = query.length <= 2 ? 0 : query.length === 3 ? 1 : query.length <= 6 ? 2 : 3
|
||||
|
||||
let bestScore = 0
|
||||
|
||||
const distance = levenshteinDistance(text.toLowerCase(), queryLower)
|
||||
if (distance <= maxDistance) {
|
||||
const maxLen = Math.max(text.length, query.length)
|
||||
bestScore = 1 - (distance / maxLen)
|
||||
}
|
||||
|
||||
const words = tokenize(text)
|
||||
for (const word of words) {
|
||||
const wordDistance = levenshteinDistance(word, queryLower)
|
||||
if (wordDistance <= maxDistance) {
|
||||
const maxLen = Math.max(word.length, query.length)
|
||||
const score = 1 - (wordDistance / maxLen)
|
||||
bestScore = Math.max(bestScore, score)
|
||||
}
|
||||
}
|
||||
|
||||
return bestScore
|
||||
}
|
||||
|
||||
function calculateFrecency(app) {
|
||||
const usageRanking = AppUsageHistoryData.appUsageRanking || {}
|
||||
const appId = app.id || (app.execString || app.exec || "")
|
||||
const idVariants = [appId, appId.replace(".desktop", ""), app.id, app.id ? app.id.replace(".desktop", "") : null].filter(id => id)
|
||||
|
||||
let usageData = null
|
||||
for (const variant of idVariants) {
|
||||
if (usageRanking[variant]) {
|
||||
usageData = usageRanking[variant]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!usageData || !usageData.usageCount) {
|
||||
return {
|
||||
"frecency": 0,
|
||||
"daysSinceUsed": 999999
|
||||
}
|
||||
}
|
||||
|
||||
const usageCount = usageData.usageCount || 0
|
||||
const lastUsed = usageData.lastUsed || 0
|
||||
const now = Date.now()
|
||||
const daysSinceUsed = (now - lastUsed) / (1000 * 60 * 60 * 24)
|
||||
|
||||
let timeBucketWeight = 10
|
||||
for (const bucket of timeBuckets) {
|
||||
if (daysSinceUsed <= bucket.maxDays) {
|
||||
timeBucketWeight = bucket.weight
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const contextBonus = 100
|
||||
const sampleSize = Math.min(usageCount, frecencySampleSize)
|
||||
const frecency = (timeBucketWeight * contextBonus * sampleSize) / 100
|
||||
|
||||
return {
|
||||
"frecency": frecency,
|
||||
"daysSinceUsed": daysSinceUsed
|
||||
}
|
||||
}
|
||||
|
||||
function searchApplications(query) {
|
||||
if (!query || query.length === 0) {
|
||||
@@ -23,7 +156,7 @@ Singleton {
|
||||
|
||||
const queryLower = query.toLowerCase().trim()
|
||||
const scoredApps = []
|
||||
const usageRanking = AppUsageHistoryData.appUsageRanking || {}
|
||||
const results = []
|
||||
|
||||
for (const app of applications) {
|
||||
const name = (app.name || "").toLowerCase()
|
||||
@@ -31,123 +164,83 @@ Singleton {
|
||||
const comment = (app.comment || "").toLowerCase()
|
||||
const keywords = app.keywords ? app.keywords.map(k => k.toLowerCase()) : []
|
||||
|
||||
let score = 0
|
||||
let matched = false
|
||||
|
||||
const nameWords = name.trim().split(/\s+/).filter(w => w.length > 0)
|
||||
const containsAsWord = nameWords.includes(queryLower)
|
||||
const startsWithAsWord = nameWords.some(word => word.startsWith(queryLower))
|
||||
let textScore = 0
|
||||
let matchType = "none"
|
||||
|
||||
if (name === queryLower) {
|
||||
score = 10000
|
||||
matched = true
|
||||
} else if (containsAsWord) {
|
||||
score = 9500 + (100 - Math.min(name.length, 100))
|
||||
matched = true
|
||||
textScore = 10000
|
||||
matchType = "exact"
|
||||
} else if (name.startsWith(queryLower)) {
|
||||
score = 9000 + (100 - Math.min(name.length, 100))
|
||||
matched = true
|
||||
} else if (startsWithAsWord) {
|
||||
score = 8500 + (100 - Math.min(name.length, 100))
|
||||
matched = true
|
||||
textScore = 5000
|
||||
matchType = "prefix"
|
||||
} else if (wordBoundaryMatch(name, queryLower)) {
|
||||
textScore = 1000
|
||||
matchType = "word_boundary"
|
||||
} else if (name.includes(queryLower)) {
|
||||
score = 8000 + (100 - Math.min(name.length, 100))
|
||||
matched = true
|
||||
} else if (keywords.length > 0) {
|
||||
textScore = 500
|
||||
matchType = "substring"
|
||||
} else if (genericName && genericName.startsWith(queryLower)) {
|
||||
textScore = 800
|
||||
matchType = "generic_prefix"
|
||||
} else if (genericName && genericName.includes(queryLower)) {
|
||||
textScore = 400
|
||||
matchType = "generic"
|
||||
}
|
||||
|
||||
if (matchType === "none" && keywords.length > 0) {
|
||||
for (const keyword of keywords) {
|
||||
if (keyword === queryLower) {
|
||||
score = 6000
|
||||
matched = true
|
||||
break
|
||||
} else if (keyword.startsWith(queryLower)) {
|
||||
score = 5500
|
||||
matched = true
|
||||
if (keyword.startsWith(queryLower)) {
|
||||
textScore = 300
|
||||
matchType = "keyword_prefix"
|
||||
break
|
||||
} else if (keyword.includes(queryLower)) {
|
||||
score = 5000
|
||||
matched = true
|
||||
textScore = 150
|
||||
matchType = "keyword"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matched && genericName.includes(queryLower)) {
|
||||
if (genericName === queryLower) {
|
||||
score = 9000
|
||||
} else if (genericName.startsWith(queryLower)) {
|
||||
score = 8500
|
||||
} else {
|
||||
const genericWords = genericName.trim().split(/\s+/).filter(w => w.length > 0)
|
||||
if (genericWords.includes(queryLower)) {
|
||||
score = 8000
|
||||
} else if (genericWords.some(word => word.startsWith(queryLower))) {
|
||||
score = 7500
|
||||
} else {
|
||||
score = 7000
|
||||
}
|
||||
}
|
||||
matched = true
|
||||
} else if (!matched && comment.includes(queryLower)) {
|
||||
score = 3000
|
||||
matched = true
|
||||
} else if (!matched) {
|
||||
const nameFinder = new Fzf.Finder([app], {
|
||||
"selector": a => a.name || "",
|
||||
"casing": "case-insensitive",
|
||||
"fuzzy": "v2"
|
||||
})
|
||||
const fuzzyResults = nameFinder.find(query)
|
||||
if (fuzzyResults.length > 0 && fuzzyResults[0].score > 0) {
|
||||
score = Math.min(fuzzyResults[0].score, 2000)
|
||||
matched = true
|
||||
|
||||
if (matchType === "none" && comment && comment.includes(queryLower)) {
|
||||
textScore = 50
|
||||
matchType = "comment"
|
||||
}
|
||||
|
||||
if (matchType === "none") {
|
||||
const fuzzyScore = fuzzyMatchScore(name, queryLower)
|
||||
if (fuzzyScore > 0) {
|
||||
textScore = fuzzyScore * 100
|
||||
matchType = "fuzzy"
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
const appId = app.id || (app.execString || app.exec || "")
|
||||
const idVariants = [
|
||||
appId,
|
||||
appId.replace(".desktop", ""),
|
||||
app.id,
|
||||
app.id ? app.id.replace(".desktop", "") : null
|
||||
].filter(id => id)
|
||||
if (matchType !== "none") {
|
||||
const frecencyData = calculateFrecency(app)
|
||||
|
||||
let usageData = null
|
||||
for (const variant of idVariants) {
|
||||
if (usageRanking[variant]) {
|
||||
usageData = usageRanking[variant]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (usageData) {
|
||||
const usageCount = usageData.usageCount || 0
|
||||
const lastUsed = usageData.lastUsed || 0
|
||||
const now = Date.now()
|
||||
const daysSinceUsed = (now - lastUsed) / (1000 * 60 * 60 * 24)
|
||||
|
||||
let usageBonus = 0
|
||||
usageBonus += Math.min(usageCount * 100, 2000)
|
||||
|
||||
if (daysSinceUsed < 1) {
|
||||
usageBonus += 1500
|
||||
} else if (daysSinceUsed < 7) {
|
||||
usageBonus += 1000
|
||||
} else if (daysSinceUsed < 30) {
|
||||
usageBonus += 500
|
||||
}
|
||||
|
||||
score += usageBonus
|
||||
}
|
||||
|
||||
scoredApps.push({
|
||||
"app": app,
|
||||
"score": score
|
||||
})
|
||||
results.push({
|
||||
"app": app,
|
||||
"textScore": textScore,
|
||||
"frecency": frecencyData.frecency,
|
||||
"daysSinceUsed": frecencyData.daysSinceUsed,
|
||||
"matchType": matchType
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const result of results) {
|
||||
const frecencyBonus = result.frecency > 0 ? Math.min(result.frecency / 10, 2000) : 0
|
||||
const recencyBonus = result.daysSinceUsed < 1 ? 1500 : result.daysSinceUsed < 7 ? 1000 : result.daysSinceUsed < 30 ? 500 : 0
|
||||
|
||||
const finalScore = result.textScore + frecencyBonus + recencyBonus
|
||||
|
||||
scoredApps.push({
|
||||
"app": result.app,
|
||||
"score": finalScore
|
||||
})
|
||||
}
|
||||
|
||||
scoredApps.sort((a, b) => b.score - a.score)
|
||||
return scoredApps.slice(0, 50).map(item => item.app)
|
||||
return scoredApps.slice(0, maxResults).map(item => item.app)
|
||||
}
|
||||
|
||||
function getCategoriesForApp(app) {
|
||||
@@ -265,7 +358,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function getPluginCategoryIcon(category) {
|
||||
if (typeof PluginService === "undefined") return null
|
||||
if (typeof PluginService === "undefined")
|
||||
return null
|
||||
|
||||
const launchers = PluginService.getLauncherPlugins()
|
||||
for (const pluginId in launchers) {
|
||||
@@ -295,7 +389,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function getPluginItems(category, query) {
|
||||
if (typeof PluginService === "undefined") return []
|
||||
if (typeof PluginService === "undefined")
|
||||
return []
|
||||
|
||||
const launchers = PluginService.getLauncherPlugins()
|
||||
for (const pluginId in launchers) {
|
||||
@@ -313,12 +408,13 @@ Singleton {
|
||||
}
|
||||
|
||||
const component = PluginService.pluginLauncherComponents[pluginId]
|
||||
if (!component) return []
|
||||
if (!component)
|
||||
return []
|
||||
|
||||
try {
|
||||
const instance = component.createObject(root, {
|
||||
"pluginService": PluginService
|
||||
})
|
||||
"pluginService": PluginService
|
||||
})
|
||||
|
||||
if (instance && typeof instance.getItems === "function") {
|
||||
const items = instance.getItems(query || "")
|
||||
@@ -337,15 +433,17 @@ Singleton {
|
||||
}
|
||||
|
||||
function executePluginItem(item, pluginId) {
|
||||
if (typeof PluginService === "undefined") return false
|
||||
if (typeof PluginService === "undefined")
|
||||
return false
|
||||
|
||||
const component = PluginService.pluginLauncherComponents[pluginId]
|
||||
if (!component) return false
|
||||
if (!component)
|
||||
return false
|
||||
|
||||
try {
|
||||
const instance = component.createObject(root, {
|
||||
"pluginService": PluginService
|
||||
})
|
||||
"pluginService": PluginService
|
||||
})
|
||||
|
||||
if (instance && typeof instance.executeItem === "function") {
|
||||
instance.executeItem(item)
|
||||
@@ -364,7 +462,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function searchPluginItems(query) {
|
||||
if (typeof PluginService === "undefined") return []
|
||||
if (typeof PluginService === "undefined")
|
||||
return []
|
||||
|
||||
let allItems = []
|
||||
const launchers = PluginService.getLauncherPlugins()
|
||||
|
||||
@@ -49,20 +49,20 @@ Singleton {
|
||||
return devices.sort((a, b) => {
|
||||
const aName = a.name || a.deviceName || ""
|
||||
const bName = b.name || b.deviceName || ""
|
||||
const aAddr = a.address || ""
|
||||
const bAddr = b.address || ""
|
||||
|
||||
const aHasRealName = aName.includes(" ") && aName.length > 3
|
||||
const bHasRealName = bName.includes(" ") && bName.length > 3
|
||||
|
||||
if (aHasRealName && !bHasRealName) {
|
||||
return -1
|
||||
}
|
||||
if (!aHasRealName && bHasRealName) {
|
||||
return 1
|
||||
if (aHasRealName && !bHasRealName) return -1
|
||||
if (!aHasRealName && bHasRealName) return 1
|
||||
|
||||
if (aHasRealName && bHasRealName) {
|
||||
return aName.localeCompare(bName)
|
||||
}
|
||||
|
||||
const aSignal = (a.signalStrength !== undefined && a.signalStrength > 0) ? a.signalStrength : 0
|
||||
const bSignal = (b.signalStrength !== undefined && b.signalStrength > 0) ? b.signalStrength : 0
|
||||
return bSignal - aSignal
|
||||
return aAddr.localeCompare(bAddr)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -441,6 +441,7 @@ Singleton {
|
||||
if (isHyprland) return Hyprland.dispatch("dpms off")
|
||||
if (isDwl) return _dwlPowerOffMonitors()
|
||||
if (isSway) { try { I3.dispatch("output * dpms off") } catch(_){} return }
|
||||
if (isLabwc) { Quickshell.execDetached(["dms", "dpms", "off"]) }
|
||||
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
|
||||
}
|
||||
|
||||
@@ -449,6 +450,7 @@ Singleton {
|
||||
if (isHyprland) return Hyprland.dispatch("dpms on")
|
||||
if (isDwl) return _dwlPowerOnMonitors()
|
||||
if (isSway) { try { I3.dispatch("output * dpms on") } catch(_){} return }
|
||||
if (isLabwc) { Quickshell.execDetached(["dms", "dpms", "on"]) }
|
||||
console.warn("CompositorService: Cannot power on monitors, unknown compositor")
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
const oldValue = deviceBrightness[device.id]
|
||||
const newBrightness = Object.assign({}, deviceBrightness)
|
||||
newBrightness[device.id] = displayValue
|
||||
deviceBrightness = newBrightness
|
||||
brightnessVersion++
|
||||
|
||||
if (oldValue !== undefined && oldValue !== displayValue && brightnessInitialized) {
|
||||
brightnessChanged(true)
|
||||
}
|
||||
}
|
||||
|
||||
function updateFromBrightnessState(state) {
|
||||
@@ -110,9 +115,12 @@ Singleton {
|
||||
deviceMaxCache = newMaxCache
|
||||
|
||||
const newBrightness = {}
|
||||
let anyDeviceBrightnessChanged = false
|
||||
|
||||
for (const device of state.devices) {
|
||||
const isExponential = SessionData.getBrightnessExponential(device.id)
|
||||
const userSetValue = deviceBrightnessUserSet[device.id]
|
||||
const oldValue = deviceBrightness[device.id]
|
||||
|
||||
if (isExponential) {
|
||||
if (userSetValue !== undefined) {
|
||||
@@ -123,6 +131,11 @@ Singleton {
|
||||
} else {
|
||||
newBrightness[device.id] = device.currentPercent
|
||||
}
|
||||
|
||||
const newValue = newBrightness[device.id]
|
||||
if (oldValue !== undefined && oldValue !== newValue) {
|
||||
anyDeviceBrightnessChanged = true
|
||||
}
|
||||
}
|
||||
deviceBrightness = newBrightness
|
||||
brightnessVersion++
|
||||
@@ -142,9 +155,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
const shouldShowOsd = brightnessInitialized && anyDeviceBrightnessChanged
|
||||
|
||||
if (!brightnessInitialized) {
|
||||
brightnessInitialized = true
|
||||
}
|
||||
|
||||
if (shouldShowOsd) {
|
||||
brightnessChanged(true)
|
||||
}
|
||||
}
|
||||
|
||||
function setBrightness(percentage, device, suppressOsd) {
|
||||
|
||||
@@ -46,8 +46,17 @@ Singleton {
|
||||
|
||||
const hasExtWorkspace = DMSService.capabilities.includes("extworkspace")
|
||||
if (hasExtWorkspace && !extWorkspaceAvailable) {
|
||||
if (typeof CompositorService !== "undefined") {
|
||||
const useExtWorkspace = DMSService.forceExtWorkspace || (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isDwl && !CompositorService.isSway)
|
||||
if (!useExtWorkspace) {
|
||||
console.info("ExtWorkspaceService: ext-workspace available but compositor has native support")
|
||||
extWorkspaceAvailable = false
|
||||
return
|
||||
}
|
||||
}
|
||||
extWorkspaceAvailable = true
|
||||
console.info("ExtWorkspaceService: ext-workspace capability detected")
|
||||
DMSService.addSubscription("extworkspace")
|
||||
requestState()
|
||||
} else if (!hasExtWorkspace) {
|
||||
extWorkspaceAvailable = false
|
||||
@@ -181,12 +190,17 @@ Singleton {
|
||||
|
||||
function getVisibleWorkspaces(outputName) {
|
||||
const workspaces = getWorkspacesForOutput(outputName)
|
||||
const visible = workspaces.filter(ws => !ws.hidden).sort((a, b) => {
|
||||
const coordsA = a.coordinates || [0, 0]
|
||||
const coordsB = b.coordinates || [0, 0]
|
||||
if (coordsA[0] !== coordsB[0]) return coordsA[0] - coordsB[0]
|
||||
return coordsA[1] - coordsB[1]
|
||||
})
|
||||
let visible = workspaces.filter(ws => !ws.hidden)
|
||||
|
||||
const hasValidCoordinates = visible.some(ws => ws.coordinates && ws.coordinates.length > 0)
|
||||
if (hasValidCoordinates) {
|
||||
visible = visible.sort((a, b) => {
|
||||
const coordsA = a.coordinates || [0, 0]
|
||||
const coordsB = b.coordinates || [0, 0]
|
||||
if (coordsA[0] !== coordsB[0]) return coordsA[0] - coordsB[0]
|
||||
return coordsA[1] - coordsB[1]
|
||||
})
|
||||
}
|
||||
|
||||
const cacheKey = outputName
|
||||
if (!_cachedWorkspaces[cacheKey]) {
|
||||
|
||||
@@ -23,6 +23,7 @@ Singleton {
|
||||
property var colorPickerModal: null
|
||||
property var notificationModal: null
|
||||
property var wifiPasswordModal: null
|
||||
property var bluetoothPairingModal: null
|
||||
property var networkInfoModal: null
|
||||
|
||||
property var notepadSlideouts: []
|
||||
|
||||
@@ -1 +1 @@
|
||||
v0.5.2
|
||||
v0.6.0
|
||||
|
||||
@@ -75,6 +75,8 @@ PanelWindow {
|
||||
readonly property real alignedWidth: Theme.px(osdWidth, dpr)
|
||||
readonly property real alignedHeight: Theme.px(osdHeight, dpr)
|
||||
|
||||
readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter
|
||||
|
||||
readonly property real barThickness: {
|
||||
if (!SettingsData.dankBarVisible) return 0
|
||||
const widgetThickness = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
|
||||
@@ -86,6 +88,16 @@ PanelWindow {
|
||||
return barThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
|
||||
}
|
||||
|
||||
readonly property real dockThickness: {
|
||||
if (!SettingsData.showDock) return 0
|
||||
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10
|
||||
}
|
||||
|
||||
readonly property real dockOffset: {
|
||||
if (!SettingsData.showDock || SettingsData.dockAutoHide) return 0
|
||||
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin
|
||||
}
|
||||
|
||||
readonly property real alignedX: {
|
||||
const margin = Theme.spacingM
|
||||
const centerX = (screenWidth - alignedWidth) / 2
|
||||
@@ -93,12 +105,22 @@ PanelWindow {
|
||||
switch (SettingsData.osdPosition) {
|
||||
case SettingsData.Position.Left:
|
||||
case SettingsData.Position.Bottom:
|
||||
const leftOffset = SettingsData.dankBarPosition === SettingsData.Position.Left ? barOffset : 0
|
||||
return Theme.snap(margin + leftOffset, dpr)
|
||||
const leftBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Left ? barOffset : 0
|
||||
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr)
|
||||
case SettingsData.Position.Top:
|
||||
case SettingsData.Position.Right:
|
||||
const rightOffset = SettingsData.dankBarPosition === SettingsData.Position.Right ? barOffset : 0
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - rightOffset, dpr)
|
||||
const rightBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Right ? barOffset : 0
|
||||
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr)
|
||||
case SettingsData.Position.LeftCenter:
|
||||
const leftCenterBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Left ? barOffset : 0
|
||||
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr)
|
||||
case SettingsData.Position.RightCenter:
|
||||
const rightCenterBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Right ? barOffset : 0
|
||||
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0
|
||||
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr)
|
||||
case SettingsData.Position.TopCenter:
|
||||
case SettingsData.Position.BottomCenter:
|
||||
default:
|
||||
@@ -108,19 +130,25 @@ PanelWindow {
|
||||
|
||||
readonly property real alignedY: {
|
||||
const margin = Theme.spacingM
|
||||
const centerY = (screenHeight - alignedHeight) / 2
|
||||
|
||||
switch (SettingsData.osdPosition) {
|
||||
case SettingsData.Position.Top:
|
||||
case SettingsData.Position.Left:
|
||||
case SettingsData.Position.TopCenter:
|
||||
const topOffset = SettingsData.dankBarPosition === SettingsData.Position.Top ? barOffset : 0
|
||||
return Theme.snap(margin + topOffset, dpr)
|
||||
const topBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Top ? barOffset : 0
|
||||
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0
|
||||
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr)
|
||||
case SettingsData.Position.Right:
|
||||
case SettingsData.Position.Bottom:
|
||||
case SettingsData.Position.BottomCenter:
|
||||
const bottomBarOffset = SettingsData.dankBarPosition === SettingsData.Position.Bottom ? barOffset : 0
|
||||
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0
|
||||
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr)
|
||||
case SettingsData.Position.LeftCenter:
|
||||
case SettingsData.Position.RightCenter:
|
||||
default:
|
||||
const bottomOffset = SettingsData.dankBarPosition === SettingsData.Position.Bottom ? barOffset : 0
|
||||
return Theme.snap(screenHeight - alignedHeight - margin - bottomOffset, dpr)
|
||||
return Theme.snap(centerY, dpr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
{
|
||||
"term": "All",
|
||||
"context": "All",
|
||||
"reference": "Services/AppSearchService.qml:217, Services/AppSearchService.qml:233, Modals/Spotlight/SpotlightModal.qml:61, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:16, Modules/AppDrawer/AppLauncher.qml:27, Modules/AppDrawer/AppLauncher.qml:28, Modules/AppDrawer/AppLauncher.qml:45, Modules/AppDrawer/AppLauncher.qml:46, Modules/AppDrawer/AppLauncher.qml:80, Modules/AppDrawer/AppDrawerPopout.qml:46",
|
||||
"reference": "Services/AppSearchService.qml:310, Services/AppSearchService.qml:326, Modals/Spotlight/SpotlightModal.qml:61, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:16, Modules/AppDrawer/AppLauncher.qml:27, Modules/AppDrawer/AppLauncher.qml:28, Modules/AppDrawer/AppLauncher.qml:45, Modules/AppDrawer/AppLauncher.qml:46, Modules/AppDrawer/AppLauncher.qml:80, Modules/AppDrawer/AppDrawerPopout.qml:46",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -254,7 +254,7 @@
|
||||
{
|
||||
"term": "Audio Codec",
|
||||
"context": "Audio Codec",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:570",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:572",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -302,19 +302,19 @@
|
||||
{
|
||||
"term": "Authorize",
|
||||
"context": "Authorize",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:294",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:314, Modals/BluetoothPairingModal.qml:317",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Authorize pairing with ",
|
||||
"context": "Authorize pairing with ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:116",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:129",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Authorize service for ",
|
||||
"context": "Authorize service for ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:118",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:136",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -446,7 +446,7 @@
|
||||
{
|
||||
"term": "Back",
|
||||
"context": "Back",
|
||||
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:1191",
|
||||
"reference": "Modules/DankBar/Widgets/SystemTrayBar.qml:1182",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -482,7 +482,7 @@
|
||||
{
|
||||
"term": "Bluetooth Settings",
|
||||
"context": "Bluetooth Settings",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:71",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:73",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -548,7 +548,7 @@
|
||||
{
|
||||
"term": "Brightness OSD",
|
||||
"context": "Brightness OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:805",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:815",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -614,7 +614,7 @@
|
||||
{
|
||||
"term": "Cancel",
|
||||
"context": "Cancel",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:251, Modals/PolkitAuthModal.qml:291, Modals/WifiPasswordModal.qml:494, Modals/FileBrowser/FileBrowserOverwriteDialog.qml:83, Modules/Settings/PluginBrowser.qml:650",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:266, Modals/PolkitAuthModal.qml:291, Modals/WifiPasswordModal.qml:494, Modals/FileBrowser/FileBrowserOverwriteDialog.qml:83, Modules/Settings/PluginBrowser.qml:650",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -632,7 +632,7 @@
|
||||
{
|
||||
"term": "Caps Lock OSD",
|
||||
"context": "Caps Lock OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:835",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:845",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -848,7 +848,7 @@
|
||||
{
|
||||
"term": "Confirm",
|
||||
"context": "Confirm",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:292",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:312",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -860,7 +860,7 @@
|
||||
{
|
||||
"term": "Confirm passkey for ",
|
||||
"context": "Confirm passkey for ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:114",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:125",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1190,7 +1190,7 @@
|
||||
{
|
||||
"term": "Development",
|
||||
"context": "Development",
|
||||
"reference": "Services/AppSearchService.qml:161, Services/AppSearchService.qml:162, Services/AppSearchService.qml:163, Widgets/DankIconPicker.qml:31",
|
||||
"reference": "Services/AppSearchService.qml:254, Services/AppSearchService.qml:255, Services/AppSearchService.qml:256, Widgets/DankIconPicker.qml:31",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1202,7 +1202,7 @@
|
||||
{
|
||||
"term": "Device paired",
|
||||
"context": "Device paired",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:42",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:44",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1394,7 +1394,7 @@
|
||||
{
|
||||
"term": "Education",
|
||||
"context": "Education",
|
||||
"reference": "Services/AppSearchService.qml:164",
|
||||
"reference": "Services/AppSearchService.qml:257",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1466,19 +1466,19 @@
|
||||
{
|
||||
"term": "Enter 6-digit passkey",
|
||||
"context": "Enter 6-digit passkey",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:190",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:205",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enter PIN",
|
||||
"context": "Enter PIN",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:155",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:170",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enter PIN for ",
|
||||
"context": "Enter PIN for ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:120",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:131",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1514,7 +1514,7 @@
|
||||
{
|
||||
"term": "Enter passkey for ",
|
||||
"context": "Enter passkey for ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:122",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:133",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1523,6 +1523,12 @@
|
||||
"reference": "Modals/WifiPasswordModal.qml:200, Modals/WifiPasswordModal.qml:202",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enter this passkey on ",
|
||||
"context": "Enter this passkey on ",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:127",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Error",
|
||||
"context": "Error",
|
||||
@@ -1610,7 +1616,7 @@
|
||||
{
|
||||
"term": "Failed to remove device",
|
||||
"context": "Failed to remove device",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:617",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:619",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1724,7 +1730,7 @@
|
||||
{
|
||||
"term": "Forget Device",
|
||||
"context": "Forget Device",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:595",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:597",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1772,7 +1778,7 @@
|
||||
{
|
||||
"term": "Games",
|
||||
"context": "Games",
|
||||
"reference": "Services/AppSearchService.qml:165",
|
||||
"reference": "Services/AppSearchService.qml:258",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1814,7 +1820,7 @@
|
||||
{
|
||||
"term": "Graphics",
|
||||
"context": "Graphics",
|
||||
"reference": "Services/AppSearchService.qml:166, Services/AppSearchService.qml:167",
|
||||
"reference": "Services/AppSearchService.qml:259, Services/AppSearchService.qml:260",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -1940,7 +1946,7 @@
|
||||
{
|
||||
"term": "Idle Inhibitor OSD",
|
||||
"context": "Idle Inhibitor OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:815",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:825",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2012,7 +2018,7 @@
|
||||
{
|
||||
"term": "Internet",
|
||||
"context": "Internet",
|
||||
"reference": "Services/AppSearchService.qml:168, Services/AppSearchService.qml:169, Services/AppSearchService.qml:170",
|
||||
"reference": "Services/AppSearchService.qml:261, Services/AppSearchService.qml:262, Services/AppSearchService.qml:263",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2306,7 +2312,7 @@
|
||||
{
|
||||
"term": "Media",
|
||||
"context": "Media",
|
||||
"reference": "Services/AppSearchService.qml:158, Services/AppSearchService.qml:159, Services/AppSearchService.qml:160, Widgets/DankIconPicker.qml:37, Modules/DankDash/DankDashPopout.qml:210",
|
||||
"reference": "Services/AppSearchService.qml:251, Services/AppSearchService.qml:252, Services/AppSearchService.qml:253, Widgets/DankIconPicker.qml:37, Modules/DankDash/DankDashPopout.qml:210",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2378,7 +2384,7 @@
|
||||
{
|
||||
"term": "Microphone Mute OSD",
|
||||
"context": "Microphone Mute OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:825",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:835",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2564,7 +2570,7 @@
|
||||
{
|
||||
"term": "No Bluetooth adapter found",
|
||||
"context": "No Bluetooth adapter found",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:519",
|
||||
"reference": "Modules/ControlCenter/Details/BluetoothDetail.qml:521",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2642,7 +2648,7 @@
|
||||
{
|
||||
"term": "Notepad",
|
||||
"context": "Notepad",
|
||||
"reference": "DMSShell.qml:413, Modules/Settings/DankBarTab.qml:191",
|
||||
"reference": "DMSShell.qml:421, Modules/Settings/DankBarTab.qml:191",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2726,7 +2732,7 @@
|
||||
{
|
||||
"term": "Office",
|
||||
"context": "Office",
|
||||
"reference": "Services/AppSearchService.qml:171, Services/AppSearchService.qml:172, Services/AppSearchService.qml:173, Services/AppSearchService.qml:174",
|
||||
"reference": "Services/AppSearchService.qml:264, Services/AppSearchService.qml:265, Services/AppSearchService.qml:266, Services/AppSearchService.qml:267",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -2828,25 +2834,25 @@
|
||||
{
|
||||
"term": "Pair",
|
||||
"context": "Pair",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:295",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:318",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Pair Bluetooth Device",
|
||||
"context": "Pair Bluetooth Device",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:105",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:115",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Pairing failed",
|
||||
"context": "Pairing failed",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:356, Modules/ControlCenter/Details/BluetoothDetail.qml:40",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:394, Modules/ControlCenter/Details/BluetoothDetail.qml:42",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Passkey:",
|
||||
"context": "Passkey:",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:214",
|
||||
"reference": "Modals/BluetoothPairingModal.qml:229",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3044,7 +3050,7 @@
|
||||
{
|
||||
"term": "Power Profile OSD",
|
||||
"context": "Power Profile OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:845",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:855",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3344,7 +3350,7 @@
|
||||
{
|
||||
"term": "Science",
|
||||
"context": "Science",
|
||||
"reference": "Services/AppSearchService.qml:175",
|
||||
"reference": "Services/AppSearchService.qml:268",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3494,7 +3500,7 @@
|
||||
{
|
||||
"term": "Settings",
|
||||
"context": "Settings",
|
||||
"reference": "Services/AppSearchService.qml:176, Modals/Settings/SettingsModal.qml:205, Modules/DankDash/DankDashPopout.qml:225",
|
||||
"reference": "Services/AppSearchService.qml:269, Modals/Settings/SettingsModal.qml:205, Modules/DankDash/DankDashPopout.qml:225",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3620,37 +3626,37 @@
|
||||
{
|
||||
"term": "Show on-screen display when brightness changes",
|
||||
"context": "Show on-screen display when brightness changes",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:806",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:816",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Show on-screen display when caps lock state changes",
|
||||
"context": "Show on-screen display when caps lock state changes",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:836",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:846",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Show on-screen display when idle inhibitor state changes",
|
||||
"context": "Show on-screen display when idle inhibitor state changes",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:816",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:826",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Show on-screen display when microphone is muted/unmuted",
|
||||
"context": "Show on-screen display when microphone is muted/unmuted",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:826",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:836",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Show on-screen display when power profile changes",
|
||||
"context": "Show on-screen display when power profile changes",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:846",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:856",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Show on-screen display when volume changes",
|
||||
"context": "Show on-screen display when volume changes",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:796",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:806",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -3872,7 +3878,7 @@
|
||||
{
|
||||
"term": "System",
|
||||
"context": "System",
|
||||
"reference": "Services/AppSearchService.qml:177, Widgets/DankIconPicker.qml:40, Modules/ProcessList/SystemTab.qml:130",
|
||||
"reference": "Services/AppSearchService.qml:270, Widgets/DankIconPicker.qml:40, Modules/ProcessList/SystemTab.qml:130",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -4280,7 +4286,7 @@
|
||||
{
|
||||
"term": "Utilities",
|
||||
"context": "Utilities",
|
||||
"reference": "Services/AppSearchService.qml:178, Services/AppSearchService.qml:179, Services/AppSearchService.qml:180, Services/AppSearchService.qml:181",
|
||||
"reference": "Services/AppSearchService.qml:271, Services/AppSearchService.qml:272, Services/AppSearchService.qml:273, Services/AppSearchService.qml:274",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
@@ -4364,7 +4370,7 @@
|
||||
{
|
||||
"term": "Volume OSD",
|
||||
"context": "Volume OSD",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:795",
|
||||
"reference": "Modules/Settings/WidgetTweaksTab.qml:805",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"Add a VPN in NetworkManager": "Aggiungi una VPN in NetworkManager"
|
||||
},
|
||||
"Adjust the number of columns in grid view mode.": {
|
||||
"Adjust the number of columns in grid view mode.": ""
|
||||
"Adjust the number of columns in grid view mode.": "Regola il numero di colonne nella modalità di visualizzazione a griglia."
|
||||
},
|
||||
"All": {
|
||||
"All": "Tutto"
|
||||
@@ -306,7 +306,7 @@
|
||||
"CUPS Missing Filter Warning": "Avviso Filtro Mancante CUPS"
|
||||
},
|
||||
"Camera": {
|
||||
"Camera": ""
|
||||
"Camera": "Fotocamera"
|
||||
},
|
||||
"Cancel": {
|
||||
"Cancel": "Annulla"
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "Trascina i widget per riordinarli nelle sezioni. Usa l'icona occhio per nascondere/mostrare i widget (mantenendo la spaziatura), o X per rimuoverli completamente."
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "Duplica Sfondo con Sfocatura"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "Durata"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "Inserisci password per"
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "Errore"
|
||||
},
|
||||
@@ -915,7 +918,7 @@
|
||||
"Grid": "Griglia"
|
||||
},
|
||||
"Grid Columns": {
|
||||
"Grid Columns": ""
|
||||
"Grid Columns": "Colonne Griglia"
|
||||
},
|
||||
"Group by App": {
|
||||
"Group by App": "Raggruppa per App"
|
||||
@@ -1191,7 +1194,7 @@
|
||||
"Memory usage indicator": "Indicatore uso memoria"
|
||||
},
|
||||
"Microphone": {
|
||||
"Microphone": ""
|
||||
"Microphone": "Microfono"
|
||||
},
|
||||
"Microphone Mute OSD": {
|
||||
"Microphone Mute OSD": ""
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "Modalità:"
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "Modello"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "Selezione Monitor:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "NM non supportato"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "Nome"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "Icone Workspace con Nome"
|
||||
@@ -1683,7 +1686,7 @@
|
||||
"Science": "Scienza"
|
||||
},
|
||||
"Screen sharing": {
|
||||
"Screen sharing": ""
|
||||
"Screen sharing": "Condivisione schermo"
|
||||
},
|
||||
"Scrolling": {
|
||||
"Scrolling": "Scorrimento"
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
"Choose where notification popups appear on screen": "通知ポップアップが画面に表示される場所を選ぶ"
|
||||
},
|
||||
"Choose where on-screen displays appear on screen": {
|
||||
"Choose where on-screen displays appear on screen": ""
|
||||
"Choose where on-screen displays appear on screen": "OSDの表示する場所を選んでください"
|
||||
},
|
||||
"Clear": {
|
||||
"Clear": "クリア"
|
||||
@@ -627,7 +627,7 @@
|
||||
"Dismiss": "解除"
|
||||
},
|
||||
"Display Name Format": {
|
||||
"Display Name Format": ""
|
||||
"Display Name Format": "名称形式を表示"
|
||||
},
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "画面の上、下、左、右の端に配置できる、ピン留めされた実行中のアプリケーションを含むドックを表示します"
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "ウィジェットをドラッグしてセクション内で順序を変更できます。目のアイコンでウィジェットを表示/非表示に(スペースは維持)、Xで完全に削除できます。"
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "ぼかしで壁紙を複製"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "期間"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "パスワードを入力"
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": "ここでパスキーを入力してください "
|
||||
},
|
||||
"Error": {
|
||||
"Error": "エラー"
|
||||
},
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "モード: "
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "モデル"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "モニターの選択:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "NMが利用できません"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "名称"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "名前付きワークスペースアイコン"
|
||||
@@ -1365,7 +1368,7 @@
|
||||
"OS Logo": "OSロゴ"
|
||||
},
|
||||
"OSD Position": {
|
||||
"OSD Position": ""
|
||||
"OSD Position": "OSD位置"
|
||||
},
|
||||
"Office": {
|
||||
"Office": "オフィス"
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
"Choose where notification popups appear on screen": "Wybierz, gdzie na ekranie mają pojawiać się powiadomienia"
|
||||
},
|
||||
"Choose where on-screen displays appear on screen": {
|
||||
"Choose where on-screen displays appear on screen": ""
|
||||
"Choose where on-screen displays appear on screen": "Wybierz miejsce wyświetlania informacji na ekranie"
|
||||
},
|
||||
"Clear": {
|
||||
"Clear": "Wyczyść"
|
||||
@@ -627,7 +627,7 @@
|
||||
"Dismiss": "Odrzuć"
|
||||
},
|
||||
"Display Name Format": {
|
||||
"Display Name Format": ""
|
||||
"Display Name Format": "Format nazwy wyświetlanej"
|
||||
},
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "Wyświetla dok z przypiętymi i uruchomionymi aplikacjami, który można umieścić na górze, na dole, po lewej lub po prawej stronie ekranu."
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "Przeciągnij widżety, aby zmienić kolejność w sekcjach. Użyj ikony oka, aby ukryć/pokazać widżety (zachowując odstępy), lub X, aby je całkowicie usunąć."
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "Powiel tapetę z rozmyciem"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "Czas trwania"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "Wprowadź hasło dla "
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "Błąd"
|
||||
},
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "Tryb: "
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "Model"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "Wybór monitora:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "NM nie jest obsługiwany"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "Nazwa"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "Ikony nazwanych obszarów roboczych"
|
||||
@@ -1365,7 +1368,7 @@
|
||||
"OS Logo": "Logo OS"
|
||||
},
|
||||
"OSD Position": {
|
||||
"OSD Position": ""
|
||||
"OSD Position": "Pozycja OSD"
|
||||
},
|
||||
"Office": {
|
||||
"Office": "Biuro"
|
||||
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "Insira senha para "
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": ""
|
||||
},
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
"Brightness": "Parlaklık"
|
||||
},
|
||||
"Brightness OSD": {
|
||||
"Brightness OSD": ""
|
||||
"Brightness OSD": "Parlaklık OSD"
|
||||
},
|
||||
"Browse": {
|
||||
"Browse": "Göz at"
|
||||
@@ -318,7 +318,7 @@
|
||||
"Caps Lock Indicator": "Caps Lock Göstergesi"
|
||||
},
|
||||
"Caps Lock OSD": {
|
||||
"Caps Lock OSD": ""
|
||||
"Caps Lock OSD": "Caps Lock OSD"
|
||||
},
|
||||
"Center Section": {
|
||||
"Center Section": "Orta Bölüm"
|
||||
@@ -348,7 +348,7 @@
|
||||
"Choose where notification popups appear on screen": "Bildirim açılır pencerelerinin ekranda nerede görüneceğini seçin"
|
||||
},
|
||||
"Choose where on-screen displays appear on screen": {
|
||||
"Choose where on-screen displays appear on screen": ""
|
||||
"Choose where on-screen displays appear on screen": "Ekran gösterimlerinin ekranda nerede gösterileceğini seç"
|
||||
},
|
||||
"Clear": {
|
||||
"Clear": "Temizle"
|
||||
@@ -627,7 +627,7 @@
|
||||
"Dismiss": "Reddet"
|
||||
},
|
||||
"Display Name Format": {
|
||||
"Display Name Format": ""
|
||||
"Display Name Format": "Ekran İsim Formatı"
|
||||
},
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "Ekranın üst, alt, sol veya sağ kenarına yerleştirilebilen, sabitlenmiş ve çalışan uygulamaları içeren bir dock görüntüleyin."
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "Widget'ları sürükleyerek bölümler içinde yeniden sıralayın. Göz simgesini kullanarak widget'ları gizleyin/gösterin (aralıkları korur) veya X simgesini kullanarak tamamen kaldırın."
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "Duvar kağıdını bulanıklık ile çoğalt"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "Süre"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "Parolayı girin "
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "Hata"
|
||||
},
|
||||
@@ -975,7 +978,7 @@
|
||||
"Idle Inhibitor": "Boşta Kalma Engelleyici"
|
||||
},
|
||||
"Idle Inhibitor OSD": {
|
||||
"Idle Inhibitor OSD": ""
|
||||
"Idle Inhibitor OSD": "Boşta Kalma Engelleyici OSD"
|
||||
},
|
||||
"Idle Settings": {
|
||||
"Idle Settings": "Boşta Kalma Ayarları"
|
||||
@@ -1194,7 +1197,7 @@
|
||||
"Microphone": "Mikrofon"
|
||||
},
|
||||
"Microphone Mute OSD": {
|
||||
"Microphone Mute OSD": ""
|
||||
"Microphone Mute OSD": "Mikrofon Sessiz OSD"
|
||||
},
|
||||
"Minimal palette built around a single hue.": {
|
||||
"Minimal palette built around a single hue.": "Tek bir renk tonu etrafında oluşturulmuş minimal palet."
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "Mod: "
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "Model"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "Monitör Seçimi:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "NM desteklenmiyor"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "İsim"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "Adlandırılmış Çalışma Alanı Simgeleri"
|
||||
@@ -1365,7 +1368,7 @@
|
||||
"OS Logo": "OS Logo"
|
||||
},
|
||||
"OSD Position": {
|
||||
"OSD Position": ""
|
||||
"OSD Position": "OSD Pozisyonu"
|
||||
},
|
||||
"Office": {
|
||||
"Office": "Ofis"
|
||||
@@ -1377,7 +1380,7 @@
|
||||
"On-Screen Displays": "Ekran Üstü Gösterimler"
|
||||
},
|
||||
"On-screen Displays": {
|
||||
"On-screen Displays": ""
|
||||
"On-screen Displays": "Ekran Gösterimleri"
|
||||
},
|
||||
"Only adjust gamma based on time or location rules.": {
|
||||
"Only adjust gamma based on time or location rules.": "Gamayı yalnızca zaman veya konum kurallarına göre ayarlayın."
|
||||
@@ -1527,7 +1530,7 @@
|
||||
"Power Profile Degradation": "Güç Profili Dejenerasyonu"
|
||||
},
|
||||
"Power Profile OSD": {
|
||||
"Power Profile OSD": ""
|
||||
"Power Profile OSD": "Güç Profili OSD"
|
||||
},
|
||||
"Pressure": {
|
||||
"Pressure": "Basınç"
|
||||
@@ -1818,22 +1821,22 @@
|
||||
"Show on screens:": "Şu ekranda göster:"
|
||||
},
|
||||
"Show on-screen display when brightness changes": {
|
||||
"Show on-screen display when brightness changes": ""
|
||||
"Show on-screen display when brightness changes": "Parlaklık değiştiğinde ekran gösterimi göster"
|
||||
},
|
||||
"Show on-screen display when caps lock state changes": {
|
||||
"Show on-screen display when caps lock state changes": ""
|
||||
"Show on-screen display when caps lock state changes": "Caps lock durumu değiştiğinde ekran gösterimi göster"
|
||||
},
|
||||
"Show on-screen display when idle inhibitor state changes": {
|
||||
"Show on-screen display when idle inhibitor state changes": ""
|
||||
"Show on-screen display when idle inhibitor state changes": "Boşta kalma engelleyici durumu değiştiğinde ekran gösterimi göster"
|
||||
},
|
||||
"Show on-screen display when microphone is muted/unmuted": {
|
||||
"Show on-screen display when microphone is muted/unmuted": ""
|
||||
"Show on-screen display when microphone is muted/unmuted": "Mikrofon sessize alındığında/sessizden çıkarıldığında ekran gösterimi göster"
|
||||
},
|
||||
"Show on-screen display when power profile changes": {
|
||||
"Show on-screen display when power profile changes": ""
|
||||
"Show on-screen display when power profile changes": "Güç profili değiştiğinde ekran gösterimi göster"
|
||||
},
|
||||
"Show on-screen display when volume changes": {
|
||||
"Show on-screen display when volume changes": ""
|
||||
"Show on-screen display when volume changes": "Ses değiştiğinde ekran gösterimi göster"
|
||||
},
|
||||
"Show only apps running in current workspace": {
|
||||
"Show only apps running in current workspace": "Yalnızca mevcut çalışma alanında çalışan uygulamaları göster"
|
||||
@@ -2196,7 +2199,7 @@
|
||||
"Volume Changed": "Ses Seviyesi Değişti"
|
||||
},
|
||||
"Volume OSD": {
|
||||
"Volume OSD": ""
|
||||
"Volume OSD": "Ses OSD"
|
||||
},
|
||||
"Volume, brightness, and other system OSDs": {
|
||||
"Volume, brightness, and other system OSDs": "Ses, parlaklık ve diğer sistem ekran üstü gösterimleri"
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
"Choose where notification popups appear on screen": "设置通知弹窗的出现位置"
|
||||
},
|
||||
"Choose where on-screen displays appear on screen": {
|
||||
"Choose where on-screen displays appear on screen": ""
|
||||
"Choose where on-screen displays appear on screen": "选择OSD在屏幕上出现的位置"
|
||||
},
|
||||
"Clear": {
|
||||
"Clear": "清除"
|
||||
@@ -627,7 +627,7 @@
|
||||
"Dismiss": "忽略"
|
||||
},
|
||||
"Display Name Format": {
|
||||
"Display Name Format": ""
|
||||
"Display Name Format": "显示名称格式"
|
||||
},
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "显示一个包含固定和运行中应用的程序坞,可放置在屏幕四边任意位置"
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "拖动组件以在各区块内重新排序。点击眼睛图标可隐藏/显示组件(保留间距),点击 X 可彻底移除。"
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "带模糊效果的壁纸复本"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "持续时间"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "输入密码"
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": "在该处输入此通行密钥 "
|
||||
},
|
||||
"Error": {
|
||||
"Error": "错误"
|
||||
},
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "模式: "
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "模型"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "选择显示器:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "不支持 NM"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "名称"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "已命名工作区图标"
|
||||
@@ -1365,7 +1368,7 @@
|
||||
"OS Logo": "系统 Logo"
|
||||
},
|
||||
"OSD Position": {
|
||||
"OSD Position": ""
|
||||
"OSD Position": "OSD位置"
|
||||
},
|
||||
"Office": {
|
||||
"Office": "办公"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"Add a VPN in NetworkManager": "新增VPN至網路管理器"
|
||||
},
|
||||
"Adjust the number of columns in grid view mode.": {
|
||||
"Adjust the number of columns in grid view mode.": ""
|
||||
"Adjust the number of columns in grid view mode.": "調整網格檢視模式中的欄數。"
|
||||
},
|
||||
"All": {
|
||||
"All": "所有"
|
||||
@@ -75,7 +75,7 @@
|
||||
"Always Show OSD Percentage": "OSD 始終顯示百分比"
|
||||
},
|
||||
"Always on icons": {
|
||||
"Always on icons": ""
|
||||
"Always on icons": "始終顯示圖示"
|
||||
},
|
||||
"Always show a minimum of 3 workspaces, even if fewer are available": {
|
||||
"Always show a minimum of 3 workspaces, even if fewer are available": "始終顯示至少 3 個工作區,即使可用的工作區較少"
|
||||
@@ -276,7 +276,7 @@
|
||||
"Brightness": "亮度"
|
||||
},
|
||||
"Brightness OSD": {
|
||||
"Brightness OSD": ""
|
||||
"Brightness OSD": "亮度 OSD"
|
||||
},
|
||||
"Browse": {
|
||||
"Browse": "瀏覽"
|
||||
@@ -306,7 +306,7 @@
|
||||
"CUPS Missing Filter Warning": "CUPS 缺少過濾器警告"
|
||||
},
|
||||
"Camera": {
|
||||
"Camera": ""
|
||||
"Camera": "相機"
|
||||
},
|
||||
"Cancel": {
|
||||
"Cancel": "取消"
|
||||
@@ -315,10 +315,10 @@
|
||||
"Capacity": "容量"
|
||||
},
|
||||
"Caps Lock Indicator": {
|
||||
"Caps Lock Indicator": ""
|
||||
"Caps Lock Indicator": "大小寫鎖定指示器"
|
||||
},
|
||||
"Caps Lock OSD": {
|
||||
"Caps Lock OSD": ""
|
||||
"Caps Lock OSD": "大小寫鎖定 OSD"
|
||||
},
|
||||
"Center Section": {
|
||||
"Center Section": "中間區塊"
|
||||
@@ -348,7 +348,7 @@
|
||||
"Choose where notification popups appear on screen": "選擇通知彈出視窗在螢幕上出現的位置"
|
||||
},
|
||||
"Choose where on-screen displays appear on screen": {
|
||||
"Choose where on-screen displays appear on screen": ""
|
||||
"Choose where on-screen displays appear on screen": "選擇螢幕顯示出現在螢幕上的位置"
|
||||
},
|
||||
"Clear": {
|
||||
"Clear": "清除"
|
||||
@@ -627,7 +627,7 @@
|
||||
"Dismiss": "忽略"
|
||||
},
|
||||
"Display Name Format": {
|
||||
"Display Name Format": ""
|
||||
"Display Name Format": "顯示名稱格式"
|
||||
},
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": {
|
||||
"Display a dock with pinned and running applications that can be positioned at the top, bottom, left, or right edge of the screen": "顯示一個帶有固定和正在運行的應用程式的 Dock,這些應用程式可以放置在螢幕的頂部、底部、左側或右側邊緣"
|
||||
@@ -645,7 +645,7 @@
|
||||
"Display currently focused application title": "顯示目前焦點應用程式的標題"
|
||||
},
|
||||
"Display power menu actions in a grid instead of a list": {
|
||||
"Display power menu actions in a grid instead of a list": ""
|
||||
"Display power menu actions in a grid instead of a list": "以網格而非列表顯示電源選單操作"
|
||||
},
|
||||
"Display settings for ": {
|
||||
"Display settings for ": "顯示設定適用於"
|
||||
@@ -687,7 +687,7 @@
|
||||
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "拖曳部件即可在版塊內重新排序。使用眼睛圖示隱藏/顯示部件 (會保持間距),或使用 X 將其完全移除。"
|
||||
},
|
||||
"Duplicate Wallpaper with Blur": {
|
||||
"Duplicate Wallpaper with Blur": ""
|
||||
"Duplicate Wallpaper with Blur": "模糊化重複桌布"
|
||||
},
|
||||
"Duration": {
|
||||
"Duration": "持續時間"
|
||||
@@ -764,6 +764,9 @@
|
||||
"Enter password for ": {
|
||||
"Enter password for ": "輸入密碼 "
|
||||
},
|
||||
"Enter this passkey on ": {
|
||||
"Enter this passkey on ": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "錯誤"
|
||||
},
|
||||
@@ -915,7 +918,7 @@
|
||||
"Grid": "網格"
|
||||
},
|
||||
"Grid Columns": {
|
||||
"Grid Columns": ""
|
||||
"Grid Columns": "網格欄數"
|
||||
},
|
||||
"Group by App": {
|
||||
"Group by App": "App 分組"
|
||||
@@ -939,7 +942,7 @@
|
||||
"Hibernate": "休眠"
|
||||
},
|
||||
"Hide Delay (ms)": {
|
||||
"Hide Delay (ms)": ""
|
||||
"Hide Delay (ms)": "隱藏延遲 (毫秒)"
|
||||
},
|
||||
"Hide the dock when not in use and reveal it when hovering near the dock area": {
|
||||
"Hide the dock when not in use and reveal it when hovering near the dock area": "不使用時隱藏 Dock,並在 Dock 區域附近懸停時顯示 Dock"
|
||||
@@ -975,7 +978,7 @@
|
||||
"Idle Inhibitor": "空閒抑制器"
|
||||
},
|
||||
"Idle Inhibitor OSD": {
|
||||
"Idle Inhibitor OSD": ""
|
||||
"Idle Inhibitor OSD": "閒置抑制器 OSD"
|
||||
},
|
||||
"Idle Settings": {
|
||||
"Idle Settings": "閒置設定"
|
||||
@@ -1191,10 +1194,10 @@
|
||||
"Memory usage indicator": "記憶體使用率指示器"
|
||||
},
|
||||
"Microphone": {
|
||||
"Microphone": ""
|
||||
"Microphone": "麥克風"
|
||||
},
|
||||
"Microphone Mute OSD": {
|
||||
"Microphone Mute OSD": ""
|
||||
"Microphone Mute OSD": "麥克風靜音 OSD"
|
||||
},
|
||||
"Minimal palette built around a single hue.": {
|
||||
"Minimal palette built around a single hue.": "圍繞單一色調構建的最小調色板。"
|
||||
@@ -1209,7 +1212,7 @@
|
||||
"Mode: ": "模式:"
|
||||
},
|
||||
"Model": {
|
||||
"Model": ""
|
||||
"Model": "型號"
|
||||
},
|
||||
"Monitor Selection:": {
|
||||
"Monitor Selection:": "螢幕選擇:"
|
||||
@@ -1236,7 +1239,7 @@
|
||||
"NM not supported": "NM 不支援"
|
||||
},
|
||||
"Name": {
|
||||
"Name": ""
|
||||
"Name": "名稱"
|
||||
},
|
||||
"Named Workspace Icons": {
|
||||
"Named Workspace Icons": "命名工作區圖示"
|
||||
@@ -1365,7 +1368,7 @@
|
||||
"OS Logo": "發行版 Logo"
|
||||
},
|
||||
"OSD Position": {
|
||||
"OSD Position": ""
|
||||
"OSD Position": "OSD 位置"
|
||||
},
|
||||
"Office": {
|
||||
"Office": "辦公"
|
||||
@@ -1377,7 +1380,7 @@
|
||||
"On-Screen Displays": "螢幕顯示"
|
||||
},
|
||||
"On-screen Displays": {
|
||||
"On-screen Displays": ""
|
||||
"On-screen Displays": "螢幕顯示"
|
||||
},
|
||||
"Only adjust gamma based on time or location rules.": {
|
||||
"Only adjust gamma based on time or location rules.": "僅根據時間或位置規則調整 gamma。"
|
||||
@@ -1527,7 +1530,7 @@
|
||||
"Power Profile Degradation": "電源配置降級"
|
||||
},
|
||||
"Power Profile OSD": {
|
||||
"Power Profile OSD": ""
|
||||
"Power Profile OSD": "電源設定檔 OSD"
|
||||
},
|
||||
"Pressure": {
|
||||
"Pressure": "氣壓"
|
||||
@@ -1683,7 +1686,7 @@
|
||||
"Science": "科學"
|
||||
},
|
||||
"Screen sharing": {
|
||||
"Screen sharing": ""
|
||||
"Screen sharing": "螢幕分享"
|
||||
},
|
||||
"Scrolling": {
|
||||
"Scrolling": "滾動"
|
||||
@@ -1818,22 +1821,22 @@
|
||||
"Show on screens:": "在螢幕上顯示:"
|
||||
},
|
||||
"Show on-screen display when brightness changes": {
|
||||
"Show on-screen display when brightness changes": ""
|
||||
"Show on-screen display when brightness changes": "亮度改變時顯示螢幕顯示"
|
||||
},
|
||||
"Show on-screen display when caps lock state changes": {
|
||||
"Show on-screen display when caps lock state changes": ""
|
||||
"Show on-screen display when caps lock state changes": "大小寫鎖定狀態改變時顯示螢幕顯示"
|
||||
},
|
||||
"Show on-screen display when idle inhibitor state changes": {
|
||||
"Show on-screen display when idle inhibitor state changes": ""
|
||||
"Show on-screen display when idle inhibitor state changes": "閒置抑制器狀態改變時顯示螢幕顯示"
|
||||
},
|
||||
"Show on-screen display when microphone is muted/unmuted": {
|
||||
"Show on-screen display when microphone is muted/unmuted": ""
|
||||
"Show on-screen display when microphone is muted/unmuted": "麥克風靜音/取消靜音時顯示螢幕顯示"
|
||||
},
|
||||
"Show on-screen display when power profile changes": {
|
||||
"Show on-screen display when power profile changes": ""
|
||||
"Show on-screen display when power profile changes": "電源設定檔改變時顯示螢幕顯示"
|
||||
},
|
||||
"Show on-screen display when volume changes": {
|
||||
"Show on-screen display when volume changes": ""
|
||||
"Show on-screen display when volume changes": "音量改變時顯示螢幕顯示"
|
||||
},
|
||||
"Show only apps running in current workspace": {
|
||||
"Show only apps running in current workspace": "僅顯示目前工作區中正在執行的應用程式"
|
||||
@@ -1863,7 +1866,7 @@
|
||||
"Shows current workspace and allows switching": "顯示目前工作區並允許切換"
|
||||
},
|
||||
"Shows when caps lock is active": {
|
||||
"Shows when caps lock is active": ""
|
||||
"Shows when caps lock is active": "顯示大小寫鎖定啟用時"
|
||||
},
|
||||
"Shows when microphone, camera, or screen sharing is active": {
|
||||
"Shows when microphone, camera, or screen sharing is active": "顯示麥克風、攝影機或螢幕共用處於活動狀態"
|
||||
@@ -2109,7 +2112,7 @@
|
||||
"Use Fahrenheit instead of Celsius for temperature": "使用華氏度代替攝氏度來表示溫度"
|
||||
},
|
||||
"Use Grid Layout": {
|
||||
"Use Grid Layout": ""
|
||||
"Use Grid Layout": "使用網格佈局"
|
||||
},
|
||||
"Use IP Location": {
|
||||
"Use IP Location": "使用 IP 位置"
|
||||
@@ -2196,7 +2199,7 @@
|
||||
"Volume Changed": "音量改變"
|
||||
},
|
||||
"Volume OSD": {
|
||||
"Volume OSD": ""
|
||||
"Volume OSD": "音量 OSD"
|
||||
},
|
||||
"Volume, brightness, and other system OSDs": {
|
||||
"Volume, brightness, and other system OSDs": "音量、亮度及其他系統OSD"
|
||||
|
||||
@@ -1777,6 +1777,13 @@
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Enter this passkey on ",
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"term": "Error",
|
||||
"translation": "",
|
||||
|
||||
Reference in New Issue
Block a user