mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-26 22:42:50 -05:00
Compare commits
42 Commits
15dc91f779
...
dock-overf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d1d73a845 | ||
|
|
14a3a0ae55 | ||
|
|
2263338878 | ||
|
|
26bc5425d3 | ||
|
|
38b4d1dc95 | ||
|
|
3aaca7ff39 | ||
|
|
83d9808536 | ||
|
|
ad458dfece | ||
|
|
8f6fe7ed2b | ||
|
|
419a692593 | ||
|
|
03fdf795e0 | ||
|
|
832807a217 | ||
|
|
f7df3b2a68 | ||
|
|
0d03e73595 | ||
|
|
c5ae1a77d3 | ||
|
|
5f16624000 | ||
|
|
80025804ab | ||
|
|
028d3b4e61 | ||
|
|
9cce5ccfe6 | ||
|
|
a260b8060e | ||
|
|
f945307232 | ||
|
|
8f44d52cb2 | ||
|
|
3413cb7b89 | ||
|
|
4e3b24ffbb | ||
|
|
03cfa55e0b | ||
|
|
a887e60f40 | ||
|
|
816819bf9f | ||
|
|
78f3bb3812 | ||
|
|
01d7ed5dd8 | ||
|
|
50311db280 | ||
|
|
01b1a276c5 | ||
|
|
6d4c31492c | ||
|
|
f8c5f07e9f | ||
|
|
11e23feb0e | ||
|
|
b4ba2dac37 | ||
|
|
d013c3b718 | ||
|
|
b3ea28c5c4 | ||
|
|
775b381987 | ||
|
|
3a41f2f1ed | ||
|
|
972fc534a4 | ||
|
|
808ee66e11 | ||
|
|
3936a516f8 |
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature or improvement for DMS
|
||||
description: Suggest a new feature or improvement for DMS. Keep features focused on a single topic with clear benefits, examples, etc. Avoid vague or broad requests, they will be closed.
|
||||
labels:
|
||||
- enhancement
|
||||
body:
|
||||
|
||||
@@ -6,6 +6,8 @@ This file is more of a quick reference so I know what to account for before next
|
||||
- dbus API for plugins, KDEConnect
|
||||
- new dank16 algorithm
|
||||
- launcher actions, customize env, args, name, icon
|
||||
- launcher v2 - omega stuff, GIF search, supa powerful
|
||||
- dock on bar
|
||||
|
||||
# 1.2.0
|
||||
|
||||
|
||||
@@ -91,6 +91,9 @@ bind = SUPER CTRL, up, movetoworkspace, e-1
|
||||
bind = SUPER CTRL, U, movetoworkspace, e+1
|
||||
bind = SUPER CTRL, I, movetoworkspace, e-1
|
||||
|
||||
# === Workspace Management ===
|
||||
bind = CTRL SHIFT, R, exec, dms ipc call workspace-rename open
|
||||
|
||||
# === Move Workspaces ===
|
||||
bind = SUPER SHIFT, Page_Down, movetoworkspace, e+1
|
||||
bind = SUPER SHIFT, Page_Up, movetoworkspace, e-1
|
||||
|
||||
@@ -81,7 +81,6 @@ master {
|
||||
misc {
|
||||
disable_hyprland_logo = true
|
||||
disable_splash_rendering = true
|
||||
vrr = 1
|
||||
}
|
||||
|
||||
# ==================
|
||||
|
||||
@@ -133,6 +133,11 @@ binds {
|
||||
Mod+Ctrl+U { move-column-to-workspace-down; }
|
||||
Mod+Ctrl+I { move-column-to-workspace-up; }
|
||||
|
||||
// === Workspace Management ===
|
||||
Ctrl+Shift+R hotkey-overlay-title="Rename Workspace" {
|
||||
spawn "dms" "ipc" "call" "workspace-rename" "open";
|
||||
}
|
||||
|
||||
// === Move Workspaces ===
|
||||
Mod+Shift+Page_Down { move-workspace-down; }
|
||||
Mod+Shift+Page_Up { move-workspace-up; }
|
||||
|
||||
@@ -41,6 +41,9 @@ func init() {
|
||||
Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewArchDistribution(config, logChan)
|
||||
})
|
||||
Register("XeroLinux", "#888fe2", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewArchDistribution(config, logChan)
|
||||
})
|
||||
}
|
||||
|
||||
type ArchDistribution struct {
|
||||
|
||||
@@ -502,17 +502,17 @@ func (p *MangoWCParser) handleSource(line, baseDir string, keybinds *[]MangoWCKe
|
||||
p.dmsProcessed = true
|
||||
}
|
||||
|
||||
fullPath := sourcePath
|
||||
if !filepath.IsAbs(sourcePath) {
|
||||
fullPath = filepath.Join(baseDir, sourcePath)
|
||||
}
|
||||
|
||||
expanded, err := utils.ExpandPath(fullPath)
|
||||
expanded, err := utils.ExpandPath(sourcePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
includedBinds, err := p.parseFileWithSource(expanded)
|
||||
fullPath := expanded
|
||||
if !filepath.IsAbs(expanded) {
|
||||
fullPath = filepath.Join(baseDir, expanded)
|
||||
}
|
||||
|
||||
includedBinds, err := p.parseFileWithSource(fullPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -521,33 +521,10 @@ func (p *MangoWCParser) handleSource(line, baseDir string, keybinds *[]MangoWCKe
|
||||
}
|
||||
|
||||
func (p *MangoWCParser) parseDMSBindsDirectly(dmsBindsPath string) []MangoWCKeyBinding {
|
||||
data, err := os.ReadFile(dmsBindsPath)
|
||||
keybinds, err := p.parseFileWithSource(dmsBindsPath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
prevSource := p.currentSource
|
||||
p.currentSource = dmsBindsPath
|
||||
|
||||
var keybinds []MangoWCKeyBinding
|
||||
lines := strings.Split(string(data), "\n")
|
||||
|
||||
for lineNum, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if !strings.HasPrefix(trimmed, "bind") {
|
||||
continue
|
||||
}
|
||||
|
||||
kb := p.getKeybindAtLineContent(line, lineNum)
|
||||
if kb == nil {
|
||||
continue
|
||||
}
|
||||
kb.Source = dmsBindsPath
|
||||
p.addBind(kb)
|
||||
keybinds = append(keybinds, *kb)
|
||||
}
|
||||
|
||||
p.currentSource = prevSource
|
||||
p.dmsProcessed = true
|
||||
return keybinds
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||
serverPlugins "github.com/AvengeMedia/DankMaterialShell/core/internal/server/plugins"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode"
|
||||
serverThemes "github.com/AvengeMedia/DankMaterialShell/core/internal/server/themes"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
||||
@@ -44,6 +45,15 @@ func RouteRequest(conn net.Conn, req models.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "theme.auto.") {
|
||||
if themeModeManager == nil {
|
||||
models.RespondError(conn, req.ID, "theme mode manager not initialized")
|
||||
return
|
||||
}
|
||||
thememode.HandleRequest(conn, req, themeModeManager)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "loginctl.") {
|
||||
if loginctlManager == nil {
|
||||
models.RespondError(conn, req.ID, "loginctl manager not initialized")
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
||||
@@ -68,6 +69,7 @@ var evdevManager *evdev.Manager
|
||||
var clipboardManager *clipboard.Manager
|
||||
var dbusManager *serverDbus.Manager
|
||||
var wlContext *wlcontext.SharedContext
|
||||
var themeModeManager *thememode.Manager
|
||||
|
||||
const dbusClientID = "dms-dbus-client"
|
||||
|
||||
@@ -380,6 +382,14 @@ func InitializeDbusManager() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializeThemeModeManager() error {
|
||||
manager := thememode.NewManager()
|
||||
themeModeManager = manager
|
||||
|
||||
log.Info("Theme mode automation manager initialized")
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleConnection(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
@@ -457,6 +467,10 @@ func getCapabilities() Capabilities {
|
||||
caps = append(caps, "clipboard")
|
||||
}
|
||||
|
||||
if themeModeManager != nil {
|
||||
caps = append(caps, "theme.auto")
|
||||
}
|
||||
|
||||
if dbusManager != nil {
|
||||
caps = append(caps, "dbus")
|
||||
}
|
||||
@@ -519,6 +533,10 @@ func getServerInfo() ServerInfo {
|
||||
caps = append(caps, "clipboard")
|
||||
}
|
||||
|
||||
if themeModeManager != nil {
|
||||
caps = append(caps, "theme.auto")
|
||||
}
|
||||
|
||||
if dbusManager != nil {
|
||||
caps = append(caps, "dbus")
|
||||
}
|
||||
@@ -791,6 +809,38 @@ func handleSubscribe(conn net.Conn, req models.Request) {
|
||||
}()
|
||||
}
|
||||
|
||||
if shouldSubscribe("theme.auto") && themeModeManager != nil {
|
||||
wg.Add(1)
|
||||
themeAutoChan := themeModeManager.Subscribe(clientID + "-theme-auto")
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer themeModeManager.Unsubscribe(clientID + "-theme-auto")
|
||||
|
||||
initialState := themeModeManager.GetState()
|
||||
select {
|
||||
case eventChan <- ServiceEvent{Service: "theme.auto", Data: initialState}:
|
||||
case <-stopChan:
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case state, ok := <-themeAutoChan:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case eventChan <- ServiceEvent{Service: "theme.auto", Data: state}:
|
||||
case <-stopChan:
|
||||
return
|
||||
}
|
||||
case <-stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if shouldSubscribe("bluetooth") && bluezManager != nil {
|
||||
wg.Add(1)
|
||||
bluezChan := bluezManager.Subscribe(clientID + "-bluetooth")
|
||||
@@ -1251,6 +1301,9 @@ func cleanupManagers() {
|
||||
if dbusManager != nil {
|
||||
dbusManager.Close()
|
||||
}
|
||||
if themeModeManager != nil {
|
||||
themeModeManager.Close()
|
||||
}
|
||||
if wlContext != nil {
|
||||
wlContext.Close()
|
||||
}
|
||||
@@ -1346,6 +1399,15 @@ func Start(printDocs bool) error {
|
||||
log.Info(" wayland.gamma.setGamma - Set gamma value (params: gamma)")
|
||||
log.Info(" wayland.gamma.setEnabled - Enable/disable gamma control (params: enabled)")
|
||||
log.Info(" wayland.gamma.subscribe - Subscribe to gamma state changes (streaming)")
|
||||
log.Info("Theme automation:")
|
||||
log.Info(" theme.auto.getState - Get current theme automation state")
|
||||
log.Info(" theme.auto.setEnabled - Enable/disable theme automation (params: enabled)")
|
||||
log.Info(" theme.auto.setMode - Set automation mode (params: mode [time|location])")
|
||||
log.Info(" theme.auto.setSchedule - Set time schedule (params: startHour, startMinute, endHour, endMinute)")
|
||||
log.Info(" theme.auto.setLocation - Set location (params: latitude, longitude)")
|
||||
log.Info(" theme.auto.setUseIPLocation - Use IP location (params: use)")
|
||||
log.Info(" theme.auto.trigger - Trigger immediate re-evaluation")
|
||||
log.Info(" theme.auto.subscribe - Subscribe to theme automation state changes (streaming)")
|
||||
log.Info("Bluetooth:")
|
||||
log.Info(" bluetooth.getState - Get current bluetooth state")
|
||||
log.Info(" bluetooth.startDiscovery - Start device discovery")
|
||||
@@ -1503,6 +1565,12 @@ func Start(printDocs bool) error {
|
||||
log.Debugf("WlrOutput manager unavailable: %v", err)
|
||||
}
|
||||
|
||||
if err := InitializeThemeModeManager(); err != nil {
|
||||
log.Warnf("Theme mode manager unavailable: %v", err)
|
||||
} else {
|
||||
notifyCapabilityChange()
|
||||
}
|
||||
|
||||
fatalErrChan := make(chan error, 1)
|
||||
if wlrOutputManager != nil {
|
||||
go func() {
|
||||
|
||||
154
core/internal/server/thememode/handlers.go
Normal file
154
core/internal/server/thememode/handlers.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package thememode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
|
||||
)
|
||||
|
||||
func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
|
||||
if manager == nil {
|
||||
models.RespondError(conn, req.ID, "theme mode manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case "theme.auto.getState":
|
||||
handleGetState(conn, req, manager)
|
||||
case "theme.auto.setEnabled":
|
||||
handleSetEnabled(conn, req, manager)
|
||||
case "theme.auto.setMode":
|
||||
handleSetMode(conn, req, manager)
|
||||
case "theme.auto.setSchedule":
|
||||
handleSetSchedule(conn, req, manager)
|
||||
case "theme.auto.setLocation":
|
||||
handleSetLocation(conn, req, manager)
|
||||
case "theme.auto.setUseIPLocation":
|
||||
handleSetUseIPLocation(conn, req, manager)
|
||||
case "theme.auto.trigger":
|
||||
handleTrigger(conn, req, manager)
|
||||
case "theme.auto.subscribe":
|
||||
handleSubscribe(conn, req, manager)
|
||||
default:
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
|
||||
models.Respond(conn, req.ID, manager.GetState())
|
||||
}
|
||||
|
||||
func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) {
|
||||
enabled, err := params.Bool(req.Params, "enabled")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
manager.SetEnabled(enabled)
|
||||
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto enabled set"})
|
||||
}
|
||||
|
||||
func handleSetMode(conn net.Conn, req models.Request, manager *Manager) {
|
||||
mode, err := params.String(req.Params, "mode")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if mode != "time" && mode != "location" {
|
||||
models.RespondError(conn, req.ID, "invalid mode")
|
||||
return
|
||||
}
|
||||
|
||||
manager.SetMode(mode)
|
||||
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto mode set"})
|
||||
}
|
||||
|
||||
func handleSetSchedule(conn net.Conn, req models.Request, manager *Manager) {
|
||||
startHour, err := params.Int(req.Params, "startHour")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
startMinute, err := params.Int(req.Params, "startMinute")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
endHour, err := params.Int(req.Params, "endHour")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
endMinute, err := params.Int(req.Params, "endMinute")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := manager.ValidateSchedule(startHour, startMinute, endHour, endMinute); err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
manager.SetSchedule(startHour, startMinute, endHour, endMinute)
|
||||
models.Respond(conn, req.ID, manager.GetState())
|
||||
}
|
||||
|
||||
func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) {
|
||||
lat, err := params.Float(req.Params, "latitude")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
lon, err := params.Float(req.Params, "longitude")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
manager.SetLocation(lat, lon)
|
||||
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto location set"})
|
||||
}
|
||||
|
||||
func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) {
|
||||
use, err := params.Bool(req.Params, "use")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
manager.SetUseIPLocation(use)
|
||||
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto IP location set"})
|
||||
}
|
||||
|
||||
func handleTrigger(conn net.Conn, req models.Request, manager *Manager) {
|
||||
manager.TriggerUpdate()
|
||||
models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto update triggered"})
|
||||
}
|
||||
|
||||
func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
|
||||
clientID := fmt.Sprintf("client-%p", conn)
|
||||
stateChan := manager.Subscribe(clientID)
|
||||
defer manager.Unsubscribe(clientID)
|
||||
|
||||
initialState := manager.GetState()
|
||||
if err := json.NewEncoder(conn).Encode(models.Response[State]{
|
||||
ID: req.ID,
|
||||
Result: &initialState,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for state := range stateChan {
|
||||
if err := json.NewEncoder(conn).Encode(models.Response[State]{
|
||||
Result: &state,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
432
core/internal/server/thememode/manager.go
Normal file
432
core/internal/server/thememode/manager.go
Normal file
@@ -0,0 +1,432 @@
|
||||
package thememode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultStartHour = 18
|
||||
defaultStartMinute = 0
|
||||
defaultEndHour = 6
|
||||
defaultEndMinute = 0
|
||||
defaultElevationTwilight = -6.0
|
||||
defaultElevationDaylight = 3.0
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
config Config
|
||||
configMutex sync.RWMutex
|
||||
|
||||
state *State
|
||||
stateMutex sync.RWMutex
|
||||
|
||||
subscribers syncmap.Map[string, chan State]
|
||||
|
||||
locationMutex sync.RWMutex
|
||||
cachedIPLat *float64
|
||||
cachedIPLon *float64
|
||||
|
||||
stopChan chan struct{}
|
||||
updateTrigger chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
m := &Manager{
|
||||
config: Config{
|
||||
Enabled: false,
|
||||
Mode: "time",
|
||||
StartHour: defaultStartHour,
|
||||
StartMinute: defaultStartMinute,
|
||||
EndHour: defaultEndHour,
|
||||
EndMinute: defaultEndMinute,
|
||||
ElevationTwilight: defaultElevationTwilight,
|
||||
ElevationDaylight: defaultElevationDaylight,
|
||||
},
|
||||
stopChan: make(chan struct{}),
|
||||
updateTrigger: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
m.updateState(time.Now())
|
||||
|
||||
m.wg.Add(1)
|
||||
go m.schedulerLoop()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manager) GetState() State {
|
||||
m.stateMutex.RLock()
|
||||
defer m.stateMutex.RUnlock()
|
||||
if m.state == nil {
|
||||
return State{Config: m.getConfig()}
|
||||
}
|
||||
stateCopy := *m.state
|
||||
return stateCopy
|
||||
}
|
||||
|
||||
func (m *Manager) Subscribe(id string) chan State {
|
||||
ch := make(chan State, 64)
|
||||
m.subscribers.Store(id, ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
func (m *Manager) Unsubscribe(id string) {
|
||||
if val, ok := m.subscribers.LoadAndDelete(id); ok {
|
||||
close(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) SetEnabled(enabled bool) {
|
||||
m.configMutex.Lock()
|
||||
if m.config.Enabled == enabled {
|
||||
m.configMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.config.Enabled = enabled
|
||||
m.configMutex.Unlock()
|
||||
m.TriggerUpdate()
|
||||
}
|
||||
|
||||
func (m *Manager) SetMode(mode string) {
|
||||
m.configMutex.Lock()
|
||||
if m.config.Mode == mode {
|
||||
m.configMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.config.Mode = mode
|
||||
m.configMutex.Unlock()
|
||||
m.TriggerUpdate()
|
||||
}
|
||||
|
||||
func (m *Manager) SetSchedule(startHour, startMinute, endHour, endMinute int) {
|
||||
m.configMutex.Lock()
|
||||
changed := m.config.StartHour != startHour ||
|
||||
m.config.StartMinute != startMinute ||
|
||||
m.config.EndHour != endHour ||
|
||||
m.config.EndMinute != endMinute
|
||||
if !changed {
|
||||
m.configMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.config.StartHour = startHour
|
||||
m.config.StartMinute = startMinute
|
||||
m.config.EndHour = endHour
|
||||
m.config.EndMinute = endMinute
|
||||
m.configMutex.Unlock()
|
||||
m.TriggerUpdate()
|
||||
}
|
||||
|
||||
func (m *Manager) SetLocation(lat, lon float64) {
|
||||
m.configMutex.Lock()
|
||||
if m.config.Latitude != nil && m.config.Longitude != nil &&
|
||||
*m.config.Latitude == lat && *m.config.Longitude == lon && !m.config.UseIPLocation {
|
||||
m.configMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.config.Latitude = &lat
|
||||
m.config.Longitude = &lon
|
||||
m.config.UseIPLocation = false
|
||||
m.configMutex.Unlock()
|
||||
|
||||
m.locationMutex.Lock()
|
||||
m.cachedIPLat = nil
|
||||
m.cachedIPLon = nil
|
||||
m.locationMutex.Unlock()
|
||||
|
||||
m.TriggerUpdate()
|
||||
}
|
||||
|
||||
func (m *Manager) SetUseIPLocation(use bool) {
|
||||
m.configMutex.Lock()
|
||||
if m.config.UseIPLocation == use {
|
||||
m.configMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.config.UseIPLocation = use
|
||||
if use {
|
||||
m.config.Latitude = nil
|
||||
m.config.Longitude = nil
|
||||
}
|
||||
m.configMutex.Unlock()
|
||||
|
||||
if use {
|
||||
m.locationMutex.Lock()
|
||||
m.cachedIPLat = nil
|
||||
m.cachedIPLon = nil
|
||||
m.locationMutex.Unlock()
|
||||
}
|
||||
|
||||
m.TriggerUpdate()
|
||||
}
|
||||
|
||||
func (m *Manager) TriggerUpdate() {
|
||||
select {
|
||||
case m.updateTrigger <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Close() {
|
||||
select {
|
||||
case <-m.stopChan:
|
||||
return
|
||||
default:
|
||||
close(m.stopChan)
|
||||
}
|
||||
m.wg.Wait()
|
||||
m.subscribers.Range(func(key string, ch chan State) bool {
|
||||
close(ch)
|
||||
m.subscribers.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) schedulerLoop() {
|
||||
defer m.wg.Done()
|
||||
|
||||
var timer *time.Timer
|
||||
for {
|
||||
config := m.getConfig()
|
||||
now := time.Now()
|
||||
var isLight bool
|
||||
var next time.Time
|
||||
if config.Enabled {
|
||||
isLight, next = m.computeSchedule(now, config)
|
||||
} else {
|
||||
m.stateMutex.RLock()
|
||||
if m.state != nil {
|
||||
isLight = m.state.IsLight
|
||||
}
|
||||
m.stateMutex.RUnlock()
|
||||
next = now.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
m.updateStateWithValues(config, isLight, next)
|
||||
|
||||
waitDur := time.Until(next)
|
||||
if !config.Enabled {
|
||||
waitDur = 24 * time.Hour
|
||||
}
|
||||
if waitDur < time.Second {
|
||||
waitDur = time.Second
|
||||
}
|
||||
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
timer = time.NewTimer(waitDur)
|
||||
|
||||
select {
|
||||
case <-m.stopChan:
|
||||
timer.Stop()
|
||||
return
|
||||
case <-m.updateTrigger:
|
||||
timer.Stop()
|
||||
continue
|
||||
case <-timer.C:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) updateState(now time.Time) {
|
||||
config := m.getConfig()
|
||||
var isLight bool
|
||||
var next time.Time
|
||||
if config.Enabled {
|
||||
isLight, next = m.computeSchedule(now, config)
|
||||
} else {
|
||||
m.stateMutex.RLock()
|
||||
if m.state != nil {
|
||||
isLight = m.state.IsLight
|
||||
}
|
||||
m.stateMutex.RUnlock()
|
||||
next = now.Add(24 * time.Hour)
|
||||
}
|
||||
m.updateStateWithValues(config, isLight, next)
|
||||
}
|
||||
|
||||
func (m *Manager) updateStateWithValues(config Config, isLight bool, next time.Time) {
|
||||
newState := State{
|
||||
Config: config,
|
||||
IsLight: isLight,
|
||||
NextTransition: next,
|
||||
}
|
||||
|
||||
m.stateMutex.Lock()
|
||||
if m.state != nil && statesEqual(m.state, &newState) {
|
||||
m.stateMutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.state = &newState
|
||||
m.stateMutex.Unlock()
|
||||
|
||||
m.notifySubscribers()
|
||||
}
|
||||
|
||||
func (m *Manager) notifySubscribers() {
|
||||
state := m.GetState()
|
||||
m.subscribers.Range(func(key string, ch chan State) bool {
|
||||
select {
|
||||
case ch <- state:
|
||||
default:
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) getConfig() Config {
|
||||
m.configMutex.RLock()
|
||||
defer m.configMutex.RUnlock()
|
||||
return m.config
|
||||
}
|
||||
|
||||
func (m *Manager) getLocation(config Config) (*float64, *float64) {
|
||||
if config.Latitude != nil && config.Longitude != nil {
|
||||
return config.Latitude, config.Longitude
|
||||
}
|
||||
if !config.UseIPLocation {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m.locationMutex.RLock()
|
||||
if m.cachedIPLat != nil && m.cachedIPLon != nil {
|
||||
lat, lon := m.cachedIPLat, m.cachedIPLon
|
||||
m.locationMutex.RUnlock()
|
||||
return lat, lon
|
||||
}
|
||||
m.locationMutex.RUnlock()
|
||||
|
||||
lat, lon, err := wayland.FetchIPLocation()
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m.locationMutex.Lock()
|
||||
m.cachedIPLat = lat
|
||||
m.cachedIPLon = lon
|
||||
m.locationMutex.Unlock()
|
||||
|
||||
return lat, lon
|
||||
}
|
||||
|
||||
func statesEqual(a, b *State) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
if a.IsLight != b.IsLight || !a.NextTransition.Equal(b.NextTransition) {
|
||||
return false
|
||||
}
|
||||
return a.Config == b.Config
|
||||
}
|
||||
|
||||
func (m *Manager) computeSchedule(now time.Time, config Config) (bool, time.Time) {
|
||||
if config.Mode == "location" {
|
||||
return m.computeLocationSchedule(now, config)
|
||||
}
|
||||
return computeTimeSchedule(now, config)
|
||||
}
|
||||
|
||||
func computeTimeSchedule(now time.Time, config Config) (bool, time.Time) {
|
||||
startMinutes := config.StartHour*60 + config.StartMinute
|
||||
endMinutes := config.EndHour*60 + config.EndMinute
|
||||
currentMinutes := now.Hour()*60 + now.Minute()
|
||||
|
||||
startTime := time.Date(now.Year(), now.Month(), now.Day(), config.StartHour, config.StartMinute, 0, 0, now.Location())
|
||||
endTime := time.Date(now.Year(), now.Month(), now.Day(), config.EndHour, config.EndMinute, 0, 0, now.Location())
|
||||
|
||||
if startMinutes == endMinutes {
|
||||
next := startTime
|
||||
if !next.After(now) {
|
||||
next = next.Add(24 * time.Hour)
|
||||
}
|
||||
return true, next
|
||||
}
|
||||
|
||||
if startMinutes < endMinutes {
|
||||
if currentMinutes < startMinutes {
|
||||
return true, startTime
|
||||
}
|
||||
if currentMinutes >= endMinutes {
|
||||
return true, startTime.Add(24 * time.Hour)
|
||||
}
|
||||
return false, endTime
|
||||
}
|
||||
|
||||
if currentMinutes >= startMinutes {
|
||||
return false, endTime.Add(24 * time.Hour)
|
||||
}
|
||||
if currentMinutes < endMinutes {
|
||||
return false, endTime
|
||||
}
|
||||
return true, startTime
|
||||
}
|
||||
|
||||
func (m *Manager) computeLocationSchedule(now time.Time, config Config) (bool, time.Time) {
|
||||
lat, lon := m.getLocation(config)
|
||||
if lat == nil || lon == nil {
|
||||
currentIsLight := false
|
||||
m.stateMutex.RLock()
|
||||
if m.state != nil {
|
||||
currentIsLight = m.state.IsLight
|
||||
}
|
||||
m.stateMutex.RUnlock()
|
||||
return currentIsLight, now.Add(10 * time.Minute)
|
||||
}
|
||||
|
||||
times, cond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight)
|
||||
if cond != wayland.SunNormal {
|
||||
if cond == wayland.SunMidnightSun {
|
||||
return true, startOfNextDay(now)
|
||||
}
|
||||
return false, startOfNextDay(now)
|
||||
}
|
||||
|
||||
if now.Before(times.Sunrise) {
|
||||
return false, times.Sunrise
|
||||
}
|
||||
if now.Before(times.Sunset) {
|
||||
return true, times.Sunset
|
||||
}
|
||||
|
||||
nextDay := startOfNextDay(now)
|
||||
nextTimes, nextCond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, nextDay, config.ElevationTwilight, config.ElevationDaylight)
|
||||
if nextCond != wayland.SunNormal {
|
||||
if nextCond == wayland.SunMidnightSun {
|
||||
return true, startOfNextDay(nextDay)
|
||||
}
|
||||
return false, startOfNextDay(nextDay)
|
||||
}
|
||||
|
||||
return false, nextTimes.Sunrise
|
||||
}
|
||||
|
||||
func startOfNextDay(t time.Time) time.Time {
|
||||
next := t.Add(24 * time.Hour)
|
||||
return time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location())
|
||||
}
|
||||
|
||||
func validateHourMinute(hour, minute int) bool {
|
||||
if hour < 0 || hour > 23 {
|
||||
return false
|
||||
}
|
||||
if minute < 0 || minute > 59 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Manager) ValidateSchedule(startHour, startMinute, endHour, endMinute int) error {
|
||||
if !validateHourMinute(startHour, startMinute) || !validateHourMinute(endHour, endMinute) {
|
||||
return errInvalidTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errInvalidTime = errors.New("invalid schedule time")
|
||||
23
core/internal/server/thememode/types.go
Normal file
23
core/internal/server/thememode/types.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package thememode
|
||||
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Mode string `json:"mode"`
|
||||
StartHour int `json:"startHour"`
|
||||
StartMinute int `json:"startMinute"`
|
||||
EndHour int `json:"endHour"`
|
||||
EndMinute int `json:"endMinute"`
|
||||
Latitude *float64 `json:"latitude,omitempty"`
|
||||
Longitude *float64 `json:"longitude,omitempty"`
|
||||
UseIPLocation bool `json:"useIPLocation"`
|
||||
ElevationTwilight float64 `json:"elevationTwilight"`
|
||||
ElevationDaylight float64 `json:"elevationDaylight"`
|
||||
}
|
||||
|
||||
type State struct {
|
||||
Config Config `json:"config"`
|
||||
IsLight bool `json:"isLight"`
|
||||
NextTransition time.Time `json:"nextTransition"`
|
||||
}
|
||||
@@ -626,6 +626,7 @@ func (m *Manager) schedulerLoop() {
|
||||
m.schedule.calcDay = time.Time{}
|
||||
m.scheduleMutex.Unlock()
|
||||
m.recalcSchedule(time.Now())
|
||||
m.updateStateFromSchedule()
|
||||
m.configMutex.RLock()
|
||||
enabled := m.config.Enabled
|
||||
m.configMutex.RUnlock()
|
||||
|
||||
@@ -109,8 +109,6 @@ rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
|
||||
echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
|
||||
|
||||
%posttrans
|
||||
# Signal running DMS instances to reload
|
||||
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
@@ -100,8 +100,6 @@ rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/core
|
||||
|
||||
echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
|
||||
|
||||
%posttrans
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1766651565,
|
||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||
"lastModified": 1769018530,
|
||||
"narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||
"rev": "88d3861acdd3d2f0e361767018218e51810df8a1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
kirigami.unwrapped
|
||||
sonnet
|
||||
qtmultimedia
|
||||
qtimageformats
|
||||
];
|
||||
in
|
||||
{
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property var facts: [
|
||||
"A photon takes 100,000 to 200,000 years bouncing through the Sun's dense core, then races to Earth in just 8 minutes 20 seconds.",
|
||||
"A teaspoon of neutron star matter would weigh a billion metric tons here on Earth.",
|
||||
"Right now, 100 trillion solar neutrinos are passing through your body every second.",
|
||||
"The Sun converts 4 million metric tons of matter into pure energy every second—enough to power Earth for 500,000 years.",
|
||||
"The universe still glows with leftover heat from the Big Bang—just 2.7 degrees above absolute zero.",
|
||||
"There's a nebula out there that's actually colder than empty space itself.",
|
||||
"We've detected black holes crashing together by measuring spacetime stretch by less than 1/10,000th the width of a proton.",
|
||||
"Fast radio bursts can release more energy in 5 milliseconds than our Sun produces in 3 days.",
|
||||
"Our galaxy might be crawling with billions of rogue planets drifting alone in the dark.",
|
||||
"Distant galaxies can move away from us faster than light because space itself is stretching.",
|
||||
"The edge of what we can see is 46.5 billion light-years away, even though the universe is only 13.8 billion years old.",
|
||||
"The universe is mostly invisible: 5% regular matter, 27% dark matter, 68% dark energy.",
|
||||
"A day on Venus lasts longer than its entire year around the Sun.",
|
||||
"On Mercury, the time between sunrises is 176 Earth days long.",
|
||||
"In about 4.5 billion years, our galaxy will smash into Andromeda.",
|
||||
"Most of the gold in your jewelry was forged when neutron stars collided somewhere in space.",
|
||||
"PSR J1748-2446ad, the fastest spinning star, rotates 716 times per second—its equator moves at 24% the speed of light.",
|
||||
"Cosmic rays create particles that shouldn't make it to Earth's surface, but time dilation lets them sneak through.",
|
||||
"Jupiter's magnetic field is so huge that if we could see it, it would look bigger than the Moon in our sky.",
|
||||
"Interstellar space is so empty it's like a cube 32 kilometers wide containing just a single grain of sand.",
|
||||
"Voyager 1 is 24 billion kilometers away but won't leave the Sun's gravitational influence for another 30,000 years.",
|
||||
"Counting to a billion at one number per second would take over 31 years.",
|
||||
"Space is so vast, even speeding at light-speed, you'd never return past the cosmic horizon.",
|
||||
"Astronauts on the ISS age about 0.01 seconds less each year than people on Earth.",
|
||||
"Sagittarius B2, a dust cloud near our galaxy's center, contains ethyl formate—the compound that gives raspberries their flavor and rum its smell.",
|
||||
"Beyond 16 billion light-years, the cosmic event horizon marks where space expands too fast for light to ever reach us again.",
|
||||
"Even at light-speed, you'd never catch up to most galaxies—space expands faster.",
|
||||
"Only around 5% of galaxies are ever reachable—even at light-speed.",
|
||||
"If the Sun vanished, we'd still orbit it for 8 minutes before drifting away.",
|
||||
"If a planet 65 million light-years away looked at Earth now, it'd see dinosaurs.",
|
||||
"Our oldest radio signals will reach the Milky Way's center in 26,000 years.",
|
||||
"Every atom in your body heavier than hydrogen was forged in the nuclear furnace of a dying star.",
|
||||
"The Moon moves 3.8 centimeters farther from Earth every year.",
|
||||
"The universe creates 275 million new stars every single day.",
|
||||
"Jupiter's Great Red Spot is a storm twice the size of Earth that has been raging for at least 350 years.",
|
||||
"If you watched someone fall into a black hole, they'd appear frozen at the event horizon forever—time effectively stops from your perspective.",
|
||||
"The Boötes Supervoid is a cosmic desert 1.8 billion light-years across with 60% fewer galaxies than it should have."
|
||||
]
|
||||
|
||||
function getRandomFact() {
|
||||
return facts[Math.floor(Math.random() * facts.length)]
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,8 @@ const DMS_ACTIONS = [
|
||||
{ id: "spawn dms ipc call hypr openOverview", label: "Hyprland: Open Overview", compositor: "hyprland" },
|
||||
{ id: "spawn dms ipc call hypr closeOverview", label: "Hyprland: Close Overview", compositor: "hyprland" },
|
||||
{ id: "spawn dms ipc call wallpaper next", label: "Wallpaper: Next" },
|
||||
{ id: "spawn dms ipc call wallpaper prev", label: "Wallpaper: Previous" }
|
||||
{ id: "spawn dms ipc call wallpaper prev", label: "Wallpaper: Previous" },
|
||||
{ id: "spawn dms ipc call workspace-rename open", label: "Workspace: Rename" }
|
||||
];
|
||||
|
||||
const NIRI_ACTIONS = {
|
||||
|
||||
@@ -13,7 +13,7 @@ import "settings/SessionStore.js" as Store
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property int sessionConfigVersion: 2
|
||||
readonly property int sessionConfigVersion: 3
|
||||
|
||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||
property bool _parseError: false
|
||||
@@ -82,7 +82,17 @@ Singleton {
|
||||
property bool nightModeUseIPLocation: false
|
||||
property string nightModeLocationProvider: ""
|
||||
|
||||
property bool themeModeAutoEnabled: false
|
||||
property string themeModeAutoMode: "time"
|
||||
property int themeModeStartHour: 18
|
||||
property int themeModeStartMinute: 0
|
||||
property int themeModeEndHour: 6
|
||||
property int themeModeEndMinute: 0
|
||||
property bool themeModeShareGammaSettings: true
|
||||
property string themeModeNextTransition: ""
|
||||
|
||||
property var pinnedApps: []
|
||||
property var barPinnedApps: []
|
||||
property int dockLauncherPosition: 0
|
||||
property var hiddenTrayIds: []
|
||||
property var recentColors: []
|
||||
@@ -108,6 +118,8 @@ Singleton {
|
||||
property var appOverrides: ({})
|
||||
property bool searchAppActions: true
|
||||
|
||||
property string vpnLastConnected: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!isGreeterMode) {
|
||||
loadSettings();
|
||||
@@ -171,7 +183,7 @@ Singleton {
|
||||
} catch (e) {
|
||||
_parseError = true;
|
||||
const msg = e.message;
|
||||
console.error("SessionData: Failed to parse session.json - file will not be overwritten. Error:", msg);
|
||||
console.error("SessionData: Failed to parse session.json - file will not be overwritten.");
|
||||
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse session.json"), msg));
|
||||
}
|
||||
}
|
||||
@@ -185,14 +197,10 @@ Singleton {
|
||||
_isReadOnly = !writable;
|
||||
if (_isReadOnly) {
|
||||
_hasUnsavedChanges = _checkForUnsavedChanges();
|
||||
if (!wasReadOnly)
|
||||
console.info("SessionData: session.json is now read-only");
|
||||
} else {
|
||||
_loadedSessionSnapshot = getCurrentSessionJson();
|
||||
_hasUnsavedChanges = false;
|
||||
if (wasReadOnly)
|
||||
console.info("SessionData: session.json is now writable");
|
||||
if (_pendingMigration)
|
||||
if (wasReadOnly && _pendingMigration)
|
||||
settingsFile.setText(JSON.stringify(_pendingMigration, null, 2));
|
||||
}
|
||||
_pendingMigration = null;
|
||||
@@ -254,7 +262,7 @@ Singleton {
|
||||
} catch (e) {
|
||||
_parseError = true;
|
||||
const msg = e.message;
|
||||
console.error("SessionData: Failed to parse session.json - file will not be overwritten. Error:", msg);
|
||||
console.error("SessionData: Failed to parse session.json - file will not be overwritten.");
|
||||
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse session.json"), msg));
|
||||
}
|
||||
}
|
||||
@@ -272,7 +280,6 @@ Singleton {
|
||||
}
|
||||
|
||||
function migrateFromUndefinedToV1(settings) {
|
||||
console.info("SessionData: Migrating configuration from undefined to version 1");
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
if (settings.acMonitorTimeout !== undefined) {
|
||||
SettingsData.set("acMonitorTimeout", settings.acMonitorTimeout);
|
||||
@@ -447,7 +454,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (!screen) {
|
||||
console.warn("SessionData: Screen not found:", screenName);
|
||||
console.warn("SessionData: Screen not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -544,7 +551,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (!screen) {
|
||||
console.warn("SessionData: Screen not found:", screenName);
|
||||
console.warn("SessionData: Screen not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -582,7 +589,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (!screen) {
|
||||
console.warn("SessionData: Screen not found:", screenName);
|
||||
console.warn("SessionData: Screen not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -620,7 +627,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (!screen) {
|
||||
console.warn("SessionData: Screen not found:", screenName);
|
||||
console.warn("SessionData: Screen not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -658,7 +665,7 @@ Singleton {
|
||||
}
|
||||
|
||||
if (!screen) {
|
||||
console.warn("SessionData: Screen not found:", screenName);
|
||||
console.warn("SessionData: Screen not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -701,7 +708,6 @@ Singleton {
|
||||
}
|
||||
|
||||
function setNightModeAutoEnabled(enabled) {
|
||||
console.log("SessionData: Setting nightModeAutoEnabled to", enabled);
|
||||
nightModeAutoEnabled = enabled;
|
||||
saveSettings();
|
||||
}
|
||||
@@ -737,13 +743,11 @@ Singleton {
|
||||
}
|
||||
|
||||
function setLatitude(lat) {
|
||||
console.log("SessionData: Setting latitude to", lat);
|
||||
latitude = lat;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setLongitude(lng) {
|
||||
console.log("SessionData: Setting longitude to", lng);
|
||||
longitude = lng;
|
||||
saveSettings();
|
||||
}
|
||||
@@ -753,6 +757,41 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeAutoEnabled(enabled) {
|
||||
themeModeAutoEnabled = enabled;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeAutoMode(mode) {
|
||||
themeModeAutoMode = mode;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeStartHour(hour) {
|
||||
themeModeStartHour = hour;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeStartMinute(minute) {
|
||||
themeModeStartMinute = minute;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeEndHour(hour) {
|
||||
themeModeEndHour = hour;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeEndMinute(minute) {
|
||||
themeModeEndMinute = minute;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setThemeModeShareGammaSettings(share) {
|
||||
themeModeShareGammaSettings = share;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setPinnedApps(apps) {
|
||||
pinnedApps = apps;
|
||||
saveSettings();
|
||||
@@ -784,6 +823,32 @@ Singleton {
|
||||
return appId && pinnedApps.indexOf(appId) !== -1;
|
||||
}
|
||||
|
||||
function setBarPinnedApps(apps) {
|
||||
barPinnedApps = apps;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function addBarPinnedApp(appId) {
|
||||
if (!appId)
|
||||
return;
|
||||
var currentPinned = [...barPinnedApps];
|
||||
if (currentPinned.indexOf(appId) === -1) {
|
||||
currentPinned.push(appId);
|
||||
setBarPinnedApps(currentPinned);
|
||||
}
|
||||
}
|
||||
|
||||
function removeBarPinnedApp(appId) {
|
||||
if (!appId)
|
||||
return;
|
||||
var currentPinned = barPinnedApps.filter(id => id !== appId);
|
||||
setBarPinnedApps(currentPinned);
|
||||
}
|
||||
|
||||
function isBarPinnedApp(appId) {
|
||||
return appId && barPinnedApps.indexOf(appId) !== -1;
|
||||
}
|
||||
|
||||
function hideTrayId(trayId) {
|
||||
if (!trayId)
|
||||
return;
|
||||
@@ -976,6 +1041,11 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setVpnLastConnected(uuid) {
|
||||
vpnLastConnected = uuid || "";
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function syncWallpaperForCurrentMode() {
|
||||
if (!perModeWallpaper)
|
||||
return;
|
||||
|
||||
@@ -133,6 +133,7 @@ Singleton {
|
||||
property real dockTransparency: 1
|
||||
property string widgetBackgroundColor: "sch"
|
||||
property string widgetColorMode: "default"
|
||||
property string controlCenterTileColorMode: "primary"
|
||||
property real cornerRadius: 12
|
||||
property int niriLayoutGapsOverride: -1
|
||||
property int niriLayoutRadiusOverride: -1
|
||||
@@ -146,6 +147,7 @@ Singleton {
|
||||
|
||||
property bool use24HourClock: true
|
||||
property bool showSeconds: false
|
||||
property bool padHours12Hour: false
|
||||
property bool useFahrenheit: false
|
||||
property string windSpeedUnit: "kmh"
|
||||
property bool nightModeEnabled: false
|
||||
@@ -241,6 +243,7 @@ Singleton {
|
||||
property bool showWorkspaceApps: false
|
||||
property bool groupWorkspaceApps: true
|
||||
property int maxWorkspaceIcons: 3
|
||||
property int workspaceAppIconSizeOffset: 0
|
||||
property bool workspaceFollowFocus: false
|
||||
property bool showOccupiedWorkspacesOnly: false
|
||||
property bool reverseScrolling: false
|
||||
@@ -273,6 +276,8 @@ Singleton {
|
||||
property string spotlightModalViewMode: "list"
|
||||
property string browserPickerViewMode: "grid"
|
||||
property var browserUsageHistory: ({})
|
||||
property string appPickerViewMode: "grid"
|
||||
property var filePickerUsageHistory: ({})
|
||||
property bool sortAppsAlphabetically: false
|
||||
property int appLauncherGridColumns: 4
|
||||
property bool spotlightCloseNiriOverview: true
|
||||
@@ -289,13 +294,13 @@ Singleton {
|
||||
|
||||
property string _legacyWeatherLocation: "New York, NY"
|
||||
property string _legacyWeatherCoordinates: "40.7128,-74.0060"
|
||||
property string _legacyVpnLastConnected: ""
|
||||
readonly property string weatherLocation: SessionData.weatherLocation
|
||||
readonly property string weatherCoordinates: SessionData.weatherCoordinates
|
||||
property bool useAutoLocation: false
|
||||
property bool weatherEnabled: true
|
||||
|
||||
property string networkPreference: "auto"
|
||||
property string vpnLastConnected: ""
|
||||
|
||||
property string iconTheme: "System Default"
|
||||
property var availableIconThemes: ["System Default"]
|
||||
@@ -438,6 +443,9 @@ Singleton {
|
||||
property int dockLauncherLogoSizeOffset: 0
|
||||
property real dockLauncherLogoBrightness: 0.5
|
||||
property real dockLauncherLogoContrast: 1
|
||||
property int dockMaxVisibleApps: 0
|
||||
property int dockMaxVisibleRunningApps: 0
|
||||
property bool dockShowOverflowBadge: true
|
||||
|
||||
property bool notificationOverlayEnabled: false
|
||||
property int overviewRows: 2
|
||||
@@ -452,6 +460,7 @@ Singleton {
|
||||
property bool lockScreenShowDate: true
|
||||
property bool lockScreenShowProfileImage: true
|
||||
property bool lockScreenShowPasswordField: true
|
||||
property bool lockScreenShowMediaPlayer: true
|
||||
property bool lockScreenPowerOffMonitorsOnLock: false
|
||||
|
||||
property bool enableFprint: false
|
||||
@@ -998,7 +1007,6 @@ Singleton {
|
||||
fi
|
||||
done
|
||||
|
||||
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
||||
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
||||
|
||||
Quickshell.execDetached(["sh", "-lc", configScript]);
|
||||
@@ -1030,8 +1038,7 @@ Singleton {
|
||||
fi
|
||||
}
|
||||
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
||||
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
||||
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
||||
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'`;
|
||||
|
||||
Quickshell.execDetached(["sh", "-lc", script]);
|
||||
}
|
||||
@@ -1074,11 +1081,15 @@ Singleton {
|
||||
_legacyWeatherLocation = obj.weatherLocation;
|
||||
if (obj?.weatherCoordinates !== undefined)
|
||||
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
||||
if (obj?.vpnLastConnected !== undefined && obj.vpnLastConnected !== "") {
|
||||
_legacyVpnLastConnected = obj.vpnLastConnected;
|
||||
SessionData.vpnLastConnected = _legacyVpnLastConnected;
|
||||
SessionData.saveSettings();
|
||||
}
|
||||
|
||||
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
||||
_hasLoaded = true;
|
||||
applyStoredTheme();
|
||||
applyStoredIconTheme();
|
||||
updateCompositorCursor();
|
||||
Processes.detectQtTools();
|
||||
|
||||
@@ -1089,7 +1100,6 @@ Singleton {
|
||||
console.error("SettingsData: Failed to parse settings.json - file will not be overwritten. Error:", msg);
|
||||
Qt.callLater(() => ToastService.showError(I18n.tr("Failed to parse settings.json"), msg));
|
||||
applyStoredTheme();
|
||||
applyStoredIconTheme();
|
||||
} finally {
|
||||
_loading = false;
|
||||
}
|
||||
@@ -1251,11 +1261,11 @@ Singleton {
|
||||
}
|
||||
|
||||
function getEffectiveTimeFormat() {
|
||||
if (use24HourClock) {
|
||||
if (use24HourClock)
|
||||
return showSeconds ? "hh:mm:ss" : "hh:mm";
|
||||
} else {
|
||||
return showSeconds ? "h:mm:ss AP" : "h:mm AP";
|
||||
}
|
||||
if (padHours12Hour)
|
||||
return showSeconds ? "hh:mm:ss AP" : "hh:mm AP";
|
||||
return showSeconds ? "h:mm:ss AP" : "h:mm AP";
|
||||
}
|
||||
|
||||
function getEffectiveClockDateFormat() {
|
||||
@@ -2307,11 +2317,15 @@ Singleton {
|
||||
_legacyWeatherLocation = obj.weatherLocation;
|
||||
if (obj.weatherCoordinates !== undefined)
|
||||
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
||||
if (obj.vpnLastConnected !== undefined && obj.vpnLastConnected !== "") {
|
||||
_legacyVpnLastConnected = obj.vpnLastConnected;
|
||||
SessionData.vpnLastConnected = _legacyVpnLastConnected;
|
||||
SessionData.saveSettings();
|
||||
}
|
||||
|
||||
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
||||
_hasLoaded = true;
|
||||
applyStoredTheme();
|
||||
applyStoredIconTheme();
|
||||
updateCompositorCursor();
|
||||
} catch (e) {
|
||||
_parseError = true;
|
||||
|
||||
@@ -94,6 +94,9 @@ Singleton {
|
||||
property var matugenColors: ({})
|
||||
property var _pendingGenerateParams: null
|
||||
|
||||
property bool themeModeAutomationActive: false
|
||||
property bool dmsServiceWasDisconnected: true
|
||||
|
||||
readonly property var dank16: {
|
||||
const raw = matugenColors?.dank16;
|
||||
if (!raw)
|
||||
@@ -176,6 +179,227 @@ Singleton {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
|
||||
switchTheme(SettingsData.currentThemeName, false, false);
|
||||
}
|
||||
|
||||
if (typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled) {
|
||||
startThemeModeAutomation();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
enabled: typeof SessionData !== "undefined"
|
||||
|
||||
function onThemeModeAutoEnabledChanged() {
|
||||
if (SessionData.themeModeAutoEnabled) {
|
||||
root.startThemeModeAutomation();
|
||||
} else {
|
||||
root.stopThemeModeAutomation();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeAutoModeChanged() {
|
||||
if (root.themeModeAutomationActive) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeStartHourChanged() {
|
||||
if (root.themeModeAutomationActive && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeStartMinuteChanged() {
|
||||
if (root.themeModeAutomationActive && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeEndHourChanged() {
|
||||
if (root.themeModeAutomationActive && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeEndMinuteChanged() {
|
||||
if (root.themeModeAutomationActive && !SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeModeShareGammaSettingsChanged() {
|
||||
if (root.themeModeAutomationActive) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeStartHourChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeStartMinuteChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeEndHourChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeEndMinuteChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeShareGammaSettings) {
|
||||
root.evaluateThemeMode();
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onLatitudeChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeAutoMode === "location") {
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
root.evaluateThemeMode();
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onLongitudeChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeAutoMode === "location") {
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0 && typeof DMSService !== "undefined") {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
root.evaluateThemeMode();
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
function onNightModeUseIPLocationChanged() {
|
||||
if (root.themeModeAutomationActive && SessionData.themeModeAutoMode === "location") {
|
||||
if (typeof DMSService !== "undefined") {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": SessionData.nightModeUseIPLocation
|
||||
}, response => {
|
||||
if (!response.error && !SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
root.evaluateThemeMode();
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// React to gamma backend's isDay state changes for location-based mode
|
||||
Connections {
|
||||
target: DisplayService
|
||||
enabled: typeof DisplayService !== "undefined" && typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location" && !themeAutoBackendAvailable()
|
||||
|
||||
function onGammaIsDayChanged() {
|
||||
if (root.isLightMode !== DisplayService.gammaIsDay) {
|
||||
root.setLightMode(DisplayService.gammaIsDay, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
enabled: typeof DMSService !== "undefined" && typeof SessionData !== "undefined"
|
||||
|
||||
function onLoginctlEvent(event) {
|
||||
if (!SessionData.themeModeAutoEnabled)
|
||||
return;
|
||||
if (event.event === "unlock" || event.event === "resume") {
|
||||
if (!themeAutoBackendAvailable()) {
|
||||
root.evaluateThemeMode();
|
||||
return;
|
||||
}
|
||||
DMSService.sendRequest("theme.auto.trigger", {});
|
||||
}
|
||||
}
|
||||
|
||||
function onThemeAutoStateUpdate(data) {
|
||||
if (!SessionData.themeModeAutoEnabled) {
|
||||
return;
|
||||
}
|
||||
applyThemeAutoState(data);
|
||||
}
|
||||
|
||||
function onConnectionStateChanged() {
|
||||
if (DMSService.isConnected && SessionData.themeModeAutoMode === "time") {
|
||||
root.syncTimeThemeSchedule();
|
||||
}
|
||||
|
||||
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
|
||||
root.syncLocationThemeSchedule();
|
||||
}
|
||||
|
||||
if (themeAutoBackendAvailable() && SessionData.themeModeAutoEnabled) {
|
||||
DMSService.sendRequest("theme.auto.getState", null, response => {
|
||||
if (response && response.result) {
|
||||
applyThemeAutoState(response.result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!SessionData.themeModeAutoEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMSService.isConnected && SessionData.themeModeAutoMode === "location") {
|
||||
if (SessionData.nightModeUseIPLocation) {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": true
|
||||
}, response => {
|
||||
if (!response.error) {
|
||||
console.info("Theme automation: IP location enabled after connection");
|
||||
}
|
||||
});
|
||||
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": false
|
||||
}, response => {
|
||||
if (!response.error) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
}, locationResponse => {
|
||||
if (locationResponse?.error) {
|
||||
console.warn("Theme automation: Failed to set location", locationResponse.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn("Theme automation: No location configured");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyGreeterTheme(themeName) {
|
||||
@@ -330,6 +554,58 @@ Singleton {
|
||||
property color errorHover: Qt.rgba(error.r, error.g, error.b, 0.12)
|
||||
property color errorPressed: Qt.rgba(error.r, error.g, error.b, 0.16)
|
||||
|
||||
readonly property color ccTileActiveBg: {
|
||||
switch (SettingsData.controlCenterTileColorMode) {
|
||||
case "primaryContainer":
|
||||
return primaryContainer;
|
||||
case "secondary":
|
||||
return secondary;
|
||||
case "surfaceVariant":
|
||||
return surfaceVariant;
|
||||
default:
|
||||
return primary;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property color ccTileActiveText: {
|
||||
switch (SettingsData.controlCenterTileColorMode) {
|
||||
case "primaryContainer":
|
||||
return primary;
|
||||
case "secondary":
|
||||
return surfaceText;
|
||||
case "surfaceVariant":
|
||||
return surfaceText;
|
||||
default:
|
||||
return primaryText;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property color ccTileInactiveIcon: {
|
||||
switch (SettingsData.controlCenterTileColorMode) {
|
||||
case "primaryContainer":
|
||||
return primary;
|
||||
case "secondary":
|
||||
return secondary;
|
||||
case "surfaceVariant":
|
||||
return surfaceText;
|
||||
default:
|
||||
return primary;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property color ccTileRing: {
|
||||
switch (SettingsData.controlCenterTileColorMode) {
|
||||
case "primaryContainer":
|
||||
return Qt.rgba(primary.r, primary.g, primary.b, 0.22);
|
||||
case "secondary":
|
||||
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.22);
|
||||
case "surfaceVariant":
|
||||
return Qt.rgba(surfaceText.r, surfaceText.g, surfaceText.b, 0.22);
|
||||
default:
|
||||
return Qt.rgba(primaryText.r, primaryText.g, primaryText.b, 0.22);
|
||||
}
|
||||
}
|
||||
|
||||
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
||||
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
|
||||
|
||||
@@ -491,7 +767,9 @@ Singleton {
|
||||
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0
|
||||
|
||||
function screenTransition() {
|
||||
CompositorService.isNiri && NiriService.doScreenTransition();
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.doScreenTransition();
|
||||
}
|
||||
}
|
||||
|
||||
function switchTheme(themeName, savePrefs = true, enableTransition = true) {
|
||||
@@ -543,8 +821,10 @@ Singleton {
|
||||
}
|
||||
|
||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
|
||||
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
||||
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode) {
|
||||
SessionData.setLightMode(light);
|
||||
}
|
||||
|
||||
if (!isGreeterMode) {
|
||||
// Skip with matugen because, our script runner will do it.
|
||||
if (!matugenAvailable) {
|
||||
@@ -948,7 +1228,7 @@ Singleton {
|
||||
skipTemplates.push("kcolorscheme");
|
||||
if (!SettingsData.matugenTemplateVscode)
|
||||
skipTemplates.push("vscode");
|
||||
if (!SettingsData.matugenTemplateEmacs)
|
||||
if (!SettingsData.matugenTemplateEmacs)
|
||||
skipTemplates.push("emacs");
|
||||
}
|
||||
if (skipTemplates.length > 0) {
|
||||
@@ -1233,7 +1513,7 @@ Singleton {
|
||||
return `#${invR}${invG}${invB}`;
|
||||
}
|
||||
|
||||
property string baseLogoColor: {
|
||||
property var baseLogoColor: {
|
||||
if (typeof SettingsData === "undefined")
|
||||
return "";
|
||||
const colorOverride = SettingsData.launcherLogoColorOverride;
|
||||
@@ -1246,7 +1526,7 @@ Singleton {
|
||||
return colorOverride;
|
||||
}
|
||||
|
||||
property string effectiveLogoColor: {
|
||||
property var effectiveLogoColor: {
|
||||
if (typeof SettingsData === "undefined")
|
||||
return "";
|
||||
|
||||
@@ -1453,4 +1733,303 @@ Singleton {
|
||||
root.switchTheme(defaultTheme, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Theme mode automation functions
|
||||
function themeAutoBackendAvailable() {
|
||||
return typeof DMSService !== "undefined" && DMSService.isConnected && Array.isArray(DMSService.capabilities) && DMSService.capabilities.includes("theme.auto");
|
||||
}
|
||||
|
||||
function applyThemeAutoState(state) {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
if (state.config && state.config.mode && state.config.mode !== SessionData.themeModeAutoMode) {
|
||||
return;
|
||||
}
|
||||
if (typeof SessionData !== "undefined" && state.nextTransition !== undefined) {
|
||||
SessionData.themeModeNextTransition = state.nextTransition || "";
|
||||
}
|
||||
if (state.isLight !== undefined && root.isLightMode !== state.isLight) {
|
||||
root.setLightMode(state.isLight, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
function syncTimeThemeSchedule() {
|
||||
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DMSService.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "time";
|
||||
|
||||
if (!timeModeActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
DMSService.sendRequest("theme.auto.setMode", {
|
||||
"mode": "time"
|
||||
});
|
||||
|
||||
const shareSettings = SessionData.themeModeShareGammaSettings;
|
||||
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
|
||||
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
|
||||
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
|
||||
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
|
||||
|
||||
DMSService.sendRequest("theme.auto.setSchedule", {
|
||||
"startHour": startHour,
|
||||
"startMinute": startMinute,
|
||||
"endHour": endHour,
|
||||
"endMinute": endMinute
|
||||
}, response => {
|
||||
if (response && response.error) {
|
||||
console.error("Theme automation: Failed to sync time schedule:", response.error);
|
||||
}
|
||||
});
|
||||
|
||||
DMSService.sendRequest("theme.auto.setEnabled", {
|
||||
"enabled": true
|
||||
});
|
||||
DMSService.sendRequest("theme.auto.trigger", {});
|
||||
}
|
||||
|
||||
function syncLocationThemeSchedule() {
|
||||
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DMSService.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const locationModeActive = SessionData.themeModeAutoEnabled && SessionData.themeModeAutoMode === "location";
|
||||
|
||||
if (!locationModeActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
DMSService.sendRequest("theme.auto.setMode", {
|
||||
"mode": "location"
|
||||
});
|
||||
|
||||
if (SessionData.nightModeUseIPLocation) {
|
||||
DMSService.sendRequest("theme.auto.setUseIPLocation", {
|
||||
"use": true
|
||||
});
|
||||
} else {
|
||||
DMSService.sendRequest("theme.auto.setUseIPLocation", {
|
||||
"use": false
|
||||
});
|
||||
if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("theme.auto.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DMSService.sendRequest("theme.auto.setEnabled", {
|
||||
"enabled": true
|
||||
});
|
||||
DMSService.sendRequest("theme.auto.trigger", {});
|
||||
}
|
||||
|
||||
function evaluateThemeMode() {
|
||||
if (typeof SessionData === "undefined" || !SessionData.themeModeAutoEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeAutoBackendAvailable()) {
|
||||
DMSService.sendRequest("theme.auto.getState", null, response => {
|
||||
if (response && response.result) {
|
||||
applyThemeAutoState(response.result);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = SessionData.themeModeAutoMode;
|
||||
|
||||
if (mode === "location") {
|
||||
evaluateLocationBasedThemeMode();
|
||||
} else {
|
||||
evaluateTimeBasedThemeMode();
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateLocationBasedThemeMode() {
|
||||
if (typeof DisplayService !== "undefined") {
|
||||
const shouldBeLight = DisplayService.gammaIsDay;
|
||||
if (root.isLightMode !== shouldBeLight) {
|
||||
root.setLightMode(shouldBeLight, true, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SessionData.nightModeUseIPLocation && SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
const shouldBeLight = calculateIsDaytime(SessionData.latitude, SessionData.longitude);
|
||||
if (root.isLightMode !== shouldBeLight) {
|
||||
root.setLightMode(shouldBeLight, true, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (root.themeModeAutomationActive) {
|
||||
if (SessionData.nightModeUseIPLocation) {
|
||||
console.warn("Theme automation: Waiting for IP location from backend");
|
||||
} else {
|
||||
console.warn("Theme automation: Location mode requires coordinates");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateTimeBasedThemeMode() {
|
||||
const shareSettings = SessionData.themeModeShareGammaSettings;
|
||||
|
||||
const startHour = shareSettings ? SessionData.nightModeStartHour : SessionData.themeModeStartHour;
|
||||
const startMinute = shareSettings ? SessionData.nightModeStartMinute : SessionData.themeModeStartMinute;
|
||||
const endHour = shareSettings ? SessionData.nightModeEndHour : SessionData.themeModeEndHour;
|
||||
const endMinute = shareSettings ? SessionData.nightModeEndMinute : SessionData.themeModeEndMinute;
|
||||
|
||||
const now = new Date();
|
||||
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
||||
const startMinutes = startHour * 60 + startMinute;
|
||||
const endMinutes = endHour * 60 + endMinute;
|
||||
|
||||
let shouldBeLight;
|
||||
if (startMinutes < endMinutes) {
|
||||
shouldBeLight = currentMinutes < startMinutes || currentMinutes >= endMinutes;
|
||||
} else {
|
||||
shouldBeLight = currentMinutes >= endMinutes && currentMinutes < startMinutes;
|
||||
}
|
||||
|
||||
if (root.isLightMode !== shouldBeLight) {
|
||||
root.setLightMode(shouldBeLight, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateIsDaytime(lat, lng) {
|
||||
const now = new Date();
|
||||
const start = new Date(now.getFullYear(), 0, 0);
|
||||
const diff = now - start;
|
||||
const dayOfYear = Math.floor(diff / 86400000);
|
||||
const latRad = lat * Math.PI / 180;
|
||||
|
||||
const declination = 23.45 * Math.sin((360 / 365) * (dayOfYear - 81) * Math.PI / 180);
|
||||
const declinationRad = declination * Math.PI / 180;
|
||||
|
||||
const cosHourAngle = -Math.tan(latRad) * Math.tan(declinationRad);
|
||||
|
||||
if (cosHourAngle > 1) {
|
||||
return false; // Polar night
|
||||
}
|
||||
if (cosHourAngle < -1) {
|
||||
return true; // Midnight sun
|
||||
}
|
||||
|
||||
const hourAngle = Math.acos(cosHourAngle);
|
||||
const hourAngleDeg = hourAngle * 180 / Math.PI;
|
||||
|
||||
const sunriseHour = 12 - hourAngleDeg / 15;
|
||||
const sunsetHour = 12 + hourAngleDeg / 15;
|
||||
|
||||
const timeZoneOffset = now.getTimezoneOffset() / 60;
|
||||
const localSunrise = sunriseHour - lng / 15 - timeZoneOffset;
|
||||
const localSunset = sunsetHour - lng / 15 - timeZoneOffset;
|
||||
|
||||
const currentHour = now.getHours() + now.getMinutes() / 60;
|
||||
|
||||
const normalizeSunrise = ((localSunrise % 24) + 24) % 24;
|
||||
const normalizeSunset = ((localSunset % 24) + 24) % 24;
|
||||
|
||||
return currentHour >= normalizeSunrise && currentHour < normalizeSunset;
|
||||
}
|
||||
|
||||
// Helper function to send location to backend
|
||||
function sendLocationToBackend() {
|
||||
if (typeof SessionData === "undefined" || typeof DMSService === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DMSService.isConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SessionData.nightModeUseIPLocation) {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": true
|
||||
}, response => {
|
||||
if (response?.error) {
|
||||
console.warn("Theme automation: Failed to enable IP location", response.error);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
|
||||
DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
|
||||
"use": false
|
||||
}, response => {
|
||||
if (!response.error) {
|
||||
DMSService.sendRequest("wayland.gamma.setLocation", {
|
||||
"latitude": SessionData.latitude,
|
||||
"longitude": SessionData.longitude
|
||||
}, locResp => {
|
||||
if (locResp?.error) {
|
||||
console.warn("Theme automation: Failed to set location", locResp.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: locationRetryTimer
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: false
|
||||
property int retryCount: 0
|
||||
|
||||
onTriggered: {
|
||||
if (root.sendLocationToBackend()) {
|
||||
stop();
|
||||
retryCount = 0;
|
||||
root.evaluateThemeMode();
|
||||
} else {
|
||||
retryCount++;
|
||||
if (retryCount >= 10) {
|
||||
stop();
|
||||
retryCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startThemeModeAutomation() {
|
||||
root.themeModeAutomationActive = true;
|
||||
|
||||
root.syncTimeThemeSchedule();
|
||||
root.syncLocationThemeSchedule();
|
||||
|
||||
const sent = root.sendLocationToBackend();
|
||||
|
||||
if (!sent && typeof SessionData !== "undefined" && SessionData.themeModeAutoMode === "location") {
|
||||
locationRetryTimer.start();
|
||||
} else {
|
||||
root.evaluateThemeMode();
|
||||
}
|
||||
}
|
||||
|
||||
function stopThemeModeAutomation() {
|
||||
root.themeModeAutomationActive = false;
|
||||
if (typeof DMSService !== "undefined" && DMSService.isConnected) {
|
||||
DMSService.sendRequest("theme.auto.setEnabled", {
|
||||
"enabled": false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,19 @@ var SPEC = {
|
||||
nightModeUseIPLocation: { def: false },
|
||||
nightModeLocationProvider: { def: "" },
|
||||
|
||||
themeModeAutoEnabled: { def: false },
|
||||
themeModeAutoMode: { def: "time" },
|
||||
themeModeStartHour: { def: 18 },
|
||||
themeModeStartMinute: { def: 0 },
|
||||
themeModeEndHour: { def: 6 },
|
||||
themeModeEndMinute: { def: 0 },
|
||||
themeModeShareGammaSettings: { def: true },
|
||||
|
||||
weatherLocation: { def: "New York, NY" },
|
||||
weatherCoordinates: { def: "40.7128,-74.0060" },
|
||||
|
||||
pinnedApps: { def: [] },
|
||||
barPinnedApps: { def: [] },
|
||||
dockLauncherPosition: { def: 0 },
|
||||
hiddenTrayIds: { def: [] },
|
||||
recentColors: { def: [] },
|
||||
@@ -60,7 +69,9 @@ var SPEC = {
|
||||
|
||||
hiddenApps: { def: [] },
|
||||
appOverrides: { def: {} },
|
||||
searchAppActions: { def: true }
|
||||
searchAppActions: { def: true },
|
||||
|
||||
vpnLastConnected: { def: "" }
|
||||
};
|
||||
|
||||
function getValidKeys() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.pragma library
|
||||
|
||||
.import "./SessionSpec.js" as SpecModule
|
||||
.import "./SessionSpec.js" as SpecModule
|
||||
|
||||
function parse(root, jsonObj) {
|
||||
var SPEC = SpecModule.SPEC;
|
||||
@@ -68,6 +68,11 @@ function migrateToVersion(obj, targetVersion, settingsData) {
|
||||
session.configVersion = 2;
|
||||
}
|
||||
|
||||
if (currentVersion < 3) {
|
||||
console.info("SessionData: Migrating session to version 3");
|
||||
session.configVersion = 3;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ var SPEC = {
|
||||
|
||||
widgetBackgroundColor: { def: "sch" },
|
||||
widgetColorMode: { def: "default" },
|
||||
controlCenterTileColorMode: { def: "primary" },
|
||||
cornerRadius: { def: 12, onChange: "updateCompositorLayout" },
|
||||
niriLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
|
||||
niriLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
|
||||
@@ -32,6 +33,7 @@ var SPEC = {
|
||||
|
||||
use24HourClock: { def: true },
|
||||
showSeconds: { def: false },
|
||||
padHours12Hour: { def: false },
|
||||
useFahrenheit: { def: false },
|
||||
windSpeedUnit: { def: "kmh" },
|
||||
nightModeEnabled: { def: false },
|
||||
@@ -78,16 +80,18 @@ var SPEC = {
|
||||
privacyShowCameraIcon: { def: false },
|
||||
privacyShowScreenShareIcon: { def: false },
|
||||
|
||||
controlCenterWidgets: { def: [
|
||||
{ id: "volumeSlider", enabled: true, width: 50 },
|
||||
{ id: "brightnessSlider", enabled: true, width: 50 },
|
||||
{ id: "wifi", enabled: true, width: 50 },
|
||||
{ id: "bluetooth", enabled: true, width: 50 },
|
||||
{ id: "audioOutput", enabled: true, width: 50 },
|
||||
{ id: "audioInput", enabled: true, width: 50 },
|
||||
{ id: "nightMode", enabled: true, width: 50 },
|
||||
{ id: "darkMode", enabled: true, width: 50 }
|
||||
]},
|
||||
controlCenterWidgets: {
|
||||
def: [
|
||||
{ id: "volumeSlider", enabled: true, width: 50 },
|
||||
{ id: "brightnessSlider", enabled: true, width: 50 },
|
||||
{ id: "wifi", enabled: true, width: 50 },
|
||||
{ id: "bluetooth", enabled: true, width: 50 },
|
||||
{ id: "audioOutput", enabled: true, width: 50 },
|
||||
{ id: "audioInput", enabled: true, width: 50 },
|
||||
{ id: "nightMode", enabled: true, width: 50 },
|
||||
{ id: "darkMode", enabled: true, width: 50 }
|
||||
]
|
||||
},
|
||||
|
||||
showWorkspaceIndex: { def: false },
|
||||
showWorkspaceName: { def: false },
|
||||
@@ -95,6 +99,7 @@ var SPEC = {
|
||||
workspaceScrolling: { def: false },
|
||||
showWorkspaceApps: { def: false },
|
||||
maxWorkspaceIcons: { def: 3 },
|
||||
workspaceAppIconSizeOffset: { def: 0 },
|
||||
groupWorkspaceApps: { def: true },
|
||||
workspaceFollowFocus: { def: false },
|
||||
showOccupiedWorkspacesOnly: { def: false },
|
||||
@@ -118,13 +123,15 @@ var SPEC = {
|
||||
keyboardLayoutNameCompactMode: { def: false },
|
||||
runningAppsCurrentWorkspace: { def: false },
|
||||
runningAppsGroupByApp: { def: false },
|
||||
appIdSubstitutions: { def: [
|
||||
{ pattern: "Spotify", replacement: "spotify", type: "exact" },
|
||||
{ pattern: "beepertexts", replacement: "beeper", type: "exact" },
|
||||
{ pattern: "home assistant desktop", replacement: "homeassistant-desktop", type: "exact" },
|
||||
{ pattern: "com.transmissionbt.transmission", replacement: "transmission-gtk", type: "contains" },
|
||||
{ pattern: "^steam_app_(\\d+)$", replacement: "steam_icon_$1", type: "regex" }
|
||||
]},
|
||||
appIdSubstitutions: {
|
||||
def: [
|
||||
{ pattern: "Spotify", replacement: "spotify", type: "exact" },
|
||||
{ pattern: "beepertexts", replacement: "beeper", type: "exact" },
|
||||
{ pattern: "home assistant desktop", replacement: "homeassistant-desktop", type: "exact" },
|
||||
{ pattern: "com.transmissionbt.transmission", replacement: "transmission-gtk", type: "contains" },
|
||||
{ pattern: "^steam_app_(\\d+)$", replacement: "steam_icon_$1", type: "regex" }
|
||||
]
|
||||
},
|
||||
centeringMode: { def: "index" },
|
||||
clockDateFormat: { def: "" },
|
||||
lockDateFormat: { def: "" },
|
||||
@@ -132,6 +139,10 @@ var SPEC = {
|
||||
|
||||
appLauncherViewMode: { def: "list" },
|
||||
spotlightModalViewMode: { def: "list" },
|
||||
browserPickerViewMode: { def: "grid" },
|
||||
browserUsageHistory: { def: {} },
|
||||
appPickerViewMode: { def: "grid" },
|
||||
filePickerUsageHistory: { def: {} },
|
||||
sortAppsAlphabetically: { def: false },
|
||||
appLauncherGridColumns: { def: 4 },
|
||||
spotlightCloseNiriOverview: { def: true },
|
||||
@@ -148,7 +159,6 @@ var SPEC = {
|
||||
weatherEnabled: { def: true },
|
||||
|
||||
networkPreference: { def: "auto" },
|
||||
vpnLastConnected: { def: "" },
|
||||
|
||||
iconTheme: { def: "System Default", onChange: "applyStoredIconTheme" },
|
||||
availableIconThemes: { def: ["System Default"], persist: false },
|
||||
@@ -262,6 +272,9 @@ var SPEC = {
|
||||
dockLauncherLogoSizeOffset: { def: 0 },
|
||||
dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
|
||||
dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
|
||||
dockMaxVisibleApps: { def: 0 },
|
||||
dockMaxVisibleRunningApps: { def: 0 },
|
||||
dockShowOverflowBadge: { def: true },
|
||||
|
||||
notificationOverlayEnabled: { def: false },
|
||||
overviewRows: { def: 2, persist: false },
|
||||
@@ -276,6 +289,7 @@ var SPEC = {
|
||||
lockScreenShowDate: { def: true },
|
||||
lockScreenShowProfileImage: { def: true },
|
||||
lockScreenShowPasswordField: { def: true },
|
||||
lockScreenShowMediaPlayer: { def: true },
|
||||
lockScreenPowerOffMonitorsOnLock: { def: false },
|
||||
enableFprint: { def: false },
|
||||
maxFprintTries: { def: 15 },
|
||||
@@ -300,7 +314,7 @@ var SPEC = {
|
||||
osdAlwaysShowValue: { def: false },
|
||||
osdPosition: { def: 5 },
|
||||
osdVolumeEnabled: { def: true },
|
||||
osdMediaVolumeEnabled : { def: true },
|
||||
osdMediaVolumeEnabled: { def: true },
|
||||
osdBrightnessEnabled: { def: true },
|
||||
osdIdleInhibitorEnabled: { def: true },
|
||||
osdMicMuteEnabled: { def: true },
|
||||
@@ -331,52 +345,54 @@ var SPEC = {
|
||||
niriOutputSettings: { def: {} },
|
||||
hyprlandOutputSettings: { def: {} },
|
||||
|
||||
barConfigs: { def: [{
|
||||
id: "default",
|
||||
name: "Main Bar",
|
||||
enabled: true,
|
||||
position: 0,
|
||||
screenPreferences: ["all"],
|
||||
showOnLastDisplay: true,
|
||||
leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||
centerWidgets: ["music", "clock", "weather"],
|
||||
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||
spacing: 4,
|
||||
innerPadding: 4,
|
||||
bottomGap: 0,
|
||||
transparency: 1.0,
|
||||
widgetTransparency: 1.0,
|
||||
squareCorners: false,
|
||||
noBackground: false,
|
||||
gothCornersEnabled: false,
|
||||
gothCornerRadiusOverride: false,
|
||||
gothCornerRadiusValue: 12,
|
||||
borderEnabled: false,
|
||||
borderColor: "surfaceText",
|
||||
borderOpacity: 1.0,
|
||||
borderThickness: 1,
|
||||
widgetOutlineEnabled: false,
|
||||
widgetOutlineColor: "primary",
|
||||
widgetOutlineOpacity: 1.0,
|
||||
widgetOutlineThickness: 1,
|
||||
fontScale: 1.0,
|
||||
autoHide: false,
|
||||
autoHideDelay: 250,
|
||||
showOnWindowsOpen: false,
|
||||
openOnOverview: false,
|
||||
visible: true,
|
||||
popupGapsAuto: true,
|
||||
popupGapsManual: 4,
|
||||
maximizeDetection: true,
|
||||
scrollEnabled: true,
|
||||
scrollXBehavior: "column",
|
||||
scrollYBehavior: "workspace",
|
||||
shadowIntensity: 0,
|
||||
shadowOpacity: 60,
|
||||
shadowColorMode: "text",
|
||||
shadowCustomColor: "#000000",
|
||||
clickThrough: false
|
||||
}], onChange: "updateBarConfigs" },
|
||||
barConfigs: {
|
||||
def: [{
|
||||
id: "default",
|
||||
name: "Main Bar",
|
||||
enabled: true,
|
||||
position: 0,
|
||||
screenPreferences: ["all"],
|
||||
showOnLastDisplay: true,
|
||||
leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||
centerWidgets: ["music", "clock", "weather"],
|
||||
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||
spacing: 4,
|
||||
innerPadding: 4,
|
||||
bottomGap: 0,
|
||||
transparency: 1.0,
|
||||
widgetTransparency: 1.0,
|
||||
squareCorners: false,
|
||||
noBackground: false,
|
||||
gothCornersEnabled: false,
|
||||
gothCornerRadiusOverride: false,
|
||||
gothCornerRadiusValue: 12,
|
||||
borderEnabled: false,
|
||||
borderColor: "surfaceText",
|
||||
borderOpacity: 1.0,
|
||||
borderThickness: 1,
|
||||
widgetOutlineEnabled: false,
|
||||
widgetOutlineColor: "primary",
|
||||
widgetOutlineOpacity: 1.0,
|
||||
widgetOutlineThickness: 1,
|
||||
fontScale: 1.0,
|
||||
autoHide: false,
|
||||
autoHideDelay: 250,
|
||||
showOnWindowsOpen: false,
|
||||
openOnOverview: false,
|
||||
visible: true,
|
||||
popupGapsAuto: true,
|
||||
popupGapsManual: 4,
|
||||
maximizeDetection: true,
|
||||
scrollEnabled: true,
|
||||
scrollXBehavior: "column",
|
||||
scrollYBehavior: "workspace",
|
||||
shadowIntensity: 0,
|
||||
shadowOpacity: 60,
|
||||
shadowColorMode: "text",
|
||||
shadowCustomColor: "#000000",
|
||||
clickThrough: false
|
||||
}], onChange: "updateBarConfigs"
|
||||
},
|
||||
|
||||
desktopClockEnabled: { def: false },
|
||||
desktopClockStyle: { def: "analog" },
|
||||
@@ -431,7 +447,7 @@ var SPEC = {
|
||||
};
|
||||
|
||||
function getValidKeys() {
|
||||
return Object.keys(SPEC).filter(function(k) { return SPEC[k].persist !== false; }).concat(["configVersion"]);
|
||||
return Object.keys(SPEC).filter(function (k) { return SPEC[k].persist !== false; }).concat(["configVersion"]);
|
||||
}
|
||||
|
||||
function set(root, key, value, saveFn, hooks) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.pragma library
|
||||
|
||||
.import "./SettingsSpec.js" as SpecModule
|
||||
.import "./SettingsSpec.js" as SpecModule
|
||||
|
||||
function parse(root, jsonObj) {
|
||||
var SPEC = SpecModule.SPEC;
|
||||
|
||||
@@ -550,6 +550,11 @@ Item {
|
||||
AppPickerModal {
|
||||
id: filePickerModal
|
||||
title: I18n.tr("Open with...")
|
||||
viewMode: SettingsData.appPickerViewMode || "grid"
|
||||
|
||||
onViewModeChanged: {
|
||||
SettingsData.set("appPickerViewMode", viewMode)
|
||||
}
|
||||
|
||||
function shellEscape(str) {
|
||||
return "'" + str.replace(/'/g, "'\\''") + "'";
|
||||
@@ -644,6 +649,18 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: workspaceRenameModalLoader
|
||||
|
||||
active: false
|
||||
|
||||
Component.onCompleted: PopoutService.workspaceRenameModalLoader = workspaceRenameModalLoader
|
||||
|
||||
WorkspaceRenameModal {
|
||||
id: workspaceRenameModal
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: processListModalLoader
|
||||
|
||||
@@ -769,6 +786,7 @@ Item {
|
||||
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
||||
dankBarRepeater: dankBarRepeater
|
||||
hyprlandOverviewLoader: hyprlandOverviewLoader
|
||||
workspaceRenameModalLoader: workspaceRenameModalLoader
|
||||
}
|
||||
|
||||
Variants {
|
||||
|
||||
@@ -15,6 +15,7 @@ Item {
|
||||
required property var hyprKeybindsModalLoader
|
||||
required property var dankBarRepeater
|
||||
required property var hyprlandOverviewLoader
|
||||
required property var workspaceRenameModalLoader
|
||||
|
||||
function getFirstBar() {
|
||||
if (!root.dankBarRepeater || root.dankBarRepeater.count === 0)
|
||||
@@ -1062,7 +1063,7 @@ Item {
|
||||
}
|
||||
|
||||
function toggleQuery(query: string): string {
|
||||
PopoutService.toggleDankLauncherV2();
|
||||
PopoutService.toggleDankLauncherV2WithQuery(query);
|
||||
return "LAUNCHER_TOGGLE_QUERY_SUCCESS";
|
||||
}
|
||||
|
||||
@@ -1106,13 +1107,86 @@ Item {
|
||||
}
|
||||
|
||||
function toggleQuery(query: string): string {
|
||||
PopoutService.toggleDankLauncherV2();
|
||||
PopoutService.toggleDankLauncherV2WithQuery(query);
|
||||
return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS";
|
||||
}
|
||||
|
||||
target: "spotlight"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function info(message: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showInfo(message);
|
||||
return "TOAST_INFO_SUCCESS";
|
||||
}
|
||||
|
||||
function infoWith(message: string, details: string, command: string, category: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showInfo(message, details, command, category);
|
||||
return "TOAST_INFO_SUCCESS";
|
||||
}
|
||||
|
||||
function warn(message: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showWarning(message);
|
||||
return "TOAST_WARN_SUCCESS";
|
||||
}
|
||||
|
||||
function warnWith(message: string, details: string, command: string, category: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showWarning(message, details, command, category);
|
||||
return "TOAST_WARN_SUCCESS";
|
||||
}
|
||||
|
||||
function error(message: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showError(message);
|
||||
return "TOAST_ERROR_SUCCESS";
|
||||
}
|
||||
|
||||
function errorWith(message: string, details: string, command: string, category: string): string {
|
||||
if (!message)
|
||||
return "ERROR: No message specified";
|
||||
|
||||
ToastService.showError(message, details, command, category);
|
||||
return "TOAST_ERROR_SUCCESS";
|
||||
}
|
||||
|
||||
function hide(): string {
|
||||
ToastService.hideToast();
|
||||
return "TOAST_HIDE_SUCCESS";
|
||||
}
|
||||
|
||||
function dismiss(category: string): string {
|
||||
if (!category)
|
||||
return "ERROR: No category specified";
|
||||
|
||||
ToastService.dismissCategory(category);
|
||||
return "TOAST_DISMISS_SUCCESS";
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
if (!ToastService.toastVisible)
|
||||
return "hidden";
|
||||
|
||||
const levels = ["info", "warn", "error"];
|
||||
return `visible:${levels[ToastService.currentLevel]}:${ToastService.currentMessage}`;
|
||||
}
|
||||
|
||||
target: "toast"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function open(): string {
|
||||
FirstLaunchService.showWelcome();
|
||||
@@ -1292,4 +1366,40 @@ Item {
|
||||
|
||||
target: "desktopWidget"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function open(): string {
|
||||
root.workspaceRenameModalLoader.active = true;
|
||||
if (root.workspaceRenameModalLoader.item) {
|
||||
const ws = NiriService.workspaces[NiriService.focusedWorkspaceId];
|
||||
root.workspaceRenameModalLoader.item.show(ws?.name || "");
|
||||
return "WORKSPACE_RENAME_MODAL_OPENED";
|
||||
}
|
||||
return "WORKSPACE_RENAME_MODAL_NOT_FOUND";
|
||||
}
|
||||
|
||||
function close(): string {
|
||||
if (root.workspaceRenameModalLoader.item) {
|
||||
root.workspaceRenameModalLoader.item.hide();
|
||||
return "WORKSPACE_RENAME_MODAL_CLOSED";
|
||||
}
|
||||
return "WORKSPACE_RENAME_MODAL_NOT_FOUND";
|
||||
}
|
||||
|
||||
function toggle(): string {
|
||||
root.workspaceRenameModalLoader.active = true;
|
||||
if (root.workspaceRenameModalLoader.item) {
|
||||
if (root.workspaceRenameModalLoader.item.visible) {
|
||||
root.workspaceRenameModalLoader.item.hide();
|
||||
return "WORKSPACE_RENAME_MODAL_CLOSED";
|
||||
}
|
||||
const ws = NiriService.workspaces[NiriService.focusedWorkspaceId];
|
||||
root.workspaceRenameModalLoader.item.show(ws?.name || "");
|
||||
return "WORKSPACE_RENAME_MODAL_OPENED";
|
||||
}
|
||||
return "WORKSPACE_RENAME_MODAL_NOT_FOUND";
|
||||
}
|
||||
|
||||
target: "workspace-rename"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,16 +29,7 @@ DankModal {
|
||||
property int activeImageLoads: 0
|
||||
readonly property int maxConcurrentLoads: 3
|
||||
readonly property bool clipboardAvailable: DMSService.isConnected && (DMSService.capabilities.length === 0 || DMSService.capabilities.includes("clipboard"))
|
||||
property bool wtypeAvailable: false
|
||||
|
||||
Process {
|
||||
id: wtypeCheck
|
||||
command: ["which", "wtype"]
|
||||
running: true
|
||||
onExited: exitCode => {
|
||||
clipboardHistoryModal.wtypeAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
readonly property bool wtypeAvailable: SessionService.wtypeAvailable
|
||||
|
||||
Process {
|
||||
id: wtypeProcess
|
||||
|
||||
@@ -33,7 +33,8 @@ Rectangle {
|
||||
result.push(selectedItem.primaryAction);
|
||||
}
|
||||
|
||||
if (selectedItem?.type === "plugin") {
|
||||
switch (selectedItem?.type) {
|
||||
case "plugin":
|
||||
var pluginActions = getPluginContextMenuActions();
|
||||
for (var i = 0; i < pluginActions.length; i++) {
|
||||
var act = pluginActions[i];
|
||||
@@ -44,24 +45,45 @@ Rectangle {
|
||||
pluginAction: act.action
|
||||
});
|
||||
}
|
||||
} else if (selectedItem?.type === "app" && !selectedItem?.isCore) {
|
||||
break;
|
||||
case "plugin_browse":
|
||||
if (selectedItem?.actions) {
|
||||
for (var i = 0; i < selectedItem.actions.length; i++) {
|
||||
result.push(selectedItem.actions[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "app":
|
||||
if (selectedItem?.isCore)
|
||||
break;
|
||||
if (selectedItem?.actions) {
|
||||
for (var i = 0; i < selectedItem.actions.length; i++) {
|
||||
result.push(selectedItem.actions[i]);
|
||||
}
|
||||
}
|
||||
if (SessionService.nvidiaCommand) {
|
||||
result.push({
|
||||
name: I18n.tr("Launch on dGPU"),
|
||||
icon: "memory",
|
||||
action: "launch_dgpu"
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
readonly property bool hasActions: {
|
||||
if (selectedItem?.type === "app" && !selectedItem?.isCore)
|
||||
return true;
|
||||
if (selectedItem?.type === "plugin") {
|
||||
var pluginActions = getPluginContextMenuActions();
|
||||
return pluginActions.length > 0;
|
||||
switch (selectedItem?.type) {
|
||||
case "app":
|
||||
return !selectedItem?.isCore;
|
||||
case "plugin":
|
||||
return getPluginContextMenuActions().length > 0;
|
||||
case "plugin_browse":
|
||||
return selectedItem?.actions?.length > 0;
|
||||
default:
|
||||
return actions.length > 1;
|
||||
}
|
||||
return actions.length > 1;
|
||||
}
|
||||
|
||||
width: parent?.width ?? 200
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
157
quickshell/Modals/DankLauncherV2/ControllerUtils.js
Normal file
157
quickshell/Modals/DankLauncherV2/ControllerUtils.js
Normal file
@@ -0,0 +1,157 @@
|
||||
.pragma library
|
||||
|
||||
function getFileIcon(filename) {
|
||||
var ext = filename.lastIndexOf(".") > 0 ? filename.substring(filename.lastIndexOf(".") + 1).toLowerCase() : "";
|
||||
|
||||
switch (ext) {
|
||||
case "pdf":
|
||||
return "picture_as_pdf";
|
||||
case "doc":
|
||||
case "docx":
|
||||
case "odt":
|
||||
return "description";
|
||||
case "xls":
|
||||
case "xlsx":
|
||||
case "ods":
|
||||
return "table_chart";
|
||||
case "ppt":
|
||||
case "pptx":
|
||||
case "odp":
|
||||
return "slideshow";
|
||||
case "txt":
|
||||
case "md":
|
||||
case "rst":
|
||||
return "article";
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "gif":
|
||||
case "svg":
|
||||
case "webp":
|
||||
return "image";
|
||||
case "mp3":
|
||||
case "wav":
|
||||
case "flac":
|
||||
case "ogg":
|
||||
return "audio_file";
|
||||
case "mp4":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "webm":
|
||||
return "video_file";
|
||||
case "zip":
|
||||
case "tar":
|
||||
case "gz":
|
||||
case "7z":
|
||||
case "rar":
|
||||
return "folder_zip";
|
||||
case "js":
|
||||
case "ts":
|
||||
case "py":
|
||||
case "rs":
|
||||
case "go":
|
||||
case "java":
|
||||
case "c":
|
||||
case "cpp":
|
||||
case "h":
|
||||
return "code";
|
||||
case "html":
|
||||
case "css":
|
||||
case "htm":
|
||||
return "web";
|
||||
case "json":
|
||||
case "xml":
|
||||
case "yaml":
|
||||
case "yml":
|
||||
return "data_object";
|
||||
case "sh":
|
||||
case "bash":
|
||||
case "zsh":
|
||||
return "terminal";
|
||||
default:
|
||||
return "insert_drive_file";
|
||||
}
|
||||
}
|
||||
|
||||
function stripIconPrefix(iconName) {
|
||||
if (!iconName)
|
||||
return "extension";
|
||||
if (iconName.startsWith("unicode:"))
|
||||
return iconName.substring(8);
|
||||
if (iconName.startsWith("material:"))
|
||||
return iconName.substring(9);
|
||||
if (iconName.startsWith("image:"))
|
||||
return iconName.substring(6);
|
||||
return iconName;
|
||||
}
|
||||
|
||||
function detectIconType(iconName) {
|
||||
if (!iconName)
|
||||
return "material";
|
||||
if (iconName.startsWith("unicode:"))
|
||||
return "unicode";
|
||||
if (iconName.startsWith("material:"))
|
||||
return "material";
|
||||
if (iconName.startsWith("image:"))
|
||||
return "image";
|
||||
if (iconName.indexOf("/") >= 0 || iconName.indexOf(".") >= 0)
|
||||
return "image";
|
||||
if (/^[a-z]+-[a-z]/.test(iconName.toLowerCase()))
|
||||
return "image";
|
||||
return "material";
|
||||
}
|
||||
|
||||
function evaluateCalculator(query) {
|
||||
if (!query || query.length === 0)
|
||||
return null;
|
||||
|
||||
var mathExpr = query.replace(/[^0-9+\-*/().%\s^]/g, "");
|
||||
if (mathExpr.length < 2)
|
||||
return null;
|
||||
|
||||
var hasMath = /[+\-*/^%]/.test(query) && /\d/.test(query);
|
||||
if (!hasMath)
|
||||
return null;
|
||||
|
||||
try {
|
||||
var sanitized = mathExpr.replace(/\^/g, "**");
|
||||
var result = Function('"use strict"; return (' + sanitized + ')')();
|
||||
|
||||
if (typeof result === "number" && isFinite(result)) {
|
||||
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, "");
|
||||
return {
|
||||
expression: query,
|
||||
result: result,
|
||||
displayResult: displayResult
|
||||
};
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function sortPluginIdsByOrder(pluginIds, order) {
|
||||
if (!order || order.length === 0)
|
||||
return pluginIds;
|
||||
var orderMap = {};
|
||||
for (var i = 0; i < order.length; i++)
|
||||
orderMap[order[i]] = i;
|
||||
return pluginIds.slice().sort(function (a, b) {
|
||||
var aOrder = orderMap[a] !== undefined ? orderMap[a] : 9999;
|
||||
var bOrder = orderMap[b] !== undefined ? orderMap[b] : 9999;
|
||||
return aOrder - bOrder;
|
||||
});
|
||||
}
|
||||
|
||||
function sortPluginsOrdered(plugins, order) {
|
||||
if (!order || order.length === 0)
|
||||
return plugins;
|
||||
var orderMap = {};
|
||||
for (var i = 0; i < order.length; i++)
|
||||
orderMap[order[i]] = i;
|
||||
return plugins.sort(function (a, b) {
|
||||
var aOrder = orderMap[a.id] !== undefined ? orderMap[a.id] : 9999;
|
||||
var bOrder = orderMap[b.id] !== undefined ? orderMap[b.id] : 9999;
|
||||
return aOrder - bOrder;
|
||||
});
|
||||
}
|
||||
@@ -186,6 +186,14 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleWithQuery(query) {
|
||||
if (spotlightOpen) {
|
||||
hide();
|
||||
} else {
|
||||
showWithQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: closeCleanupTimer
|
||||
interval: Theme.expressiveDurations.expressiveFastSpatial + 50
|
||||
|
||||
@@ -36,7 +36,7 @@ Rectangle {
|
||||
readonly property int computedIconSize: Math.min(48, Math.max(32, width * 0.45))
|
||||
|
||||
radius: Theme.cornerRadius
|
||||
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : "transparent"
|
||||
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent"
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
|
||||
223
quickshell/Modals/DankLauncherV2/ItemTransformers.js
Normal file
223
quickshell/Modals/DankLauncherV2/ItemTransformers.js
Normal file
@@ -0,0 +1,223 @@
|
||||
.pragma library
|
||||
|
||||
.import "ControllerUtils.js" as Utils
|
||||
|
||||
function transformApp(app, override, defaultActions, primaryActionLabel) {
|
||||
var appId = app.id || app.execString || app.exec || "";
|
||||
|
||||
var actions = [];
|
||||
if (app.actions && app.actions.length > 0) {
|
||||
for (var i = 0; i < app.actions.length; i++) {
|
||||
actions.push({
|
||||
name: app.actions[i].name,
|
||||
icon: "play_arrow",
|
||||
actionData: app.actions[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: appId,
|
||||
type: "app",
|
||||
name: override?.name || app.name || "",
|
||||
subtitle: override?.comment || app.comment || "",
|
||||
icon: override?.icon || app.icon || "application-x-executable",
|
||||
iconType: "image",
|
||||
section: "apps",
|
||||
data: app,
|
||||
keywords: app.keywords || [],
|
||||
actions: actions,
|
||||
primaryAction: {
|
||||
name: primaryActionLabel,
|
||||
icon: "open_in_new",
|
||||
action: "launch"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function transformCoreApp(app, openLabel) {
|
||||
var iconName = "apps";
|
||||
var iconType = "material";
|
||||
|
||||
if (app.icon) {
|
||||
if (app.icon.startsWith("svg+corner:")) {
|
||||
iconType = "composite";
|
||||
} else if (app.icon.startsWith("material:")) {
|
||||
iconName = app.icon.substring(9);
|
||||
} else {
|
||||
iconName = app.icon;
|
||||
iconType = "image";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: app.builtInPluginId || app.action || "",
|
||||
type: "app",
|
||||
name: app.name || "",
|
||||
subtitle: app.comment || "",
|
||||
icon: iconName,
|
||||
iconType: iconType,
|
||||
iconFull: app.icon,
|
||||
section: "apps",
|
||||
data: app,
|
||||
isCore: true,
|
||||
actions: [],
|
||||
primaryAction: {
|
||||
name: openLabel,
|
||||
icon: "open_in_new",
|
||||
action: "launch"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function transformBuiltInLauncherItem(item, pluginId, openLabel) {
|
||||
var rawIcon = item.icon || "extension";
|
||||
var icon = Utils.stripIconPrefix(rawIcon);
|
||||
var iconType = item.iconType;
|
||||
if (!iconType) {
|
||||
if (rawIcon.startsWith("material:"))
|
||||
iconType = "material";
|
||||
else if (rawIcon.startsWith("unicode:"))
|
||||
iconType = "unicode";
|
||||
else
|
||||
iconType = "image";
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.action || "",
|
||||
type: "plugin",
|
||||
name: item.name || "",
|
||||
subtitle: item.comment || "",
|
||||
icon: icon,
|
||||
iconType: iconType,
|
||||
section: "plugin_" + pluginId,
|
||||
data: item,
|
||||
pluginId: pluginId,
|
||||
isBuiltInLauncher: true,
|
||||
keywords: item.keywords || [],
|
||||
actions: [],
|
||||
primaryAction: {
|
||||
name: openLabel,
|
||||
icon: "open_in_new",
|
||||
action: "execute"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel) {
|
||||
var filename = file.path ? file.path.split("/").pop() : "";
|
||||
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
|
||||
|
||||
return {
|
||||
id: file.path || "",
|
||||
type: "file",
|
||||
name: filename,
|
||||
subtitle: dirname,
|
||||
icon: Utils.getFileIcon(filename),
|
||||
iconType: "material",
|
||||
section: "files",
|
||||
data: file,
|
||||
actions: [
|
||||
{
|
||||
name: openFolderLabel,
|
||||
icon: "folder_open",
|
||||
action: "open_folder"
|
||||
},
|
||||
{
|
||||
name: copyPathLabel,
|
||||
icon: "content_copy",
|
||||
action: "copy_path"
|
||||
}
|
||||
],
|
||||
primaryAction: {
|
||||
name: openLabel,
|
||||
icon: "open_in_new",
|
||||
action: "open"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function transformPluginItem(item, pluginId, selectLabel) {
|
||||
var rawIcon = item.icon || "extension";
|
||||
var icon = Utils.stripIconPrefix(rawIcon);
|
||||
var iconType = item.iconType;
|
||||
if (!iconType) {
|
||||
if (rawIcon.startsWith("material:"))
|
||||
iconType = "material";
|
||||
else if (rawIcon.startsWith("unicode:"))
|
||||
iconType = "unicode";
|
||||
else
|
||||
iconType = "image";
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id || item.name || "",
|
||||
type: "plugin",
|
||||
name: item.name || "",
|
||||
subtitle: item.comment || item.description || "",
|
||||
icon: icon,
|
||||
iconType: iconType,
|
||||
section: "plugin_" + pluginId,
|
||||
data: item,
|
||||
pluginId: pluginId,
|
||||
keywords: item.keywords || [],
|
||||
actions: item.actions || [],
|
||||
primaryAction: item.primaryAction || {
|
||||
name: selectLabel,
|
||||
icon: "check",
|
||||
action: "execute"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createCalculatorItem(calc, query, copyLabel) {
|
||||
return {
|
||||
id: "calculator_result",
|
||||
type: "calculator",
|
||||
name: calc.displayResult,
|
||||
subtitle: query + " =",
|
||||
icon: "calculate",
|
||||
iconType: "material",
|
||||
section: "calculator",
|
||||
data: {
|
||||
expression: calc.expression,
|
||||
result: calc.result
|
||||
},
|
||||
actions: [],
|
||||
primaryAction: {
|
||||
name: copyLabel,
|
||||
icon: "content_copy",
|
||||
action: "copy"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createPluginBrowseItem(pluginId, plugin, trigger, isBuiltIn, isAllowed, browseLabel, triggerLabel, noTriggerLabel) {
|
||||
var rawIcon = isBuiltIn ? (plugin.cornerIcon || "extension") : (plugin.icon || "extension");
|
||||
return {
|
||||
id: "browse_" + pluginId,
|
||||
type: "plugin_browse",
|
||||
name: plugin.name || pluginId,
|
||||
subtitle: trigger ? triggerLabel.replace("%1", trigger) : noTriggerLabel,
|
||||
icon: isBuiltIn ? rawIcon : Utils.stripIconPrefix(rawIcon),
|
||||
iconType: isBuiltIn ? "material" : Utils.detectIconType(rawIcon),
|
||||
section: "browse_plugins",
|
||||
data: {
|
||||
pluginId: pluginId,
|
||||
plugin: plugin,
|
||||
isBuiltIn: isBuiltIn
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
name: "All",
|
||||
icon: isAllowed ? "visibility" : "visibility_off",
|
||||
action: "toggle_all_visibility"
|
||||
}
|
||||
],
|
||||
primaryAction: {
|
||||
name: browseLabel,
|
||||
icon: "arrow_forward",
|
||||
action: "browse_plugin"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -209,6 +209,10 @@ FocusScope {
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (event.modifiers & Qt.ShiftModifier) {
|
||||
controller.pasteSelected();
|
||||
return;
|
||||
}
|
||||
if (actionPanel.expanded && actionPanel.selectedActionIndex > 0) {
|
||||
actionPanel.executeSelectedAction();
|
||||
} else {
|
||||
@@ -267,21 +271,32 @@ FocusScope {
|
||||
anchors.fill: parent
|
||||
visible: !editMode
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: footerBar
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: root.parentModal?.borderWidth ?? 1
|
||||
anchors.rightMargin: root.parentModal?.borderWidth ?? 1
|
||||
anchors.bottomMargin: root.parentModal?.borderWidth ?? 1
|
||||
readonly property bool showFooter: SettingsData.dankLauncherV2Size !== "micro" && SettingsData.dankLauncherV2ShowFooter
|
||||
height: showFooter ? 32 : 0
|
||||
height: showFooter ? 36 : 0
|
||||
visible: showFooter
|
||||
color: Theme.surfaceContainerHigh
|
||||
radius: Theme.cornerRadius
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: -Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Row {
|
||||
id: modeButtonsRow
|
||||
x: I18n.isRtl ? parent.width - width - Theme.spacingS : Theme.spacingS
|
||||
y: (parent.height - height) / 2
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
layoutDirection: I18n.isRtl ? Qt.RightToLeft : Qt.LeftToRight
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
@@ -312,28 +327,25 @@ FocusScope {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: modeButtonMetrics.width + 14 + Theme.spacingXS + Theme.spacingM * 2 + Theme.spacingS
|
||||
height: footerBar.height - 4
|
||||
radius: Theme.cornerRadius - 2
|
||||
width: buttonContent.width + Theme.spacingM * 2
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: controller.searchMode === modelData.id || modeArea.containsMouse ? Theme.primaryContainer : "transparent"
|
||||
|
||||
TextMetrics {
|
||||
id: modeButtonMetrics
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: modelData.label
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonContent
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: modelData.icon
|
||||
size: 14
|
||||
color: controller.searchMode === modelData.id ? Theme.primary : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.label
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: controller.searchMode === modelData.id ? Theme.primary : Theme.surfaceText
|
||||
@@ -353,23 +365,28 @@ FocusScope {
|
||||
|
||||
Row {
|
||||
id: hintsRow
|
||||
x: I18n.isRtl ? Theme.spacingS : parent.width - width - Theme.spacingS
|
||||
y: (parent.height - height) / 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
layoutDirection: I18n.isRtl ? Qt.RightToLeft : Qt.LeftToRight
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "↑↓ " + I18n.tr("nav")
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "↵ " + I18n.tr("open")
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Tab " + I18n.tr("actions")
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Theme.surfaceVariantText
|
||||
@@ -386,7 +403,6 @@ FocusScope {
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.bottomMargin: Theme.spacingXS
|
||||
spacing: Theme.spacingXS
|
||||
clip: false
|
||||
|
||||
@@ -479,9 +495,64 @@ FocusScope {
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: categoryRow
|
||||
width: parent.width
|
||||
height: controller.activePluginCategories.length > 0 ? 36 : 0
|
||||
visible: controller.activePluginCategories.length > 0
|
||||
spacing: Theme.spacingS
|
||||
|
||||
clip: true
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: categoryDropdown
|
||||
width: Math.min(200, parent.width)
|
||||
compactMode: true
|
||||
dropdownWidth: 200
|
||||
popupWidth: 240
|
||||
maxPopupHeight: 300
|
||||
enableFuzzySearch: controller.activePluginCategories.length > 8
|
||||
currentValue: {
|
||||
const cats = controller.activePluginCategories;
|
||||
const current = controller.activePluginCategory;
|
||||
if (!current)
|
||||
return cats.length > 0 ? cats[0].name : "";
|
||||
for (let i = 0; i < cats.length; i++) {
|
||||
if (cats[i].id === current)
|
||||
return cats[i].name;
|
||||
}
|
||||
return cats.length > 0 ? cats[0].name : "";
|
||||
}
|
||||
options: {
|
||||
const cats = controller.activePluginCategories;
|
||||
const names = [];
|
||||
for (let i = 0; i < cats.length; i++)
|
||||
names.push(cats[i].name);
|
||||
return names;
|
||||
}
|
||||
|
||||
onValueChanged: value => {
|
||||
const cats = controller.activePluginCategories;
|
||||
for (let i = 0; i < cats.length; i++) {
|
||||
if (cats[i].name === value) {
|
||||
controller.setActivePluginCategory(cats[i].id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height - searchField.height - actionPanel.height - Theme.spacingXS * 2
|
||||
height: parent.height - searchField.height - categoryRow.height - actionPanel.height - Theme.spacingXS * (categoryRow.visible ? 3 : 2)
|
||||
opacity: root.parentModal?.isClosing ? 0 : 1
|
||||
|
||||
ResultsList {
|
||||
|
||||
@@ -131,6 +131,16 @@ Popup {
|
||||
items.push({
|
||||
type: "separator"
|
||||
});
|
||||
|
||||
if (isRegularApp && SessionService.nvidiaCommand) {
|
||||
items.push({
|
||||
type: "item",
|
||||
icon: "memory",
|
||||
text: I18n.tr("Launch on dGPU"),
|
||||
action: launchWithNvidia
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
type: "item",
|
||||
icon: "launch",
|
||||
|
||||
245
quickshell/Modals/DankLauncherV2/NavigationHelpers.js
Normal file
245
quickshell/Modals/DankLauncherV2/NavigationHelpers.js
Normal file
@@ -0,0 +1,245 @@
|
||||
.pragma library
|
||||
|
||||
function getFirstItemIndex(flatModel) {
|
||||
for (var i = 0; i < flatModel.length; i++) {
|
||||
if (!flatModel[i].isHeader)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function findNextNonHeaderIndex(flatModel, startIndex) {
|
||||
for (var i = startIndex; i < flatModel.length; i++) {
|
||||
if (!flatModel[i].isHeader)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function findPrevNonHeaderIndex(flatModel, startIndex) {
|
||||
for (var i = startIndex; i >= 0; i--) {
|
||||
if (!flatModel[i].isHeader)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getSectionBounds(flatModel, sectionId) {
|
||||
var start = -1, end = -1;
|
||||
for (var i = 0; i < flatModel.length; i++) {
|
||||
if (flatModel[i].isHeader && flatModel[i].section?.id === sectionId) {
|
||||
start = i + 1;
|
||||
} else if (start >= 0 && !flatModel[i].isHeader && flatModel[i].sectionId === sectionId) {
|
||||
end = i;
|
||||
} else if (start >= 0 && end >= 0 && flatModel[i].sectionId !== sectionId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
start: start,
|
||||
end: end,
|
||||
count: end >= start ? end - start + 1 : 0
|
||||
};
|
||||
}
|
||||
|
||||
function getGridColumns(viewMode, gridColumns) {
|
||||
switch (viewMode) {
|
||||
case "tile":
|
||||
return 3;
|
||||
case "grid":
|
||||
return gridColumns;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateNextIndex(flatModel, selectedFlatIndex, sectionId, viewMode, gridColumns, getSectionViewModeFn) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var entry = flatModel[selectedFlatIndex];
|
||||
if (!entry || entry.isHeader) {
|
||||
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
|
||||
return next !== -1 ? next : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var actualViewMode = viewMode || getSectionViewModeFn(entry.sectionId);
|
||||
if (actualViewMode === "list") {
|
||||
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
|
||||
return next !== -1 ? next : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var bounds = getSectionBounds(flatModel, entry.sectionId);
|
||||
var cols = getGridColumns(actualViewMode, gridColumns);
|
||||
var posInSection = selectedFlatIndex - bounds.start;
|
||||
var newPosInSection = posInSection + cols;
|
||||
|
||||
if (newPosInSection < bounds.count) {
|
||||
return bounds.start + newPosInSection;
|
||||
}
|
||||
|
||||
var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1);
|
||||
return nextSection !== -1 ? nextSection : selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculatePrevIndex(flatModel, selectedFlatIndex, sectionId, viewMode, gridColumns, getSectionViewModeFn) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var entry = flatModel[selectedFlatIndex];
|
||||
if (!entry || entry.isHeader) {
|
||||
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
|
||||
return prev !== -1 ? prev : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var actualViewMode = viewMode || getSectionViewModeFn(entry.sectionId);
|
||||
if (actualViewMode === "list") {
|
||||
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
|
||||
return prev !== -1 ? prev : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var bounds = getSectionBounds(flatModel, entry.sectionId);
|
||||
var cols = getGridColumns(actualViewMode, gridColumns);
|
||||
var posInSection = selectedFlatIndex - bounds.start;
|
||||
var newPosInSection = posInSection - cols;
|
||||
|
||||
if (newPosInSection >= 0) {
|
||||
return bounds.start + newPosInSection;
|
||||
}
|
||||
|
||||
var prevItem = findPrevNonHeaderIndex(flatModel, bounds.start - 1);
|
||||
return prevItem !== -1 ? prevItem : selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculateRightIndex(flatModel, selectedFlatIndex, getSectionViewModeFn) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var entry = flatModel[selectedFlatIndex];
|
||||
if (!entry || entry.isHeader) {
|
||||
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
|
||||
return next !== -1 ? next : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var viewMode = getSectionViewModeFn(entry.sectionId);
|
||||
if (viewMode === "list") {
|
||||
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
|
||||
return next !== -1 ? next : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var bounds = getSectionBounds(flatModel, entry.sectionId);
|
||||
var posInSection = selectedFlatIndex - bounds.start;
|
||||
if (posInSection + 1 < bounds.count) {
|
||||
return bounds.start + posInSection + 1;
|
||||
}
|
||||
return selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculateLeftIndex(flatModel, selectedFlatIndex, getSectionViewModeFn) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var entry = flatModel[selectedFlatIndex];
|
||||
if (!entry || entry.isHeader) {
|
||||
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
|
||||
return prev !== -1 ? prev : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var viewMode = getSectionViewModeFn(entry.sectionId);
|
||||
if (viewMode === "list") {
|
||||
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
|
||||
return prev !== -1 ? prev : selectedFlatIndex;
|
||||
}
|
||||
|
||||
var bounds = getSectionBounds(flatModel, entry.sectionId);
|
||||
var posInSection = selectedFlatIndex - bounds.start;
|
||||
if (posInSection > 0) {
|
||||
return bounds.start + posInSection - 1;
|
||||
}
|
||||
return selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculateNextSectionIndex(flatModel, selectedFlatIndex) {
|
||||
var currentSection = null;
|
||||
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) {
|
||||
currentSection = flatModel[selectedFlatIndex].sectionId;
|
||||
}
|
||||
|
||||
var foundCurrent = false;
|
||||
for (var i = 0; i < flatModel.length; i++) {
|
||||
if (flatModel[i].isHeader) {
|
||||
if (foundCurrent) {
|
||||
for (var j = i + 1; j < flatModel.length; j++) {
|
||||
if (!flatModel[j].isHeader)
|
||||
return j;
|
||||
}
|
||||
}
|
||||
if (flatModel[i].section.id === currentSection) {
|
||||
foundCurrent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculatePrevSectionIndex(flatModel, selectedFlatIndex) {
|
||||
var currentSection = null;
|
||||
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) {
|
||||
currentSection = flatModel[selectedFlatIndex].sectionId;
|
||||
}
|
||||
|
||||
var lastSectionStart = -1;
|
||||
var prevSectionStart = -1;
|
||||
|
||||
for (var i = 0; i < flatModel.length; i++) {
|
||||
if (flatModel[i].isHeader) {
|
||||
if (flatModel[i].section.id === currentSection) {
|
||||
break;
|
||||
}
|
||||
prevSectionStart = lastSectionStart;
|
||||
lastSectionStart = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSectionStart >= 0) {
|
||||
for (var j = prevSectionStart + 1; j < flatModel.length; j++) {
|
||||
if (!flatModel[j].isHeader)
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return selectedFlatIndex;
|
||||
}
|
||||
|
||||
function calculatePageDownIndex(flatModel, selectedFlatIndex, visibleItems) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var itemsToSkip = visibleItems || 8;
|
||||
var newIndex = selectedFlatIndex;
|
||||
|
||||
for (var i = 0; i < itemsToSkip; i++) {
|
||||
var next = findNextNonHeaderIndex(flatModel, newIndex + 1);
|
||||
if (next === -1)
|
||||
break;
|
||||
newIndex = next;
|
||||
}
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
function calculatePageUpIndex(flatModel, selectedFlatIndex, visibleItems) {
|
||||
if (flatModel.length === 0)
|
||||
return selectedFlatIndex;
|
||||
|
||||
var itemsToSkip = visibleItems || 8;
|
||||
var newIndex = selectedFlatIndex;
|
||||
|
||||
for (var i = 0; i < itemsToSkip; i++) {
|
||||
var prev = findPrevNonHeaderIndex(flatModel, newIndex - 1);
|
||||
if (prev === -1)
|
||||
break;
|
||||
newIndex = prev;
|
||||
}
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ Rectangle {
|
||||
|
||||
property var item: null
|
||||
property bool isSelected: false
|
||||
property bool isHovered: itemArea.containsMouse
|
||||
property bool isHovered: itemArea.containsMouse || allModeToggleArea.containsMouse
|
||||
property var controller: null
|
||||
property int flatIndex: -1
|
||||
|
||||
@@ -35,9 +35,32 @@ Rectangle {
|
||||
|
||||
width: parent?.width ?? 200
|
||||
height: 52
|
||||
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : "transparent"
|
||||
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent"
|
||||
radius: Theme.cornerRadius
|
||||
|
||||
MouseArea {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: root.item?.type === "plugin_browse" ? 40 : 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
var scenePos = mapToItem(null, mouse.x, mouse.y);
|
||||
root.rightClicked(scenePos.x, scenePos.y);
|
||||
} else {
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (root.controller)
|
||||
root.controller.keyboardNavigationActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
@@ -86,7 +109,47 @@ Rectangle {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
visible: root.item?.type && root.item.type !== "app"
|
||||
id: allModeToggle
|
||||
visible: root.item?.type === "plugin_browse"
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: allModeToggleArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
|
||||
property bool isAllowed: {
|
||||
if (root.item?.type !== "plugin_browse")
|
||||
return false;
|
||||
var pluginId = root.item?.data?.pluginId;
|
||||
if (!pluginId)
|
||||
return false;
|
||||
SettingsData.launcherPluginVisibility;
|
||||
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: allModeToggle.isAllowed ? "visibility" : "visibility_off"
|
||||
size: 18
|
||||
color: allModeToggle.isAllowed ? Theme.primary : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: allModeToggleArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
var pluginId = root.item?.data?.pluginId;
|
||||
if (!pluginId)
|
||||
return;
|
||||
SettingsData.setPluginAllowWithoutTrigger(pluginId, !allModeToggle.isAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.item?.type && root.item.type !== "app" && root.item.type !== "plugin_browse"
|
||||
width: typeBadge.implicitWidth + Theme.spacingS * 2
|
||||
height: 20
|
||||
radius: 10
|
||||
@@ -116,27 +179,4 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
var scenePos = mapToItem(null, mouse.x, mouse.y);
|
||||
root.rightClicked(scenePos.x, scenePos.y);
|
||||
} else {
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (root.controller) {
|
||||
root.controller.keyboardNavigationActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,15 @@ Item {
|
||||
contentHeight: sectionsColumn.height
|
||||
clip: true
|
||||
|
||||
Component.onCompleted: {
|
||||
verticalScrollBar.targetFlickable = mainFlickable;
|
||||
verticalScrollBar.parent = root;
|
||||
verticalScrollBar.z = 102;
|
||||
verticalScrollBar.anchors.right = root.right;
|
||||
verticalScrollBar.anchors.top = root.top;
|
||||
verticalScrollBar.anchors.bottom = root.bottom;
|
||||
}
|
||||
|
||||
Column {
|
||||
id: sectionsColumn
|
||||
width: parent.width
|
||||
|
||||
@@ -16,7 +16,7 @@ const Weights = {
|
||||
}
|
||||
|
||||
function tokenize(text) {
|
||||
return text.toLowerCase().trim().split(/[\s\-_]+/).filter(function(w) { return w.length > 0 })
|
||||
return text.toLowerCase().trim().split(/[\s\-_]+/).filter(function (w) { return w.length > 0 })
|
||||
}
|
||||
|
||||
function hasWordBoundaryMatch(text, query) {
|
||||
@@ -164,7 +164,7 @@ function scoreItems(items, query, getFrecencyFn) {
|
||||
}
|
||||
}
|
||||
|
||||
scored.sort(function(a, b) {
|
||||
scored.sort(function (a, b) {
|
||||
return b.score - a.score
|
||||
})
|
||||
|
||||
@@ -204,7 +204,7 @@ function groupBySection(scoredItems, sectionOrder, sortAlphabetically, maxPerSec
|
||||
var section = sections[sectionOrder[i].id]
|
||||
if (section && section.items.length > 0) {
|
||||
if (sortAlphabetically && section.id === "apps") {
|
||||
section.items.sort(function(a, b) {
|
||||
section.items.sort(function (a, b) {
|
||||
return (a.name || "").localeCompare(b.name || "")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ Rectangle {
|
||||
width: parent?.width ?? 200
|
||||
height: 32
|
||||
color: isSticky ? "transparent" : (hoverArea.containsMouse ? Theme.surfaceHover : "transparent")
|
||||
radius: Theme.cornerRadius / 2
|
||||
radius: Theme.cornerRadius
|
||||
|
||||
MouseArea {
|
||||
id: hoverArea
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
@@ -21,9 +23,22 @@ Rectangle {
|
||||
border.width: isSelected ? 2 : 0
|
||||
border.color: Theme.primary
|
||||
|
||||
readonly property string toplevelId: item?.data?.toplevelId ?? ""
|
||||
readonly property var waylandToplevel: {
|
||||
if (!toplevelId || !item?.pluginId)
|
||||
return null;
|
||||
const pluginInstance = PluginService.pluginInstances[item.pluginId];
|
||||
if (!pluginInstance?.getToplevelById)
|
||||
return null;
|
||||
return pluginInstance.getToplevelById(toplevelId);
|
||||
}
|
||||
readonly property bool hasScreencopy: waylandToplevel !== null
|
||||
|
||||
readonly property string iconValue: {
|
||||
if (!item)
|
||||
return "";
|
||||
if (hasScreencopy)
|
||||
return "";
|
||||
var data = item.data;
|
||||
if (data?.imageUrl)
|
||||
return "image:" + data.imageUrl;
|
||||
@@ -63,12 +78,26 @@ Rectangle {
|
||||
color: Theme.surfaceContainerHigh
|
||||
clip: true
|
||||
|
||||
ScreencopyView {
|
||||
id: screencopyView
|
||||
anchors.fill: parent
|
||||
captureSource: root.waylandToplevel
|
||||
live: root.hasScreencopy
|
||||
visible: root.hasScreencopy
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.isHovered ? Theme.withAlpha(Theme.surfaceVariant, 0.2) : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
AppIconRenderer {
|
||||
anchors.fill: parent
|
||||
iconValue: root.iconValue
|
||||
iconSize: Math.min(parent.width, parent.height)
|
||||
fallbackText: (root.item?.name?.length > 0) ? root.item.name.charAt(0).toUpperCase() : "?"
|
||||
materialIconSizeAdjustment: iconSize * 0.3
|
||||
visible: !root.hasScreencopy
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -109,6 +138,28 @@ Rectangle {
|
||||
color: Theme.primaryText
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: attributionBadge
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Theme.spacingXS
|
||||
width: root.hasScreencopy ? 28 : 40
|
||||
height: root.hasScreencopy ? 28 : 16
|
||||
radius: root.hasScreencopy ? 14 : 4
|
||||
color: root.hasScreencopy ? Theme.surfaceContainer : "transparent"
|
||||
visible: attributionImage.status === Image.Ready
|
||||
opacity: 0.95
|
||||
|
||||
Image {
|
||||
id: attributionImage
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.hasScreencopy ? 4 : 0
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: root.item?.data?.attribution || ""
|
||||
mipmap: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ FloatingWindow {
|
||||
id: sidebar
|
||||
|
||||
anchors.left: parent.left
|
||||
width: settingsModal.isCompactMode ? parent.width : 270
|
||||
width: settingsModal.isCompactMode ? parent.width : sidebar.implicitWidth
|
||||
visible: settingsModal.isCompactMode ? settingsModal.menuVisible : true
|
||||
parentModal: settingsModal
|
||||
currentIndex: settingsModal.currentTabIndex
|
||||
|
||||
@@ -483,11 +483,52 @@ Rectangle {
|
||||
return -1;
|
||||
}
|
||||
|
||||
width: 270
|
||||
property real __maxTextWidth: Math.max(__m1.advanceWidth, __m2.advanceWidth, __m3.advanceWidth, __m4.advanceWidth, __m5.advanceWidth, __m6.advanceWidth)
|
||||
property real __calculatedWidth: Math.max(270, __maxTextWidth + Theme.iconSize * 2 + Theme.spacingM * 4 + Theme.spacingS * 2)
|
||||
|
||||
implicitWidth: __calculatedWidth
|
||||
width: __calculatedWidth
|
||||
height: parent.height
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
|
||||
StyledTextMetrics {
|
||||
id: __m1
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Workspaces & Widgets")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m2
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Typography & Motion")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m3
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Keyboard Shortcuts")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m4
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Power & Security")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m5
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Dock & Launcher")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m6
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Personalization")
|
||||
}
|
||||
|
||||
function selectSearchResult(result) {
|
||||
if (!result)
|
||||
return;
|
||||
@@ -750,7 +791,7 @@ Rectangle {
|
||||
Rectangle {
|
||||
id: categoryRow
|
||||
width: parent.width
|
||||
height: 40
|
||||
height: Math.max(Theme.iconSize, Theme.fontSizeMedium) + Theme.spacingS * 2
|
||||
radius: Theme.cornerRadius
|
||||
visible: categoryDelegate.modelData.separator !== true
|
||||
|
||||
@@ -769,10 +810,9 @@ Rectangle {
|
||||
}
|
||||
|
||||
Row {
|
||||
id: categoryRowContent
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
@@ -789,19 +829,18 @@ Rectangle {
|
||||
font.weight: (categoryRow.isActive || root.isChildActive(categoryDelegate.modelData)) ? Font.Medium : Font.Normal
|
||||
color: categoryRow.isActive ? Theme.primaryText : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM - (categoryDelegate.modelData.children ? expandIcon.width + Theme.spacingS : 0)
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: expandIcon
|
||||
name: root.isCategoryExpanded(categoryDelegate.modelData.id) ? "expand_less" : "expand_more"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: categoryDelegate.modelData.children !== undefined && categoryDelegate.modelData.children.length > 0
|
||||
}
|
||||
DankIcon {
|
||||
id: expandIcon
|
||||
name: root.isCategoryExpanded(categoryDelegate.modelData.id) ? "expand_less" : "expand_more"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: categoryDelegate.modelData.children !== undefined && categoryDelegate.modelData.children.length > 0
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -847,7 +886,7 @@ Rectangle {
|
||||
readonly property bool isHighlighted: root.keyboardHighlightIndex === modelData.tabIndex
|
||||
|
||||
width: childrenColumn.width
|
||||
height: 36
|
||||
height: Math.max(Theme.iconSize - 4, Theme.fontSizeSmall + 1) + Theme.spacingS * 2
|
||||
radius: Theme.cornerRadius
|
||||
visible: root.isItemVisible(modelData)
|
||||
color: {
|
||||
@@ -861,6 +900,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
Row {
|
||||
id: childRowContent
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL + Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
229
quickshell/Modals/WorkspaceRenameModal.qml
Normal file
229
quickshell/Modals/WorkspaceRenameModal.qml
Normal file
@@ -0,0 +1,229 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
FloatingWindow {
|
||||
id: root
|
||||
|
||||
readonly property int inputFieldHeight: Theme.fontSizeMedium + Theme.spacingL * 2
|
||||
|
||||
objectName: "workspaceRenameModal"
|
||||
title: I18n.tr("Rename Workspace")
|
||||
minimumSize: Qt.size(400, 160)
|
||||
maximumSize: Qt.size(400, 160)
|
||||
color: Theme.surfaceContainer
|
||||
visible: false
|
||||
|
||||
function show(name) {
|
||||
nameInput.text = name;
|
||||
visible = true;
|
||||
Qt.callLater(() => nameInput.forceActiveFocus());
|
||||
}
|
||||
|
||||
function hide() {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
function submitAndClose() {
|
||||
renameWorkspace(nameInput.text);
|
||||
hide();
|
||||
}
|
||||
|
||||
function renameWorkspace(name) {
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.renameWorkspace(name);
|
||||
} else if (CompositorService.isHyprland) {
|
||||
HyprlandService.renameWorkspace(name);
|
||||
} else {
|
||||
console.warn("WorkspaceRenameModal: rename not supported for this compositor");
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
Qt.callLater(() => nameInput.forceActiveFocus());
|
||||
return;
|
||||
}
|
||||
nameInput.text = "";
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: contentFocusScope
|
||||
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
hide();
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentCol
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Item {
|
||||
width: contentCol.width
|
||||
height: Math.max(headerText.height, buttonRow.height)
|
||||
|
||||
MouseArea {
|
||||
anchors.left: parent.left
|
||||
anchors.right: buttonRow.left
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
height: parent.height
|
||||
onPressed: windowControls.tryStartMove()
|
||||
onDoubleClicked: windowControls.tryToggleMaximize()
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: headerText
|
||||
text: I18n.tr("Enter a new name for this workspace")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceTextMedium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - buttonRow.width - Theme.spacingM
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors.right: parent.right
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankActionButton {
|
||||
visible: windowControls.supported && windowControls.canMaximize
|
||||
iconName: root.maximized ? "fullscreen_exit" : "fullscreen"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: windowControls.tryToggleMaximize()
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: inputFieldHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceHover
|
||||
border.color: nameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: nameInput.activeFocus ? 2 : 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: nameInput.forceActiveFocus()
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: nameInput
|
||||
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
textColor: Theme.surfaceText
|
||||
placeholderText: I18n.tr("Workspace name")
|
||||
backgroundColor: "transparent"
|
||||
enabled: root.visible
|
||||
onAccepted: submitAndClose()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
|
||||
border.color: Theme.surfaceVariantAlpha
|
||||
border.width: 1
|
||||
|
||||
StyledText {
|
||||
id: cancelText
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Cancel")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: hide()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(80, renameText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: renameArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
|
||||
StyledText {
|
||||
id: renameText
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Rename")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.background
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: renameArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: submitAndClose()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FloatingWindowControls {
|
||||
id: windowControls
|
||||
targetWindow: root
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "workspace-rename"
|
||||
|
||||
function open(): string {
|
||||
const ws = NiriService.workspaces[NiriService.focusedWorkspaceId];
|
||||
show(ws?.name || "");
|
||||
return "WORKSPACE_RENAME_MODAL_OPENED";
|
||||
}
|
||||
|
||||
function close(): string {
|
||||
hide();
|
||||
return "WORKSPACE_RENAME_MODAL_CLOSED";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,9 @@ Item {
|
||||
case "analog":
|
||||
return 200;
|
||||
case "stacked":
|
||||
return 160;
|
||||
return 100;
|
||||
default:
|
||||
return 280;
|
||||
return 160;
|
||||
}
|
||||
}
|
||||
property real defaultHeight: {
|
||||
@@ -32,9 +32,9 @@ Item {
|
||||
case "analog":
|
||||
return 200;
|
||||
case "stacked":
|
||||
return 220;
|
||||
default:
|
||||
return 160;
|
||||
default:
|
||||
return 70;
|
||||
}
|
||||
}
|
||||
property real minWidth: {
|
||||
@@ -42,9 +42,9 @@ Item {
|
||||
case "analog":
|
||||
return 120;
|
||||
case "stacked":
|
||||
return 100;
|
||||
return 70;
|
||||
default:
|
||||
return 140;
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
property real minHeight: {
|
||||
@@ -52,9 +52,9 @@ Item {
|
||||
case "analog":
|
||||
return 120;
|
||||
case "stacked":
|
||||
return 140;
|
||||
default:
|
||||
return 100;
|
||||
default:
|
||||
return 45;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ Item {
|
||||
readonly property color backgroundColor: Theme.withAlpha(Theme.surface, root.transparency)
|
||||
|
||||
readonly property bool showAnalogSeconds: isInstance ? (cfg.showAnalogSeconds ?? true) : SettingsData.desktopClockShowAnalogSeconds
|
||||
readonly property bool needsSeconds: clockStyle === "analog" ? showAnalogSeconds : SettingsData.showSeconds
|
||||
readonly property bool showDigitalSeconds: isInstance ? (cfg.showDigitalSeconds ?? false) : false
|
||||
readonly property bool needsSeconds: clockStyle === "analog" ? showAnalogSeconds : showDigitalSeconds
|
||||
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
@@ -298,12 +299,25 @@ Item {
|
||||
Item {
|
||||
id: digitalRoot
|
||||
|
||||
property real baseSize: Math.max(28, height * 0.38)
|
||||
property real smallSize: Math.max(12, baseSize * 0.32)
|
||||
property bool hasDate: root.showDate
|
||||
property bool hasAmPm: !SettingsData.use24HourClock
|
||||
property real verticalScale: hasDate && hasAmPm ? 0.55 : (hasDate || hasAmPm ? 0.65 : 0.8)
|
||||
property real baseSize: Math.min(height * verticalScale, width * 0.22)
|
||||
property real digitWidth: baseSize * 0.62
|
||||
property real smallSize: baseSize * 0.35
|
||||
|
||||
property string hoursStr: {
|
||||
const hours = SettingsData.use24HourClock ? systemClock.date?.getHours() ?? 0 : ((systemClock.date?.getHours() ?? 0) % 12 || 12);
|
||||
if (SettingsData.use24HourClock || SettingsData.padHours12Hour)
|
||||
return String(hours).padStart(2, '0');
|
||||
return String(hours);
|
||||
}
|
||||
property string minutesStr: String(systemClock.date?.getMinutes() ?? 0).padStart(2, '0')
|
||||
property string secondsStr: String(systemClock.date?.getSeconds() ?? 0).padStart(2, '0')
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
visible: root.showDate
|
||||
@@ -314,51 +328,86 @@ Item {
|
||||
return systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? "";
|
||||
}
|
||||
font.pixelSize: digitalRoot.smallSize
|
||||
color: root.accentColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: {
|
||||
const hours = SettingsData.use24HourClock ? systemClock.date?.getHours() ?? 0 : ((systemClock.date?.getHours() ?? 0) % 12 || 12);
|
||||
const minutes = String(systemClock.date?.getMinutes() ?? 0).padStart(2, '0');
|
||||
return hours + ":" + minutes;
|
||||
}
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Normal
|
||||
color: root.accentColor
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
}
|
||||
|
||||
Row {
|
||||
visible: !SettingsData.use24HourClock || SettingsData.showSeconds
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Row {
|
||||
visible: SettingsData.showSeconds
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "timer"
|
||||
size: Math.max(10, digitalRoot.baseSize * 0.25)
|
||||
color: root.subtleTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock.date?.getSeconds() ?? 0).padStart(2, '0')
|
||||
font.pixelSize: digitalRoot.smallSize
|
||||
color: root.subtleTextColor
|
||||
}
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
visible: !SettingsData.use24HourClock
|
||||
text: (systemClock.date?.getHours() ?? 0) >= 12 ? "PM" : "AM"
|
||||
font.pixelSize: digitalRoot.smallSize
|
||||
visible: digitalRoot.hoursStr.length > 1
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.hoursStr.charAt(0)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.hoursStr.length > 1 ? digitalRoot.hoursStr.charAt(1) : digitalRoot.hoursStr.charAt(0)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
}
|
||||
StyledText {
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.minutesStr.charAt(0)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.minutesStr.charAt(1)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
visible: root.showDigitalSeconds
|
||||
text: ":"
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
}
|
||||
StyledText {
|
||||
visible: root.showDigitalSeconds
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.secondsStr.charAt(0)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
visible: root.showDigitalSeconds
|
||||
width: digitalRoot.digitWidth
|
||||
text: digitalRoot.secondsStr.charAt(1)
|
||||
font.pixelSize: digitalRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: !SettingsData.use24HourClock
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: (systemClock.date?.getHours() ?? 0) >= 12 ? "PM" : "AM"
|
||||
font.pixelSize: digitalRoot.smallSize
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,78 +419,127 @@ Item {
|
||||
Item {
|
||||
id: stackedRoot
|
||||
|
||||
property real baseSize: Math.max(32, height * 0.32)
|
||||
property real smallSize: Math.max(12, baseSize * 0.28)
|
||||
property bool hasSeconds: root.showDigitalSeconds
|
||||
property bool hasDate: root.showDate
|
||||
property bool hasAmPm: !SettingsData.use24HourClock
|
||||
property real extraContent: (hasSeconds ? 0.12 : 0) + (hasDate ? 0.08 : 0) + (hasAmPm ? 0.08 : 0)
|
||||
property real baseSize: height * (0.42 - extraContent * 0.5)
|
||||
property real digitWidth: baseSize * 0.58
|
||||
property real smallSize: baseSize * 0.5
|
||||
property real rowSpacing: -baseSize * 0.17
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: -baseSize * 0.1
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
visible: root.showDate
|
||||
Column {
|
||||
spacing: stackedRoot.rowSpacing
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
bottomPadding: Theme.spacingS
|
||||
text: {
|
||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0)
|
||||
return systemClock.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat) ?? "";
|
||||
return systemClock.date?.toLocaleDateString(Qt.locale(), "ddd, MMM d") ?? "";
|
||||
}
|
||||
font.pixelSize: stackedRoot.smallSize
|
||||
color: root.accentColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: {
|
||||
const hours = SettingsData.use24HourClock ? systemClock.date?.getHours() ?? 0 : ((systemClock.date?.getHours() ?? 0) % 12 || 12);
|
||||
return String(hours).padStart(2, '0');
|
||||
}
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Normal
|
||||
color: root.accentColor
|
||||
lineHeight: 0.85
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: String(systemClock.date?.getMinutes() ?? 0).padStart(2, '0')
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Normal
|
||||
color: root.accentColor
|
||||
lineHeight: 0.85
|
||||
}
|
||||
|
||||
Row {
|
||||
visible: SettingsData.showSeconds || !SettingsData.use24HourClock
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
topPadding: Theme.spacingXS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Row {
|
||||
visible: SettingsData.showSeconds
|
||||
spacing: Theme.spacingXS
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "timer"
|
||||
size: Math.max(10, stackedRoot.baseSize * 0.28)
|
||||
color: root.subtleTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.use24HourClock)
|
||||
return String(systemClock.date?.getHours() ?? 0).padStart(2, '0').charAt(0);
|
||||
const hours = systemClock.date?.getHours() ?? 0;
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(0);
|
||||
}
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
width: stackedRoot.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock.date?.getSeconds() ?? 0).padStart(2, '0')
|
||||
font.pixelSize: stackedRoot.smallSize
|
||||
color: root.subtleTextColor
|
||||
text: {
|
||||
if (SettingsData.use24HourClock)
|
||||
return String(systemClock.date?.getHours() ?? 0).padStart(2, '0').charAt(1);
|
||||
const hours = systemClock.date?.getHours() ?? 0;
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(1);
|
||||
}
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
width: stackedRoot.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock.date?.getMinutes() ?? 0).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
width: stackedRoot.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock.date?.getMinutes() ?? 0).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: stackedRoot.baseSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
width: stackedRoot.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
visible: stackedRoot.hasSeconds
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
visible: !SettingsData.use24HourClock
|
||||
text: (systemClock.date?.getHours() ?? 0) >= 12 ? "PM" : "AM"
|
||||
text: String(systemClock.date?.getSeconds() ?? 0).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: stackedRoot.smallSize
|
||||
font.weight: Font.Medium
|
||||
color: root.accentColor
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
width: stackedRoot.smallSize * 0.58
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock.date?.getSeconds() ?? 0).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: stackedRoot.smallSize
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
width: stackedRoot.smallSize * 0.58
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 1
|
||||
height: stackedRoot.baseSize * 0.1
|
||||
visible: stackedRoot.hasDate
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: stackedRoot.hasDate
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: systemClock.date?.toLocaleDateString(Qt.locale(), "MMM dd") ?? ""
|
||||
font.pixelSize: stackedRoot.smallSize * 0.7
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: stackedRoot.hasAmPm
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: (systemClock.date?.getHours() ?? 0) >= 12 ? "PM" : "AM"
|
||||
font.pixelSize: stackedRoot.smallSize * 0.7
|
||||
font.weight: Font.Medium
|
||||
color: Theme.withAlpha(root.accentColor, 0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,19 @@ Rectangle {
|
||||
property var widgetData: null
|
||||
property bool editMode: false
|
||||
|
||||
signal clicked()
|
||||
signal clicked
|
||||
|
||||
width: parent ? parent.width : 200
|
||||
height: 60
|
||||
radius: {
|
||||
if (Theme.cornerRadius === 0) return 0
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
|
||||
if (Theme.cornerRadius === 0)
|
||||
return 0;
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive:
|
||||
Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileBgActive: Theme.ccTileActiveBg
|
||||
readonly property color _tileBgInactive: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
readonly property color _tileRingActive: Theme.ccTileRing
|
||||
|
||||
color: isActive ? _tileBgActive : _tileBgInactive
|
||||
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
@@ -38,8 +37,8 @@ Rectangle {
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
const factor = 1.2;
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -49,7 +48,9 @@ Rectangle {
|
||||
opacity: mouseArea.containsMouse ? 0.08 : 0.0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.shortDuration }
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ Rectangle {
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? Theme.primaryText : Theme.primary
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.ccTileInactiveIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ Rectangle {
|
||||
width: parent.width
|
||||
text: root.text
|
||||
style: Typography.Style.Body
|
||||
color: isActive ? Theme.primaryText : Theme.surfaceText
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@@ -90,7 +91,7 @@ Rectangle {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
style: Typography.Style.Caption
|
||||
color: isActive ? Theme.primaryText : Theme.surfaceVariantText
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
|
||||
@@ -41,16 +41,16 @@ Rectangle {
|
||||
|
||||
readonly property color _labelPrimary: Theme.surfaceText
|
||||
readonly property color _labelSecondary: Theme.surfaceVariantText
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgActive: Theme.ccTileActiveBg
|
||||
readonly property color _tileBgInactive: {
|
||||
const transparency = Theme.popupTransparency;
|
||||
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1);
|
||||
return Qt.rgba(surface.r, surface.g, surface.b, transparency);
|
||||
}
|
||||
readonly property color _tileRingActive: Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileRingActive: Theme.ccTileRing
|
||||
readonly property color _tileRingInactive: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
|
||||
readonly property color _tileIconActive: Theme.primaryText
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
readonly property color _tileIconActive: Theme.ccTileActiveText
|
||||
readonly property color _tileIconInactive: Theme.ccTileInactiveIcon
|
||||
|
||||
property int _padH: Theme.spacingS
|
||||
property int _tileSize: 48
|
||||
|
||||
@@ -27,11 +27,11 @@ Rectangle {
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgActive: Theme.ccTileActiveBg
|
||||
readonly property color _tileBgInactive: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
readonly property color _tileRingActive: Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileIconActive: Theme.primaryText
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
readonly property color _tileRingActive: Theme.ccTileRing
|
||||
readonly property color _tileIconActive: Theme.ccTileActiveText
|
||||
readonly property color _tileIconInactive: Theme.ccTileInactiveIcon
|
||||
|
||||
color: {
|
||||
if (isActive)
|
||||
|
||||
@@ -73,7 +73,7 @@ Rectangle {
|
||||
return Theme.error;
|
||||
if (root.usagePercent > 75)
|
||||
return Theme.warning;
|
||||
return Theme.primary;
|
||||
return Theme.ccTileInactiveIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ Rectangle {
|
||||
return Theme.error;
|
||||
if (root.usagePercent > 75)
|
||||
return Theme.warning;
|
||||
return Theme.primary;
|
||||
return Theme.ccTileInactiveIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ Rectangle {
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor);
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgActive: Theme.ccTileActiveBg
|
||||
readonly property color _tileBgInactive: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
readonly property color _tileRingActive: Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileIconActive: Theme.primaryText
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
readonly property color _tileRingActive: Theme.ccTileRing
|
||||
readonly property color _tileIconActive: Theme.ccTileActiveText
|
||||
readonly property color _tileIconInactive: Theme.ccTileInactiveIcon
|
||||
|
||||
color: {
|
||||
if (isActive)
|
||||
|
||||
@@ -26,9 +26,9 @@ Rectangle {
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4;
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgActive: Theme.ccTileActiveBg
|
||||
readonly property color _tileBgInactive: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
readonly property color _tileRingActive: Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileRingActive: Theme.ccTileRing
|
||||
|
||||
color: {
|
||||
if (isActive)
|
||||
@@ -69,7 +69,7 @@ Rectangle {
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? Theme.primaryText : Theme.primary
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.ccTileInactiveIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rotation: root.iconRotation
|
||||
onRotationCompleted: root.iconRotationCompleted()
|
||||
@@ -89,7 +89,7 @@ Rectangle {
|
||||
width: parent.width
|
||||
text: root.text
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: isActive ? Theme.primaryText : Theme.surfaceText
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
@@ -100,7 +100,7 @@ Rectangle {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: isActive ? Theme.primaryText : Theme.surfaceVariantText
|
||||
color: isActive ? Theme.ccTileActiveText : Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
|
||||
@@ -19,6 +19,7 @@ Item {
|
||||
property var rightWidgetsModel
|
||||
|
||||
readonly property real innerPadding: barConfig?.innerPadding ?? 4
|
||||
readonly property real outlineThickness: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0
|
||||
|
||||
property alias hLeftSection: hLeftSection
|
||||
property alias hCenterSection: hCenterSection
|
||||
@@ -30,8 +31,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Math.max(Theme.spacingXS, innerPadding * 0.8)
|
||||
anchors.rightMargin: Math.max(Theme.spacingXS, innerPadding * 0.8)
|
||||
anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? 0 : Theme.spacingXS) : 0
|
||||
anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? 0 : Theme.spacingXS) : 0
|
||||
anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? outlineThickness : Theme.spacingXS) : 0
|
||||
anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? outlineThickness : Theme.spacingXS) : 0
|
||||
clip: false
|
||||
|
||||
property int componentMapRevision: 0
|
||||
@@ -302,6 +303,7 @@ Item {
|
||||
"workspaceSwitcher": workspaceSwitcherComponent,
|
||||
"focusedWindow": focusedWindowComponent,
|
||||
"runningApps": runningAppsComponent,
|
||||
"appsDock": appsDockComponent,
|
||||
"clock": clockComponent,
|
||||
"music": mediaComponent,
|
||||
"weather": weatherComponent,
|
||||
@@ -343,6 +345,7 @@ Item {
|
||||
"workspaceSwitcherComponent": workspaceSwitcherComponent,
|
||||
"focusedWindowComponent": focusedWindowComponent,
|
||||
"runningAppsComponent": runningAppsComponent,
|
||||
"appsDockComponent": appsDockComponent,
|
||||
"clockComponent": clockComponent,
|
||||
"mediaComponent": mediaComponent,
|
||||
"weatherComponent": weatherComponent,
|
||||
@@ -660,6 +663,21 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appsDockComponent
|
||||
|
||||
AppsDock {
|
||||
widgetThickness: barWindow.widgetThickness
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
barSpacing: barConfig?.spacing ?? 4
|
||||
section: topBarContent.getWidgetSection(parent)
|
||||
parentScreen: barWindow.screen
|
||||
topBar: topBarContent
|
||||
barConfig: topBarContent.barConfig
|
||||
isAutoHideBar: topBarContent.barConfig?.autoHide ?? false
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: clockComponent
|
||||
|
||||
|
||||
@@ -242,7 +242,8 @@ Loader {
|
||||
"colorPicker": components.colorPickerComponent,
|
||||
"systemUpdate": components.systemUpdateComponent,
|
||||
"layout": components.layoutComponent,
|
||||
"powerMenuButton": components.powerMenuButtonComponent
|
||||
"powerMenuButton": components.powerMenuButtonComponent,
|
||||
"appsDock": components.appsDockComponent
|
||||
};
|
||||
|
||||
if (componentMap[widgetId]) {
|
||||
|
||||
867
quickshell/Modules/DankBar/Widgets/AppsDock.qml
Normal file
867
quickshell/Modules/DankBar/Widgets/AppsDock.qml
Normal file
@@ -0,0 +1,867 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var widgetData: null
|
||||
property var barConfig: null
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "left"
|
||||
property var parentScreen
|
||||
property var hoveredItem: null
|
||||
property var topBar: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
property real barSpacing: 4
|
||||
property bool isAutoHideBar: false
|
||||
readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS
|
||||
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
|
||||
|
||||
property int draggedIndex: -1
|
||||
property int dropTargetIndex: -1
|
||||
property bool suppressShiftAnimation: false
|
||||
property int pinnedAppCount: 0
|
||||
|
||||
readonly property real effectiveBarThickness: {
|
||||
if (barThickness > 0 && barSpacing > 0) {
|
||||
return barThickness + barSpacing;
|
||||
}
|
||||
const innerPadding = barConfig?.innerPadding ?? 4;
|
||||
const spacing = barConfig?.spacing ?? 4;
|
||||
return Math.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding)) + spacing;
|
||||
}
|
||||
|
||||
readonly property var barBounds: {
|
||||
if (!parentScreen || !barConfig) {
|
||||
return {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 0,
|
||||
"height": 0,
|
||||
"wingSize": 0
|
||||
};
|
||||
}
|
||||
const barPosition = axis.edge === "left" ? 2 : (axis.edge === "right" ? 3 : (axis.edge === "top" ? 0 : 1));
|
||||
return SettingsData.getBarBounds(parentScreen, effectiveBarThickness, barPosition, barConfig);
|
||||
}
|
||||
|
||||
readonly property real barY: barBounds.y
|
||||
|
||||
readonly property real minTooltipY: {
|
||||
if (!parentScreen || !isVertical) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isAutoHideBar) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parentScreen.y > 0) {
|
||||
return effectiveBarThickness;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- Dock Logic Helpers ---
|
||||
function movePinnedApp(fromDockIndex, toDockIndex) {
|
||||
if (fromDockIndex === toDockIndex)
|
||||
return;
|
||||
|
||||
const currentPinned = [...(SessionData.barPinnedApps || [])];
|
||||
if (fromDockIndex < 0 || fromDockIndex >= currentPinned.length || toDockIndex < 0 || toDockIndex >= currentPinned.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const movedApp = currentPinned.splice(fromDockIndex, 1)[0];
|
||||
currentPinned.splice(toDockIndex, 0, movedApp);
|
||||
|
||||
SessionData.setBarPinnedApps(currentPinned);
|
||||
}
|
||||
|
||||
property int _desktopEntriesUpdateTrigger: 0
|
||||
property int _toplevelsUpdateTrigger: 0
|
||||
property int _appIdSubstitutionsTrigger: 0
|
||||
|
||||
Connections {
|
||||
target: CompositorService
|
||||
function onToplevelsChanged() {
|
||||
_toplevelsUpdateTrigger++;
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
_desktopEntriesUpdateTrigger++;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onAppIdSubstitutionsChanged() {
|
||||
_appIdSubstitutionsTrigger++;
|
||||
updateModel();
|
||||
}
|
||||
function onRunningAppsCurrentWorkspaceChanged() {
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onBarPinnedAppsChanged() {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
property var dockItems: []
|
||||
|
||||
function isOnScreen(toplevel, screenName) {
|
||||
if (!toplevel.screens)
|
||||
return false;
|
||||
for (let i = 0; i < toplevel.screens.length; i++) {
|
||||
if (toplevel.screens[i]?.name === screenName)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCoreAppData(appId) {
|
||||
if (typeof AppSearchService === "undefined")
|
||||
return null;
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
if (coreApps[i].builtInPluginId === appId)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getCoreAppDataByTitle(windowTitle) {
|
||||
if (typeof AppSearchService === "undefined" || !windowTitle)
|
||||
return null;
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
if (coreApps[i].name === windowTitle)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
const items = [];
|
||||
const pinnedApps = [...(SessionData.barPinnedApps || [])];
|
||||
_toplevelsUpdateTrigger;
|
||||
const allToplevels = CompositorService.sortedToplevels;
|
||||
|
||||
let sortedToplevels = allToplevels;
|
||||
if (SettingsData.runningAppsCurrentWorkspace && parentScreen) {
|
||||
sortedToplevels = CompositorService.filterCurrentWorkspace(allToplevels, parentScreen.name) || [];
|
||||
}
|
||||
|
||||
const appGroups = new Map();
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
const appId = Paths.moddedAppId(rawAppId);
|
||||
const coreAppData = getCoreAppData(appId);
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: true,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
});
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
let appId = Paths.moddedAppId(rawAppId);
|
||||
|
||||
let coreAppData = null;
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData) {
|
||||
appId = coreAppData.builtInPluginId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appGroups.has(appId)) {
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: false,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
}
|
||||
|
||||
appGroups.get(appId).windows.push({
|
||||
toplevel: toplevel,
|
||||
index: index,
|
||||
windowTitle: toplevel.title
|
||||
});
|
||||
});
|
||||
|
||||
const pinnedGroups = [];
|
||||
const unpinnedGroups = [];
|
||||
|
||||
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||
const firstWindow = group.windows.length > 0 ? group.windows[0] : null;
|
||||
|
||||
const item = {
|
||||
uniqueKey: "grouped_" + appId,
|
||||
type: "grouped",
|
||||
appId: appId,
|
||||
toplevel: firstWindow ? firstWindow.toplevel : null,
|
||||
isPinned: group.isPinned,
|
||||
isRunning: group.windows.length > 0,
|
||||
windowCount: group.windows.length,
|
||||
allWindows: group.windows,
|
||||
isCoreApp: group.isCoreApp || false,
|
||||
coreAppData: group.coreAppData || null
|
||||
};
|
||||
|
||||
if (group.isPinned) {
|
||||
pinnedGroups.push(item);
|
||||
} else {
|
||||
unpinnedGroups.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
pinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||
items.push({
|
||||
uniqueKey: "separator_grouped",
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
}
|
||||
|
||||
unpinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
root.pinnedAppCount = pinnedGroups.length;
|
||||
dockItems = items;
|
||||
}
|
||||
|
||||
Component.onCompleted: updateModel()
|
||||
|
||||
readonly property int calculatedSize: {
|
||||
const count = dockItems.length;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
if (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) {
|
||||
return count * 24 + (count - 1) * Theme.spacingXS + horizontalPadding * 2;
|
||||
} else {
|
||||
return count * (24 + Theme.spacingXS + 120) + (count - 1) * Theme.spacingXS + horizontalPadding * 2;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real realCalculatedSize: {
|
||||
let total = horizontalPadding * 2;
|
||||
const compact = (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode);
|
||||
|
||||
for (let i = 0; i < dockItems.length; i++) {
|
||||
const item = dockItems[i];
|
||||
let itemSize = 0;
|
||||
if (item.type === "separator") {
|
||||
itemSize = 8;
|
||||
} else {
|
||||
itemSize = compact ? 24 : (24 + Theme.spacingXS + 120);
|
||||
}
|
||||
|
||||
total += itemSize;
|
||||
if (i < dockItems.length - 1)
|
||||
total += Theme.spacingXS;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
width: dockItems.length > 0 ? (isVertical ? barThickness : realCalculatedSize) : 0
|
||||
height: dockItems.length > 0 ? (isVertical ? realCalculatedSize : barThickness) : 0
|
||||
visible: dockItems.length > 0
|
||||
|
||||
Item {
|
||||
id: visualBackground
|
||||
width: root.isVertical ? root.widgetThickness : root.realCalculatedSize
|
||||
height: root.isVertical ? root.realCalculatedSize : root.widgetThickness
|
||||
anchors.centerIn: parent
|
||||
clip: false
|
||||
|
||||
Rectangle {
|
||||
id: outline
|
||||
anchors.centerIn: parent
|
||||
width: {
|
||||
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
|
||||
return parent.width + borderWidth * 2;
|
||||
}
|
||||
height: {
|
||||
const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0;
|
||||
return parent.height + borderWidth * 2;
|
||||
}
|
||||
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
|
||||
color: "transparent"
|
||||
border.width: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0
|
||||
border.color: {
|
||||
if (!(barConfig?.widgetOutlineEnabled ?? false)) {
|
||||
return "transparent";
|
||||
}
|
||||
const colorOption = barConfig?.widgetOutlineColor || "primary";
|
||||
const opacity = barConfig?.widgetOutlineOpacity ?? 1.0;
|
||||
switch (colorOption) {
|
||||
case "surfaceText":
|
||||
return Theme.withAlpha(Theme.surfaceText, opacity);
|
||||
case "secondary":
|
||||
return Theme.withAlpha(Theme.secondary, opacity);
|
||||
case "primary":
|
||||
return Theme.withAlpha(Theme.primary, opacity);
|
||||
default:
|
||||
return Theme.withAlpha(Theme.primary, opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (dockItems.length === 0)
|
||||
return "transparent";
|
||||
if ((barConfig?.noBackground ?? false))
|
||||
return "transparent";
|
||||
|
||||
const baseColor = Theme.widgetBaseBackgroundColor;
|
||||
const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0;
|
||||
if (Theme.widgetBackgroundHasAlpha) {
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency);
|
||||
}
|
||||
return Theme.withAlpha(baseColor, transparency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: layoutLoader
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.isVertical ? columnLayout : rowLayout
|
||||
}
|
||||
|
||||
Component {
|
||||
id: rowLayout
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: ScriptModel {
|
||||
values: root.dockItems
|
||||
objectProp: "uniqueKey"
|
||||
}
|
||||
|
||||
delegate: dockDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: columnLayout
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.dockItems
|
||||
objectProp: "uniqueKey"
|
||||
}
|
||||
delegate: dockDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: dockDelegate
|
||||
Item {
|
||||
id: delegateItem
|
||||
property bool isSeparator: modelData.type === "separator"
|
||||
|
||||
readonly property real visualSize: isSeparator ? 8 : ((widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? 24 : (24 + Theme.spacingXS + 120))
|
||||
readonly property real visualWidth: root.isVertical ? root.barThickness : visualSize
|
||||
readonly property real visualHeight: root.isVertical ? visualSize : root.barThickness
|
||||
|
||||
width: visualWidth
|
||||
height: visualHeight
|
||||
|
||||
z: (dragHandler.dragging) ? 100 : 0
|
||||
|
||||
// --- Drag and Drop Shift Animation Logic ---
|
||||
property real shiftOffset: {
|
||||
if (root.draggedIndex < 0 || !modelData.isPinned || isSeparator)
|
||||
return 0;
|
||||
if (index === root.draggedIndex)
|
||||
return 0;
|
||||
|
||||
const dragIdx = root.draggedIndex;
|
||||
const dropIdx = root.dropTargetIndex;
|
||||
const myIdx = index;
|
||||
const shiftAmount = visualSize + Theme.spacingXS;
|
||||
|
||||
if (dropIdx < 0)
|
||||
return 0;
|
||||
if (dragIdx < dropIdx && myIdx > dragIdx && myIdx <= dropIdx)
|
||||
return -shiftAmount;
|
||||
if (dragIdx > dropIdx && myIdx >= dropIdx && myIdx < dragIdx)
|
||||
return shiftAmount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: root.isVertical ? 0 : delegateItem.shiftOffset
|
||||
y: root.isVertical ? delegateItem.shiftOffset : 0
|
||||
|
||||
Behavior on x {
|
||||
enabled: !root.suppressShiftAnimation
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
enabled: !root.suppressShiftAnimation
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: isSeparator
|
||||
width: root.isVertical ? root.barThickness * 0.6 : 2
|
||||
height: root.isVertical ? 2 : root.barThickness * 0.6
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||
radius: 1
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
id: appItem
|
||||
visible: !isSeparator
|
||||
anchors.fill: parent
|
||||
|
||||
property bool isFocused: {
|
||||
if (modelData.type === "grouped") {
|
||||
return modelData.allWindows.some(w => w.toplevel && w.toplevel.activated);
|
||||
}
|
||||
return modelData.toplevel ? modelData.toplevel.activated : false;
|
||||
}
|
||||
|
||||
property var appId: modelData.appId
|
||||
property int windowCount: modelData.windowCount || (modelData.isRunning ? 1 : 0)
|
||||
property string windowTitle: {
|
||||
if (modelData.type === "grouped") {
|
||||
const active = modelData.allWindows.find(w => w.toplevel && w.toplevel.activated);
|
||||
if (active)
|
||||
return active.windowTitle || "(Unnamed)";
|
||||
if (modelData.allWindows.length > 0)
|
||||
return modelData.allWindows[0].windowTitle || "(Unnamed)";
|
||||
return "";
|
||||
}
|
||||
return modelData.toplevel ? (modelData.toplevel.title || "(Unnamed)") : "";
|
||||
}
|
||||
|
||||
property string tooltipText: {
|
||||
root._desktopEntriesUpdateTrigger;
|
||||
const moddedId = Paths.moddedAppId(appId);
|
||||
const desktopEntry = moddedId ? DesktopEntries.heuristicLookup(moddedId) : null;
|
||||
const appName = appId ? Paths.getAppName(appId, desktopEntry) : "Unknown";
|
||||
|
||||
if (modelData.type === "grouped" && windowCount > 1) {
|
||||
return appName + " (" + windowCount + " windows)";
|
||||
}
|
||||
return appName + (windowTitle ? " • " + windowTitle : "");
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: (dragHandler.dragging && !root.isVertical) ? dragHandler.dragAxisOffset : 0
|
||||
y: (dragHandler.dragging && root.isVertical) ? dragHandler.dragAxisOffset : 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: visualContent
|
||||
width: root.isVertical ? 24 : delegateItem.visualSize
|
||||
height: root.isVertical ? delegateItem.visualSize : 24
|
||||
anchors.centerIn: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (appItem.isFocused) {
|
||||
return mouseArea.containsMouse ? Theme.primarySelected : Theme.withAlpha(Theme.primary, 0.2);
|
||||
}
|
||||
return mouseArea.containsMouse ? Theme.widgetBaseHoverColor : "transparent";
|
||||
}
|
||||
|
||||
border.width: dragHandler.dragging ? 2 : 0
|
||||
border.color: Theme.primary
|
||||
opacity: dragHandler.dragging ? 0.8 : 1.0
|
||||
|
||||
AppIconRenderer {
|
||||
id: coreIcon
|
||||
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
|
||||
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
|
||||
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
|
||||
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
|
||||
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
|
||||
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
|
||||
|
||||
iconSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
|
||||
materialIconSizeAdjustment: 0
|
||||
iconValue: {
|
||||
if (!modelData || !modelData.isCoreApp || !modelData.coreAppData)
|
||||
return "";
|
||||
const appId = modelData.coreAppData.id || modelData.coreAppData.builtInPluginId;
|
||||
if ((appId === "dms_settings" || appId === "dms_notepad" || appId === "dms_sysmon") && modelData.coreAppData.cornerIcon) {
|
||||
return "material:" + modelData.coreAppData.cornerIcon;
|
||||
}
|
||||
return modelData.coreAppData.icon || "";
|
||||
}
|
||||
colorOverride: Theme.widgetIconColor
|
||||
fallbackText: "?"
|
||||
visible: iconValue !== ""
|
||||
z: 2
|
||||
}
|
||||
|
||||
IconImage {
|
||||
id: iconImg
|
||||
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
|
||||
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
|
||||
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
|
||||
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
|
||||
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
|
||||
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
|
||||
|
||||
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
|
||||
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
|
||||
source: {
|
||||
root._desktopEntriesUpdateTrigger;
|
||||
root._appIdSubstitutionsTrigger;
|
||||
if (!appItem.appId)
|
||||
return "";
|
||||
if (modelData.isCoreApp)
|
||||
return ""; // Explicitly skip if core app to avoid flickering or wrong look ups
|
||||
const moddedId = Paths.moddedAppId(appItem.appId);
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(moddedId);
|
||||
return Paths.getAppIcon(appItem.appId, desktopEntry);
|
||||
}
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready && !coreIcon.visible
|
||||
layer.enabled: appItem.appId === "org.quickshell"
|
||||
layer.smooth: true
|
||||
layer.mipmap: true
|
||||
layer.effect: MultiEffect {
|
||||
saturation: 0
|
||||
colorization: 1
|
||||
colorizationColor: Theme.primary
|
||||
}
|
||||
z: 2
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
|
||||
anchors.left: (root.isVertical || isCompact) ? undefined : parent.left
|
||||
anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS
|
||||
anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined
|
||||
anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0
|
||||
anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined
|
||||
|
||||
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
|
||||
name: "sports_esports"
|
||||
color: Theme.widgetTextColor
|
||||
visible: !iconImg.visible && !coreIcon.visible && Paths.isSteamApp(appItem.appId)
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: !iconImg.visible && !coreIcon.visible && !Paths.isSteamApp(appItem.appId)
|
||||
text: {
|
||||
root._desktopEntriesUpdateTrigger;
|
||||
if (!appItem.appId)
|
||||
return "?";
|
||||
const moddedId = Paths.moddedAppId(appItem.appId);
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(moddedId);
|
||||
const appName = Paths.getAppName(appItem.appId, desktopEntry);
|
||||
return appName.charAt(0).toUpperCase();
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.widgetTextColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.rightMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? -2 : 2
|
||||
anchors.bottomMargin: -2
|
||||
width: 14
|
||||
height: 14
|
||||
radius: 7
|
||||
color: Theme.primary
|
||||
visible: modelData.type === "grouped" && appItem.windowCount > 1
|
||||
z: 10
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: appItem.windowCount > 9 ? "9+" : appItem.windowCount
|
||||
font.pixelSize: 9
|
||||
color: Theme.surface
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: !root.isVertical && !(widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode)
|
||||
anchors.left: iconImg.right
|
||||
anchors.leftMargin: Theme.spacingXS
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: appItem.windowTitle || appItem.appId
|
||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
||||
color: Theme.widgetTextColor
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: modelData.isRunning
|
||||
width: root.isVertical ? 2 : 20
|
||||
height: root.isVertical ? 20 : 2
|
||||
radius: 1
|
||||
color: appItem.isFocused ? Theme.primary : Theme.surfaceText
|
||||
opacity: appItem.isFocused ? 1 : 0.5
|
||||
|
||||
anchors.bottom: root.isVertical ? undefined : parent.bottom
|
||||
anchors.right: root.isVertical ? parent.right : undefined
|
||||
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
|
||||
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
|
||||
|
||||
anchors.margins: 0
|
||||
z: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for Drag Logic
|
||||
Item {
|
||||
id: dragHandler
|
||||
anchors.fill: parent
|
||||
property bool dragging: false
|
||||
property point dragStartPos: Qt.point(0, 0)
|
||||
property real dragAxisOffset: 0
|
||||
property bool longPressing: false
|
||||
|
||||
Timer {
|
||||
id: longPressTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (modelData.isPinned) {
|
||||
dragHandler.longPressing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.LeftButton && modelData.isPinned) {
|
||||
dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
longPressTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
longPressTimer.stop();
|
||||
const wasDragging = dragHandler.dragging;
|
||||
const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
|
||||
|
||||
if (didReorder) {
|
||||
root.movePinnedApp(root.draggedIndex, root.dropTargetIndex);
|
||||
}
|
||||
|
||||
dragHandler.longPressing = false;
|
||||
dragHandler.dragging = false;
|
||||
dragHandler.dragAxisOffset = 0;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
|
||||
if (wasDragging || mouse.button !== Qt.LeftButton)
|
||||
return;
|
||||
|
||||
if (wasDragging || mouse.button !== Qt.LeftButton)
|
||||
return;
|
||||
|
||||
if (modelData.type === "grouped") {
|
||||
if (modelData.windowCount === 0) {
|
||||
if (modelData.isCoreApp && modelData.coreAppData) {
|
||||
AppSearchService.executeCoreApp(modelData.coreAppData);
|
||||
} else {
|
||||
const moddedId = Paths.moddedAppId(modelData.appId);
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(moddedId);
|
||||
if (desktopEntry)
|
||||
SessionService.launchDesktopEntry(desktopEntry);
|
||||
}
|
||||
} else if (modelData.windowCount === 1) {
|
||||
if (modelData.allWindows[0].toplevel)
|
||||
modelData.allWindows[0].toplevel.activate();
|
||||
} else {
|
||||
let currentIndex = -1;
|
||||
for (var i = 0; i < modelData.allWindows.length; i++) {
|
||||
if (modelData.allWindows[i].toplevel.activated) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const nextIndex = (currentIndex + 1) % modelData.allWindows.length;
|
||||
modelData.allWindows[nextIndex].toplevel.activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (dragHandler.longPressing && !dragHandler.dragging) {
|
||||
const distance = Math.sqrt(Math.pow(mouse.x - dragHandler.dragStartPos.x, 2) + Math.pow(mouse.y - dragHandler.dragStartPos.y, 2));
|
||||
if (distance > 5) {
|
||||
dragHandler.dragging = true;
|
||||
root.draggedIndex = index;
|
||||
root.dropTargetIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dragHandler.dragging)
|
||||
return;
|
||||
|
||||
const axisOffset = root.isVertical ? (mouse.y - dragHandler.dragStartPos.y) : (mouse.x - dragHandler.dragStartPos.x);
|
||||
dragHandler.dragAxisOffset = axisOffset;
|
||||
|
||||
const itemSize = (root.isVertical ? delegateItem.height : delegateItem.width) + Theme.spacingXS;
|
||||
const slotOffset = Math.round(axisOffset / itemSize);
|
||||
const newTargetIndex = Math.max(0, Math.min(root.pinnedAppCount - 1, index + slotOffset));
|
||||
|
||||
if (newTargetIndex !== root.dropTargetIndex) {
|
||||
root.dropTargetIndex = newTargetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.hoveredItem = delegateItem;
|
||||
if (isSeparator)
|
||||
return;
|
||||
|
||||
tooltipLoader.active = true;
|
||||
if (tooltipLoader.item) {
|
||||
if (root.isVertical) {
|
||||
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
|
||||
const screenX = root.parentScreen ? root.parentScreen.x : 0;
|
||||
const screenY = root.parentScreen ? root.parentScreen.y : 0;
|
||||
const relativeY = globalPos.y - screenY;
|
||||
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + (barConfig?.spacing ?? 4) + Theme.spacingXS) : (root.parentScreen.width - Theme.barHeight - (barConfig?.spacing ?? 4) - Theme.spacingXS);
|
||||
const isLeft = root.axis?.edge === "left";
|
||||
const adjustedY = relativeY + root.minTooltipY;
|
||||
const finalX = screenX + tooltipX;
|
||||
tooltipLoader.item.show(appItem.tooltipText, finalX, adjustedY, root.parentScreen, isLeft, !isLeft);
|
||||
} else {
|
||||
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height);
|
||||
const screenHeight = root.parentScreen ? root.parentScreen.height : Screen.height;
|
||||
const isBottom = root.axis?.edge === "bottom";
|
||||
const tooltipY = isBottom ? (screenHeight - Theme.barHeight - (barConfig?.spacing ?? 4) - Theme.spacingXS - 35) : (Theme.barHeight + (barConfig?.spacing ?? 4) + Theme.spacingXS);
|
||||
tooltipLoader.item.show(appItem.tooltipText, globalPos.x, tooltipY, root.parentScreen, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (root.hoveredItem === delegateItem) {
|
||||
root.hoveredItem = null;
|
||||
if (tooltipLoader.item)
|
||||
tooltipLoader.item.hide();
|
||||
tooltipLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hide();
|
||||
}
|
||||
tooltipLoader.active = false;
|
||||
contextMenuLoader.active = true;
|
||||
|
||||
if (contextMenuLoader.item) {
|
||||
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
|
||||
|
||||
const isBarVertical = root.axis?.isVertical ?? false;
|
||||
const barEdge = root.axis?.edge ?? "top";
|
||||
|
||||
let x = globalPos.x;
|
||||
let y = globalPos.y;
|
||||
|
||||
if (barEdge === "bottom") {
|
||||
y = (root.parentScreen ? root.parentScreen.height : Screen.height) - root.effectiveBarThickness;
|
||||
} else if (barEdge === "top") {
|
||||
y = root.effectiveBarThickness;
|
||||
} else if (barEdge === "left") {
|
||||
x = root.effectiveBarThickness;
|
||||
} else if (barEdge === "right") {
|
||||
x = (root.parentScreen ? root.parentScreen.width : Screen.width) - root.effectiveBarThickness;
|
||||
}
|
||||
|
||||
const shouldHidePin = modelData.appId === "org.quickshell";
|
||||
const moddedId = Paths.moddedAppId(modelData.appId);
|
||||
const desktopEntry = moddedId ? DesktopEntries.heuristicLookup(moddedId) : null;
|
||||
|
||||
contextMenuLoader.item.showAt(x, y, isBarVertical, barEdge, modelData, shouldHidePin, desktopEntry, root.parentScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: contextMenuLoader
|
||||
active: false
|
||||
source: "AppsDockContextMenu.qml"
|
||||
}
|
||||
}
|
||||
431
quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml
Normal file
431
quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml
Normal file
@@ -0,0 +1,431 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
WlrLayershell.namespace: "dms:dock-context-menu"
|
||||
|
||||
property var appData: null
|
||||
property var anchorItem: null
|
||||
property int margin: 10
|
||||
property bool hidePin: false
|
||||
property var desktopEntry: null
|
||||
property bool isDmsWindow: appData?.appId === "org.quickshell"
|
||||
|
||||
property bool isVertical: false
|
||||
property string edge: "top"
|
||||
property point anchorPos: Qt.point(0, 0)
|
||||
|
||||
function showAt(x, y, vertical, barEdge, data, hidePinOption, entry, targetScreen) {
|
||||
if (targetScreen) {
|
||||
root.screen = targetScreen;
|
||||
}
|
||||
|
||||
anchorPos = Qt.point(x, y);
|
||||
isVertical = vertical ?? false;
|
||||
edge = barEdge ?? "top";
|
||||
|
||||
appData = data;
|
||||
hidePin = hidePinOption || false;
|
||||
desktopEntry = entry || null;
|
||||
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
screen: null
|
||||
visible: false
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menuContainer
|
||||
|
||||
x: {
|
||||
if (root.isVertical) {
|
||||
if (root.edge === "left") {
|
||||
return Math.min(root.width - width - 10, root.anchorPos.x);
|
||||
} else {
|
||||
return Math.max(10, root.anchorPos.x - width);
|
||||
}
|
||||
} else {
|
||||
const left = 10;
|
||||
const right = root.width - width - 10;
|
||||
const want = root.anchorPos.x - width / 2;
|
||||
return Math.max(left, Math.min(right, want));
|
||||
}
|
||||
}
|
||||
y: {
|
||||
if (root.isVertical) {
|
||||
const top = 10;
|
||||
const bottom = root.height - height - 10;
|
||||
const want = root.anchorPos.y - height / 2;
|
||||
return Math.max(top, Math.min(bottom, want));
|
||||
} else {
|
||||
if (root.edge === "top") {
|
||||
return Math.min(root.height - height - 10, root.anchorPos.y);
|
||||
} else {
|
||||
return Math.max(10, root.anchorPos.y - height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
width: Math.min(400, Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2))
|
||||
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
|
||||
opacity: root.visible ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 4
|
||||
anchors.leftMargin: 2
|
||||
anchors.rightMargin: -2
|
||||
anchors.bottomMargin: -4
|
||||
radius: parent.radius
|
||||
color: Qt.rgba(0, 0, 0, 0.15)
|
||||
z: -1
|
||||
}
|
||||
|
||||
Column {
|
||||
id: menuColumn
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Theme.spacingS
|
||||
spacing: 1
|
||||
|
||||
// Window list for grouped apps
|
||||
Repeater {
|
||||
model: {
|
||||
if (!root.appData || root.appData.type !== "grouped")
|
||||
return [];
|
||||
|
||||
const toplevels = [];
|
||||
const allToplevels = ToplevelManager.toplevels.values;
|
||||
for (let i = 0; i < allToplevels.length; i++) {
|
||||
const toplevel = allToplevels[i];
|
||||
if (toplevel.appId === root.appData.appId) {
|
||||
toplevels.push(toplevel);
|
||||
}
|
||||
}
|
||||
return toplevels;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: windowArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: closeButton.left
|
||||
anchors.rightMargin: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: (modelData && modelData.title) ? modelData.title : I18n.tr("(Unnamed)")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: closeButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: closeMouseArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.2) : "transparent"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "close"
|
||||
size: 12
|
||||
color: closeMouseArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.close) {
|
||||
modelData.close();
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: windowArea
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 24
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.activate) {
|
||||
modelData.activate();
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.appData)
|
||||
return false;
|
||||
if (root.appData.type !== "grouped")
|
||||
return false;
|
||||
return root.appData.windowCount > 0;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.desktopEntry && root.desktopEntry.actions ? root.desktopEntry.actions : []
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: actionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
height: 16
|
||||
visible: modelData.icon && modelData.icon !== ""
|
||||
|
||||
IconImage {
|
||||
anchors.fill: parent
|
||||
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.name || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: actionArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData) {
|
||||
SessionService.launchDesktopAction(root.desktopEntry, modelData);
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.desktopEntry?.actions || root.desktopEntry.actions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand);
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: !root.hidePin
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.appData && root.appData.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pinArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (!root.appData) {
|
||||
return;
|
||||
}
|
||||
if (root.appData.isPinned) {
|
||||
SessionData.removeBarPinnedApp(root.appData.appId);
|
||||
} else {
|
||||
SessionData.addBarPinnedApp(root.appData.appId);
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand;
|
||||
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0));
|
||||
const hasPinOption = !root.hidePin;
|
||||
const hasContentAbove = hasPinOption || hasNvidia;
|
||||
return hasContentAbove && hasWindow;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("Launch on dGPU")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: nvidiaArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.desktopEntry) {
|
||||
SessionService.launchDesktopEntry(root.desktopEntry, true);
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0))
|
||||
width: parent.width
|
||||
height: 28
|
||||
radius: Theme.cornerRadius
|
||||
color: closeArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (root.appData && root.appData.type === "grouped") {
|
||||
return I18n.tr("Close All Windows");
|
||||
}
|
||||
return I18n.tr("Close Window");
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.appData?.type === "window") {
|
||||
root.appData?.toplevel?.close();
|
||||
} else if (root.appData?.type === "grouped") {
|
||||
root.appData?.allWindows?.forEach(window => window.toplevel?.close());
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
onClicked: root.close()
|
||||
}
|
||||
}
|
||||
@@ -30,13 +30,11 @@ BasePill {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.use24HourClock) {
|
||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0);
|
||||
} else {
|
||||
const hours = systemClock?.date?.getHours();
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(0);
|
||||
}
|
||||
const hours = systemClock?.date?.getHours();
|
||||
if (SettingsData.use24HourClock)
|
||||
return String(hours).padStart(2, '0').charAt(0);
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(0);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||
color: Theme.widgetTextColor
|
||||
@@ -47,13 +45,11 @@ BasePill {
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.use24HourClock) {
|
||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1);
|
||||
} else {
|
||||
const hours = systemClock?.date?.getHours();
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(1);
|
||||
}
|
||||
const hours = systemClock?.date?.getHours();
|
||||
if (SettingsData.use24HourClock)
|
||||
return String(hours).padStart(2, '0').charAt(1);
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
return String(display).padStart(2, '0').charAt(1);
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||
color: Theme.widgetTextColor
|
||||
@@ -203,14 +199,101 @@ BasePill {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
id: timeText
|
||||
text: {
|
||||
return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat());
|
||||
property real fontSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||
property real digitWidth: fontSize * 0.6
|
||||
|
||||
property string hoursStr: {
|
||||
const hours = systemClock?.date?.getHours() ?? 0;
|
||||
if (SettingsData.use24HourClock)
|
||||
return String(hours).padStart(2, '0');
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
if (SettingsData.padHours12Hour)
|
||||
return String(display).padStart(2, '0');
|
||||
return String(display);
|
||||
}
|
||||
property string minutesStr: String(systemClock?.date?.getMinutes() ?? 0).padStart(2, '0')
|
||||
property string secondsStr: String(systemClock?.date?.getSeconds() ?? 0).padStart(2, '0')
|
||||
property string ampmStr: {
|
||||
if (SettingsData.use24HourClock)
|
||||
return "";
|
||||
const hours = systemClock?.date?.getHours() ?? 0;
|
||||
return hours >= 12 ? " PM" : " AM";
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
visible: clockRow.hoursStr.length > 1
|
||||
text: clockRow.hoursStr.charAt(0)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockRow.hoursStr.length > 1 ? clockRow.hoursStr.charAt(1) : clockRow.hoursStr.charAt(0)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockRow.minutesStr.charAt(0)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockRow.minutesStr.charAt(1)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.showSeconds
|
||||
text: ":"
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.showSeconds
|
||||
text: clockRow.secondsStr.charAt(0)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.showSeconds
|
||||
text: clockRow.secondsStr.charAt(1)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
width: clockRow.digitWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: !SettingsData.use24HourClock
|
||||
text: clockRow.ampmStr
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.baseline: dateText.baseline
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -218,7 +301,7 @@ BasePill {
|
||||
text: "•"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outlineButton
|
||||
anchors.baseline: dateText.baseline
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !compact
|
||||
}
|
||||
|
||||
@@ -230,7 +313,7 @@ BasePill {
|
||||
}
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d");
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||
font.pixelSize: clockRow.fontSize
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !compact
|
||||
|
||||
@@ -504,22 +504,21 @@ Item {
|
||||
readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
|
||||
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
|
||||
readonly property real appIconSize: Theme.barIconSize(barThickness, -6, root.barConfig?.noBackground)
|
||||
readonly property real appIconSize: Theme.barIconSize(barThickness, -6 + SettingsData.workspaceAppIconSizeOffset, root.barConfig?.noBackground)
|
||||
|
||||
function getRealWorkspaces() {
|
||||
return root.workspaceList.filter(ws => {
|
||||
if (useExtWorkspace)
|
||||
return ws && (ws.id !== "" || ws.name !== "") && !ws.hidden;
|
||||
if (CompositorService.isNiri)
|
||||
return ws && ws.idx !== -1;
|
||||
if (CompositorService.isHyprland)
|
||||
return ws && ws.id !== -1;
|
||||
if (CompositorService.isDwl)
|
||||
return ws && ws.tag !== -1;
|
||||
if (CompositorService.isSway || CompositorService.isScroll)
|
||||
return ws && ws.num !== -1;
|
||||
return ws !== -1;
|
||||
|
||||
if (useExtWorkspace)
|
||||
return ws && (ws.id !== "" || ws.name !== "") && !ws.hidden;
|
||||
if (CompositorService.isNiri)
|
||||
return ws && ws.idx !== -1;
|
||||
if (CompositorService.isHyprland)
|
||||
return ws && ws.id !== -1;
|
||||
if (CompositorService.isDwl)
|
||||
return ws && ws.tag !== -1;
|
||||
if (CompositorService.isSway || CompositorService.isScroll)
|
||||
return ws && ws.num !== -1;
|
||||
return ws !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -864,35 +863,91 @@ Item {
|
||||
property bool loadedHasIcon: false
|
||||
property var loadedIcons: []
|
||||
|
||||
readonly property real baseWidth: root.isVertical ? (SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5) : (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7)
|
||||
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5)
|
||||
readonly property int stableIconCount: {
|
||||
if (!SettingsData.showWorkspaceApps || isPlaceholder)
|
||||
return 0;
|
||||
|
||||
let targetWorkspaceId;
|
||||
if (root.useExtWorkspace) {
|
||||
targetWorkspaceId = modelData?.id || modelData?.name;
|
||||
} else if (CompositorService.isNiri) {
|
||||
targetWorkspaceId = modelData?.id;
|
||||
} else if (CompositorService.isHyprland) {
|
||||
targetWorkspaceId = modelData?.id;
|
||||
} else if (CompositorService.isDwl) {
|
||||
targetWorkspaceId = modelData?.tag;
|
||||
} else if (CompositorService.isSway || CompositorService.isScroll) {
|
||||
targetWorkspaceId = modelData?.num;
|
||||
}
|
||||
if (targetWorkspaceId === undefined || targetWorkspaceId === null)
|
||||
return 0;
|
||||
|
||||
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels;
|
||||
const seen = {};
|
||||
let groupedCount = 0;
|
||||
let totalCount = 0;
|
||||
|
||||
for (let i = 0; i < wins.length; i++) {
|
||||
const w = wins[i];
|
||||
if (!w)
|
||||
continue;
|
||||
|
||||
let winWs = null;
|
||||
if (CompositorService.isNiri) {
|
||||
winWs = w.workspace_id;
|
||||
} else if (CompositorService.isSway || CompositorService.isScroll) {
|
||||
winWs = w.workspace?.num;
|
||||
} else if (CompositorService.isHyprland) {
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels?.values || []);
|
||||
const hyprToplevel = hyprlandToplevels.find(ht => ht.wayland === w);
|
||||
winWs = hyprToplevel?.workspace?.id;
|
||||
}
|
||||
|
||||
if (winWs !== targetWorkspaceId)
|
||||
continue;
|
||||
totalCount++;
|
||||
|
||||
const appKey = w.app_id || w.appId || w.class || w.windowClass || "unknown";
|
||||
if (!seen[appKey]) {
|
||||
seen[appKey] = true;
|
||||
groupedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return SettingsData.groupWorkspaceApps ? groupedCount : totalCount;
|
||||
}
|
||||
|
||||
readonly property real baseWidth: root.isVertical ? (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5) : (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7)
|
||||
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? Math.max(widgetHeight * 0.7, root.appIconSize + Theme.spacingXS * 2) : widgetHeight * 0.5)
|
||||
readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== ""
|
||||
readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && CompositorService.isNiri
|
||||
readonly property real contentImplicitWidth: (hasWorkspaceName || loadedHasIcon) ? (appIconsLoader.item?.contentWidth ?? 0) : 0
|
||||
readonly property real contentImplicitHeight: (workspaceNamesEnabled || loadedHasIcon) ? (appIconsLoader.item?.contentHeight ?? 0) : 0
|
||||
|
||||
readonly property real iconsExtraWidth: {
|
||||
if (!root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
||||
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
|
||||
if (!root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) {
|
||||
const numIcons = Math.min(stableIconCount, SettingsData.maxWorkspaceIcons);
|
||||
return numIcons * root.appIconSize + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
readonly property real iconsExtraHeight: {
|
||||
if (root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
||||
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
|
||||
if (root.isVertical && SettingsData.showWorkspaceApps && stableIconCount > 0) {
|
||||
const numIcons = Math.min(stableIconCount, SettingsData.maxWorkspaceIcons);
|
||||
return numIcons * root.appIconSize + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
readonly property real visualWidth: {
|
||||
if (contentImplicitWidth <= 0) return baseWidth + iconsExtraWidth;
|
||||
if (contentImplicitWidth <= 0)
|
||||
return baseWidth + iconsExtraWidth;
|
||||
const padding = root.isVertical ? Theme.spacingXS : Theme.spacingS;
|
||||
return Math.max(baseWidth + iconsExtraWidth, contentImplicitWidth + padding);
|
||||
}
|
||||
readonly property real visualHeight: {
|
||||
if (contentImplicitHeight <= 0) return baseHeight + iconsExtraHeight;
|
||||
if (contentImplicitHeight <= 0)
|
||||
return baseHeight + iconsExtraHeight;
|
||||
const padding = root.isVertical ? Theme.spacingS : Theme.spacingXS;
|
||||
return Math.max(baseHeight + iconsExtraHeight, contentImplicitHeight + padding);
|
||||
}
|
||||
@@ -1080,6 +1135,20 @@ Item {
|
||||
width: root.isVertical ? root.widgetHeight : visualWidth
|
||||
height: root.isVertical ? visualHeight : root.widgetHeight
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: focusedBorderRing
|
||||
x: root.isVertical ? (root.widgetHeight - width) / 2 : (parent.width - width) / 2
|
||||
@@ -1349,6 +1418,15 @@ Item {
|
||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: root.getWorkspaceIndex(modelData, index)
|
||||
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
||||
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
|
||||
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
|
||||
|
||||
@@ -314,25 +314,39 @@ DankPopout {
|
||||
height: Theme.spacingXS
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
Item {
|
||||
id: pages
|
||||
width: parent.width
|
||||
height: implicitHeight
|
||||
implicitHeight: {
|
||||
if (currentIndex === 0)
|
||||
if (root.currentTabIndex === 0)
|
||||
return overviewLoader.item?.implicitHeight ?? 410;
|
||||
if (currentIndex === 1)
|
||||
if (root.currentTabIndex === 1)
|
||||
return mediaLoader.item?.implicitHeight ?? 410;
|
||||
if (currentIndex === 2)
|
||||
if (root.currentTabIndex === 2)
|
||||
return wallpaperLoader.item?.implicitHeight ?? 410;
|
||||
if (SettingsData.weatherEnabled && currentIndex === 3)
|
||||
if (SettingsData.weatherEnabled && root.currentTabIndex === 3)
|
||||
return weatherLoader.item?.implicitHeight ?? 410;
|
||||
return 410;
|
||||
}
|
||||
currentIndex: root.currentTabIndex
|
||||
|
||||
readonly property var currentItem: {
|
||||
if (root.currentTabIndex === 0)
|
||||
return overviewLoader.item;
|
||||
if (root.currentTabIndex === 1)
|
||||
return mediaLoader.item;
|
||||
if (root.currentTabIndex === 2)
|
||||
return wallpaperLoader.item;
|
||||
if (root.currentTabIndex === 3)
|
||||
return weatherLoader.item;
|
||||
return null;
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
anchors.fill: parent
|
||||
active: root.currentTabIndex === 0
|
||||
visible: active
|
||||
sourceComponent: Component {
|
||||
OverviewTab {
|
||||
onCloseDash: root.dashVisible = false
|
||||
@@ -350,7 +364,9 @@ DankPopout {
|
||||
|
||||
Loader {
|
||||
id: mediaLoader
|
||||
anchors.fill: parent
|
||||
active: root.currentTabIndex === 1
|
||||
visible: active
|
||||
sourceComponent: Component {
|
||||
MediaPlayerTab {
|
||||
targetScreen: root.screen
|
||||
@@ -379,7 +395,9 @@ DankPopout {
|
||||
|
||||
Loader {
|
||||
id: wallpaperLoader
|
||||
anchors.fill: parent
|
||||
active: root.currentTabIndex === 2
|
||||
visible: active
|
||||
sourceComponent: Component {
|
||||
WallpaperTab {
|
||||
active: true
|
||||
@@ -393,7 +411,9 @@ DankPopout {
|
||||
|
||||
Loader {
|
||||
id: weatherLoader
|
||||
anchors.fill: parent
|
||||
active: SettingsData.weatherEnabled && root.currentTabIndex === 3
|
||||
visible: active
|
||||
sourceComponent: Component {
|
||||
WeatherTab {}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ Item {
|
||||
}
|
||||
|
||||
implicitWidth: 700
|
||||
implicitHeight: 410
|
||||
implicitHeight: playerContent.height + playerContent.anchors.topMargin * 2
|
||||
|
||||
Connections {
|
||||
target: activePlayer
|
||||
@@ -327,6 +327,7 @@ Item {
|
||||
clip: false
|
||||
visible: !_noneAvailable && (!showNoPlayerNow)
|
||||
ColumnLayout {
|
||||
id: playerContent
|
||||
width: 484
|
||||
height: 370
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
@@ -12,7 +12,7 @@ Item {
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
implicitWidth: 700
|
||||
implicitHeight: 410
|
||||
implicitHeight: root.available ? mainColumn.implicitHeight : unavailableColumn.implicitHeight + Theme.spacingXL * 2
|
||||
property bool syncing: false
|
||||
|
||||
function syncFrom(type) {
|
||||
@@ -52,6 +52,7 @@ Item {
|
||||
property bool available: WeatherService.weather.available
|
||||
|
||||
Column {
|
||||
id: unavailableColumn
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingL
|
||||
visible: !root.available
|
||||
@@ -141,6 +142,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
visible: root.available
|
||||
spacing: Theme.spacingXS
|
||||
@@ -164,7 +166,7 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
width: weatherIcon.width + tempColumn.width + sunriseColumn.width + Theme.spacingM * 2
|
||||
height: 70
|
||||
height: Math.max(weatherIcon.height, tempColumn.height, sunriseColumn.height)
|
||||
|
||||
DankIcon {
|
||||
id: weatherIcon
|
||||
@@ -325,7 +327,7 @@ Item {
|
||||
Item {
|
||||
id: dateStepper
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 60
|
||||
height: dateStepperInner.height + Theme.spacingM * 2
|
||||
width: dateStepperInner.width
|
||||
|
||||
property var currentDate: new Date()
|
||||
@@ -354,10 +356,11 @@ Item {
|
||||
|
||||
Item {
|
||||
id: dateStepperInner
|
||||
anchors.fill: parent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
readonly property var space: Theme.spacingXS
|
||||
width: yearStepper.width + monthStepper.width + dayStepper.width + hourStepper.width + minuteStepper.width + (suffix.visible ? suffix.width : 0) + 10.5 * space + 2 * dateStepperInnerPadding.width
|
||||
height: Math.max(yearStepper.height, monthStepper.height, dayStepper.height, hourStepper.height, minuteStepper.height)
|
||||
|
||||
Item {
|
||||
id: dateStepperInnerPadding
|
||||
@@ -444,20 +447,15 @@ Item {
|
||||
text: ":"
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
StyledText {
|
||||
id: suffix
|
||||
visible: !SettingsData.use24HourClock
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenter: minuteStepper.verticalCenter
|
||||
anchors.left: minuteStepper.right
|
||||
anchors.leftMargin: 2 * parent.space
|
||||
StyledText {
|
||||
isMonospace: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: dateStepper.splitDate[5] ?? ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
x: -Theme.fontSizeSmall / 2
|
||||
y: -Theme.fontSizeSmall / 2
|
||||
}
|
||||
isMonospace: true
|
||||
text: dateStepper.splitDate[5] ?? ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
DankActionButton {
|
||||
id: dateResetButton
|
||||
@@ -898,7 +896,7 @@ Item {
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: 32
|
||||
height: Math.max(hourlyHeader.height, denseButton.height) + Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
@@ -928,7 +926,7 @@ Item {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 100 + Theme.spacingXS
|
||||
height: (hourlyLoader.item?.cardHeight ?? (Theme.fontSizeLarge * 6)) + Theme.spacingXS
|
||||
|
||||
Loader {
|
||||
id: hourlyLoader
|
||||
@@ -962,7 +960,7 @@ Item {
|
||||
contentHeight: cardHeight
|
||||
contentWidth: cardWidth
|
||||
|
||||
property var cardHeight: 100
|
||||
property var cardHeight: Theme.fontSizeLarge * 6
|
||||
property var cardWidth: ((hourlyList.width + hourlyList.spacing) / hourlyList.visibleCount) - hourlyList.spacing
|
||||
property int initialIndex: (new Date()).getHours()
|
||||
property bool dense: !SessionData.weatherHourlyDetailed
|
||||
@@ -1038,7 +1036,7 @@ Item {
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: 32
|
||||
height: dailyHeader.height + Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
@@ -1060,7 +1058,7 @@ Item {
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 100 + Theme.spacingXS
|
||||
height: (dailyLoader.item?.cardHeight ?? (Theme.fontSizeLarge * 6)) + Theme.spacingXS
|
||||
|
||||
Loader {
|
||||
id: dailyLoader
|
||||
@@ -1094,7 +1092,7 @@ Item {
|
||||
contentHeight: cardHeight
|
||||
contentWidth: cardWidth
|
||||
|
||||
property var cardHeight: 100
|
||||
property var cardHeight: Theme.fontSizeLarge * 6
|
||||
property var cardWidth: ((dailyList.width + dailyList.spacing) / dailyList.visibleCount) - dailyList.spacing
|
||||
property int initialIndex: 0
|
||||
property bool dense: false
|
||||
|
||||
@@ -437,21 +437,19 @@ Variants {
|
||||
|
||||
height: {
|
||||
if (dock.isVertical) {
|
||||
const extra = 4 + dock.borderThickness;
|
||||
const hiddenHeight = Math.min(Math.max(dockBackground.implicitHeight + 64, 200), screenHeight * 0.5);
|
||||
return dock.reveal ? Math.max(Math.min(dockBackground.implicitHeight + extra, maxDockHeight), hiddenHeight) : hiddenHeight;
|
||||
} else {
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
if (!dock.reveal)
|
||||
return Math.min(Math.max(dockBackground.height + 64, 200), screenHeight * 0.5);
|
||||
return Math.min(dockBackground.height + 8 + dock.borderThickness, maxDockHeight);
|
||||
}
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
}
|
||||
width: {
|
||||
if (dock.isVertical) {
|
||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
||||
} else {
|
||||
const extra = 4 + dock.borderThickness;
|
||||
const hiddenWidth = Math.min(Math.max(dockBackground.implicitWidth + 64, 200), screenWidth * 0.5);
|
||||
return dock.reveal ? Math.max(Math.min(dockBackground.implicitWidth + extra, maxDockWidth), hiddenWidth) : hiddenWidth;
|
||||
}
|
||||
if (!dock.reveal)
|
||||
return Math.min(Math.max(dockBackground.width + 64, 200), screenWidth * 0.5);
|
||||
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
||||
}
|
||||
anchors {
|
||||
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined
|
||||
|
||||
@@ -29,6 +29,15 @@ Item {
|
||||
property bool showTooltip: mouseArea.containsMouse && !dragging
|
||||
property var cachedDesktopEntry: null
|
||||
property real actualIconSize: 40
|
||||
property bool shouldShowIndicator: {
|
||||
if (!appData)
|
||||
return false;
|
||||
if (appData.type === "window")
|
||||
return true;
|
||||
if (appData.type === "grouped")
|
||||
return appData.windowCount > 0;
|
||||
return appData.isRunning;
|
||||
}
|
||||
readonly property string coreIconColorOverride: SettingsData.dockLauncherLogoColorOverride
|
||||
readonly property bool coreIconHasCustomColor: coreIconColorOverride !== "" && coreIconColorOverride !== "primary" && coreIconColorOverride !== "surface"
|
||||
readonly property color effectiveCoreIconColor: {
|
||||
@@ -206,7 +215,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
preventStealing: true
|
||||
preventStealing: dragging || longPressing
|
||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onPressed: mouse => {
|
||||
@@ -295,7 +304,7 @@ Item {
|
||||
groupedToplevel.activate();
|
||||
} else if (contextMenu) {
|
||||
const shouldHidePin = appData.appId === "org.quickshell";
|
||||
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -342,7 +351,7 @@ Item {
|
||||
case "grouped":
|
||||
if (contextMenu) {
|
||||
const shouldHidePin = appData.appId === "org.quickshell";
|
||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -365,7 +374,7 @@ Item {
|
||||
if (!contextMenu)
|
||||
return;
|
||||
const shouldHidePin = appData.appId === "org.quickshell";
|
||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -498,15 +507,7 @@ Item {
|
||||
|
||||
sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator
|
||||
|
||||
visible: {
|
||||
if (!appData)
|
||||
return false;
|
||||
if (appData.type === "window")
|
||||
return true;
|
||||
if (appData.type === "grouped")
|
||||
return appData.windowCount > 0;
|
||||
return appData.isRunning;
|
||||
}
|
||||
visible: root.shouldShowIndicator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,40 +17,55 @@ Item {
|
||||
property int draggedIndex: -1
|
||||
property int dropTargetIndex: -1
|
||||
property bool suppressShiftAnimation: false
|
||||
property int maxVisibleApps: SettingsData.dockMaxVisibleApps
|
||||
property int maxVisibleRunningApps: SettingsData.dockMaxVisibleRunningApps
|
||||
property bool overflowExpanded: false
|
||||
property int overflowItemCount: 0
|
||||
|
||||
readonly property real baseImplicitWidth: isVertical ? baseAppHeight : baseAppWidth
|
||||
readonly property real baseImplicitHeight: isVertical ? baseAppWidth : baseAppHeight
|
||||
readonly property real baseAppWidth: {
|
||||
let count = 0;
|
||||
const items = repeater.dockItems;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.isInOverflow)
|
||||
continue;
|
||||
if (item.type === "separator") {
|
||||
count += 8 / (iconSize * 1.2);
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count * (iconSize * 1.2) + Math.max(0, count - 1) * layoutFlow.spacing;
|
||||
}
|
||||
readonly property real baseAppHeight: iconSize
|
||||
|
||||
clip: false
|
||||
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
||||
implicitHeight: isVertical ? appLayout.width : appLayout.height
|
||||
|
||||
function dockIndexToPinnedIndex(dockIndex) {
|
||||
if (!SettingsData.dockLauncherEnabled) {
|
||||
if (!SettingsData.dockLauncherEnabled)
|
||||
return dockIndex;
|
||||
}
|
||||
|
||||
const launcherPos = SessionData.dockLauncherPosition;
|
||||
if (dockIndex < launcherPos) {
|
||||
return dockIndex;
|
||||
} else {
|
||||
return dockIndex - 1;
|
||||
}
|
||||
return dockIndex < launcherPos ? dockIndex : dockIndex - 1;
|
||||
}
|
||||
|
||||
function movePinnedApp(fromDockIndex, toDockIndex) {
|
||||
const fromPinnedIndex = dockIndexToPinnedIndex(fromDockIndex);
|
||||
const toPinnedIndex = dockIndexToPinnedIndex(toDockIndex);
|
||||
|
||||
if (fromPinnedIndex === toPinnedIndex) {
|
||||
if (fromPinnedIndex === toPinnedIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length) {
|
||||
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length)
|
||||
return;
|
||||
}
|
||||
|
||||
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
||||
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
||||
|
||||
SessionData.setPinnedApps(currentPinned);
|
||||
}
|
||||
|
||||
@@ -94,13 +109,10 @@ Item {
|
||||
function getCoreAppData(appId) {
|
||||
if (typeof AppSearchService === "undefined")
|
||||
return null;
|
||||
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
const app = coreApps[i];
|
||||
if (app.builtInPluginId === appId) {
|
||||
return app;
|
||||
}
|
||||
if (coreApps[i].builtInPluginId === appId)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -108,21 +120,193 @@ Item {
|
||||
function getCoreAppDataByTitle(windowTitle) {
|
||||
if (typeof AppSearchService === "undefined" || !windowTitle)
|
||||
return null;
|
||||
|
||||
const coreApps = AppSearchService.coreApps || [];
|
||||
for (let i = 0; i < coreApps.length; i++) {
|
||||
const app = coreApps[i];
|
||||
if (app.name === windowTitle) {
|
||||
return app;
|
||||
}
|
||||
if (coreApps[i].name === windowTitle)
|
||||
return coreApps[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildBaseItems() {
|
||||
const items = [];
|
||||
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||
const allToplevels = CompositorService.sortedToplevels;
|
||||
const sortedToplevels = (SettingsData.dockIsolateDisplays && root.dockScreen) ? allToplevels.filter(t => isOnScreen(t, root.dockScreen.name)) : allToplevels;
|
||||
|
||||
if (root.groupByApp) {
|
||||
return buildGroupedItems(pinnedApps, sortedToplevels);
|
||||
}
|
||||
return buildUngroupedItems(pinnedApps, sortedToplevels);
|
||||
}
|
||||
|
||||
function buildGroupedItems(pinnedApps, sortedToplevels) {
|
||||
const items = [];
|
||||
const appGroups = new Map();
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
const appId = Paths.moddedAppId(rawAppId);
|
||||
const coreAppData = getCoreAppData(appId);
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: true,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
});
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
let appId = Paths.moddedAppId(rawAppId);
|
||||
let coreAppData = null;
|
||||
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData)
|
||||
appId = coreAppData.builtInPluginId;
|
||||
}
|
||||
|
||||
if (!appGroups.has(appId)) {
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: false,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
}
|
||||
appGroups.get(appId).windows.push({
|
||||
toplevel: toplevel,
|
||||
index: index
|
||||
});
|
||||
});
|
||||
|
||||
const pinnedGroups = [];
|
||||
const unpinnedGroups = [];
|
||||
|
||||
appGroups.forEach((group, appId) => {
|
||||
const firstWindow = group.windows.length > 0 ? group.windows[0] : null;
|
||||
const item = {
|
||||
uniqueKey: "grouped_" + appId,
|
||||
type: "grouped",
|
||||
appId: appId,
|
||||
toplevel: firstWindow ? firstWindow.toplevel : null,
|
||||
isPinned: group.isPinned,
|
||||
isRunning: group.windows.length > 0,
|
||||
windowCount: group.windows.length,
|
||||
allWindows: group.windows,
|
||||
isCoreApp: group.isCoreApp || false,
|
||||
coreAppData: group.coreAppData || null
|
||||
};
|
||||
(group.isPinned ? pinnedGroups : unpinnedGroups).push(item);
|
||||
});
|
||||
|
||||
pinnedGroups.forEach(item => items.push(item));
|
||||
insertLauncher(items);
|
||||
|
||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||
items.push(createSeparator("separator_grouped"));
|
||||
}
|
||||
unpinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
return {
|
||||
items,
|
||||
pinnedCount: pinnedGroups.length,
|
||||
runningCount: unpinnedGroups.length
|
||||
};
|
||||
}
|
||||
|
||||
function buildUngroupedItems(pinnedApps, sortedToplevels) {
|
||||
const items = [];
|
||||
const runningAppIds = new Set();
|
||||
const windowItems = [];
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
let uniqueKey = "window_" + index;
|
||||
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values);
|
||||
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
||||
if (hyprlandToplevels[i].wayland === toplevel) {
|
||||
uniqueKey = "window_" + hyprlandToplevels[i].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
const moddedAppId = Paths.moddedAppId(rawAppId);
|
||||
let coreAppData = null;
|
||||
let isCoreApp = false;
|
||||
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData)
|
||||
isCoreApp = true;
|
||||
}
|
||||
|
||||
const finalAppId = isCoreApp ? coreAppData.builtInPluginId : moddedAppId;
|
||||
windowItems.push({
|
||||
uniqueKey: uniqueKey,
|
||||
type: "window",
|
||||
appId: finalAppId,
|
||||
toplevel: toplevel,
|
||||
isPinned: false,
|
||||
isRunning: true,
|
||||
isCoreApp: isCoreApp,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
runningAppIds.add(finalAppId);
|
||||
});
|
||||
|
||||
const remainingWindowItems = windowItems.slice();
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
const appId = Paths.moddedAppId(rawAppId);
|
||||
const coreAppData = getCoreAppData(appId);
|
||||
const matchIndex = remainingWindowItems.findIndex(item => item.appId === appId);
|
||||
|
||||
if (matchIndex !== -1) {
|
||||
const windowItem = remainingWindowItems.splice(matchIndex, 1)[0];
|
||||
windowItem.isPinned = true;
|
||||
if (!windowItem.isCoreApp && coreAppData) {
|
||||
windowItem.isCoreApp = true;
|
||||
windowItem.coreAppData = coreAppData;
|
||||
}
|
||||
items.push(windowItem);
|
||||
} else {
|
||||
items.push({
|
||||
uniqueKey: "pinned_" + appId,
|
||||
type: "pinned",
|
||||
appId: appId,
|
||||
toplevel: null,
|
||||
isPinned: true,
|
||||
isRunning: runningAppIds.has(appId),
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
insertLauncher(items);
|
||||
|
||||
if (pinnedApps.length > 0 && remainingWindowItems.length > 0) {
|
||||
items.push(createSeparator("separator_ungrouped"));
|
||||
}
|
||||
remainingWindowItems.forEach(item => items.push(item));
|
||||
|
||||
return {
|
||||
items,
|
||||
pinnedCount: pinnedApps.length,
|
||||
runningCount: remainingWindowItems.length
|
||||
};
|
||||
}
|
||||
|
||||
function insertLauncher(targetArray) {
|
||||
if (!SettingsData.dockLauncherEnabled)
|
||||
return;
|
||||
|
||||
const launcherItem = {
|
||||
uniqueKey: "launcher_button",
|
||||
type: "launcher",
|
||||
@@ -131,187 +315,154 @@ Item {
|
||||
isPinned: true,
|
||||
isRunning: false
|
||||
};
|
||||
|
||||
const pos = Math.max(0, Math.min(SessionData.dockLauncherPosition, targetArray.length));
|
||||
targetArray.splice(pos, 0, launcherItem);
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
const items = [];
|
||||
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||
const allToplevels = CompositorService.sortedToplevels;
|
||||
const sortedToplevels = (SettingsData.dockIsolateDisplays && root.dockScreen) ? allToplevels.filter(t => isOnScreen(t, root.dockScreen.name)) : allToplevels;
|
||||
function createSeparator(key) {
|
||||
return {
|
||||
uniqueKey: key,
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
};
|
||||
}
|
||||
|
||||
if (root.groupByApp) {
|
||||
const appGroups = new Map();
|
||||
function markAsOverflow(item) {
|
||||
return {
|
||||
uniqueKey: item.uniqueKey,
|
||||
type: item.type,
|
||||
appId: item.appId,
|
||||
toplevel: item.toplevel,
|
||||
isPinned: item.isPinned,
|
||||
isRunning: item.isRunning,
|
||||
windowCount: item.windowCount,
|
||||
allWindows: item.allWindows,
|
||||
isCoreApp: item.isCoreApp,
|
||||
coreAppData: item.coreAppData,
|
||||
isInOverflow: true
|
||||
};
|
||||
}
|
||||
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
const appId = Paths.moddedAppId(rawAppId);
|
||||
const coreAppData = getCoreAppData(appId);
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: true,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
});
|
||||
function applyOverflow(baseResult) {
|
||||
const {
|
||||
items
|
||||
} = baseResult;
|
||||
const maxPinned = root.maxVisibleApps;
|
||||
const maxRunning = root.maxVisibleRunningApps;
|
||||
const hideRunning = maxRunning === 0;
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
let appId = Paths.moddedAppId(rawAppId);
|
||||
const pinnedItems = items.filter(i => (i.type === "pinned" || i.type === "grouped" || i.type === "window") && i.isPinned && i.appId !== "__LAUNCHER__");
|
||||
const runningItems = hideRunning ? [] : items.filter(i => (i.type === "window" || i.type === "grouped") && i.isRunning && !i.isPinned);
|
||||
|
||||
let coreAppData = null;
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData) {
|
||||
appId = coreAppData.builtInPluginId;
|
||||
}
|
||||
}
|
||||
const pinnedOverflow = maxPinned > 0 && pinnedItems.length > maxPinned;
|
||||
const runningOverflow = !hideRunning && maxRunning > 0 && runningItems.length > maxRunning;
|
||||
|
||||
if (!appGroups.has(appId)) {
|
||||
appGroups.set(appId, {
|
||||
appId: appId,
|
||||
isPinned: false,
|
||||
windows: [],
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
}
|
||||
|
||||
appGroups.get(appId).windows.push({
|
||||
toplevel: toplevel,
|
||||
index: index
|
||||
});
|
||||
});
|
||||
|
||||
const pinnedGroups = [];
|
||||
const unpinnedGroups = [];
|
||||
|
||||
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||
const firstWindow = group.windows.length > 0 ? group.windows[0] : null;
|
||||
|
||||
const item = {
|
||||
uniqueKey: "grouped_" + appId,
|
||||
type: "grouped",
|
||||
appId: appId,
|
||||
toplevel: firstWindow ? firstWindow.toplevel : null,
|
||||
isPinned: group.isPinned,
|
||||
isRunning: group.windows.length > 0,
|
||||
windowCount: group.windows.length,
|
||||
allWindows: group.windows,
|
||||
isCoreApp: group.isCoreApp || false,
|
||||
coreAppData: group.coreAppData || null
|
||||
};
|
||||
|
||||
if (group.isPinned) {
|
||||
pinnedGroups.push(item);
|
||||
} else {
|
||||
unpinnedGroups.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
pinnedGroups.forEach(item => items.push(item));
|
||||
|
||||
insertLauncher(items);
|
||||
|
||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||
items.push({
|
||||
uniqueKey: "separator_grouped",
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
if (!pinnedOverflow && !runningOverflow) {
|
||||
root.overflowItemCount = 0;
|
||||
if (hideRunning) {
|
||||
return items.filter(i => !((i.type === "window" || i.type === "grouped") && i.isRunning && !i.isPinned) && i.type !== "separator");
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
unpinnedGroups.forEach(item => items.push(item));
|
||||
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
} else {
|
||||
pinnedApps.forEach(rawAppId => {
|
||||
const appId = Paths.moddedAppId(rawAppId);
|
||||
const coreAppData = getCoreAppData(appId);
|
||||
items.push({
|
||||
uniqueKey: "pinned_" + appId,
|
||||
type: "pinned",
|
||||
appId: appId,
|
||||
toplevel: null,
|
||||
isPinned: true,
|
||||
isRunning: false,
|
||||
isCoreApp: coreAppData !== null,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
});
|
||||
const visiblePinnedKeys = new Set(pinnedOverflow ? pinnedItems.slice(0, maxPinned).map(i => i.uniqueKey) : pinnedItems.map(i => i.uniqueKey));
|
||||
const visibleRunningKeys = new Set(runningOverflow ? runningItems.slice(0, maxRunning).map(i => i.uniqueKey) : runningItems.map(i => i.uniqueKey));
|
||||
|
||||
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||
const overflowPinnedCount = pinnedOverflow ? pinnedItems.length - maxPinned : 0;
|
||||
const overflowRunningCount = runningOverflow ? runningItems.length - maxRunning : 0;
|
||||
const totalOverflow = overflowPinnedCount + overflowRunningCount;
|
||||
root.overflowItemCount = totalOverflow;
|
||||
|
||||
insertLauncher(items);
|
||||
const finalItems = [];
|
||||
let addedSeparator = false;
|
||||
|
||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
||||
items.push({
|
||||
uniqueKey: "separator_ungrouped",
|
||||
type: "separator",
|
||||
appId: "__SEPARATOR__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false
|
||||
});
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
switch (item.type) {
|
||||
case "launcher":
|
||||
finalItems.push(item);
|
||||
break;
|
||||
case "separator":
|
||||
break;
|
||||
case "pinned":
|
||||
case "grouped":
|
||||
case "window":
|
||||
if (item.isPinned && item.appId !== "__LAUNCHER__") {
|
||||
if (visiblePinnedKeys.has(item.uniqueKey)) {
|
||||
finalItems.push(item);
|
||||
} else {
|
||||
finalItems.push(markAsOverflow(item));
|
||||
}
|
||||
} else if (item.isRunning && !item.isPinned) {
|
||||
if (!addedSeparator && finalItems.length > 0) {
|
||||
finalItems.push(createSeparator("separator_overflow"));
|
||||
addedSeparator = true;
|
||||
}
|
||||
if (visibleRunningKeys.has(item.uniqueKey)) {
|
||||
finalItems.push(item);
|
||||
} else if (!hideRunning) {
|
||||
finalItems.push(markAsOverflow(item));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sortedToplevels.forEach((toplevel, index) => {
|
||||
let uniqueKey = "window_" + index;
|
||||
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values);
|
||||
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
||||
if (hyprlandToplevels[i].wayland === toplevel) {
|
||||
uniqueKey = "window_" + hyprlandToplevels[i].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rawAppId = toplevel.appId || "unknown";
|
||||
const moddedAppId = Paths.moddedAppId(rawAppId);
|
||||
|
||||
// Check if this is a core app window (e.g., Settings modal with appId "org.quickshell")
|
||||
let coreAppData = null;
|
||||
let isCoreApp = false;
|
||||
if (rawAppId === "org.quickshell") {
|
||||
coreAppData = getCoreAppDataByTitle(toplevel.title);
|
||||
if (coreAppData) {
|
||||
isCoreApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
const finalAppId = isCoreApp ? coreAppData.builtInPluginId : moddedAppId;
|
||||
const isPinned = pinnedApps.indexOf(finalAppId) !== -1;
|
||||
|
||||
items.push({
|
||||
uniqueKey: uniqueKey,
|
||||
type: "window",
|
||||
appId: finalAppId,
|
||||
toplevel: toplevel,
|
||||
isPinned: isPinned,
|
||||
isRunning: true,
|
||||
isCoreApp: isCoreApp,
|
||||
coreAppData: coreAppData
|
||||
});
|
||||
if (totalOverflow > 0) {
|
||||
const toggleIndex = finalItems.findIndex(i => i.type === "separator");
|
||||
const insertPos = toggleIndex >= 0 ? toggleIndex : finalItems.length;
|
||||
finalItems.splice(insertPos, 0, {
|
||||
uniqueKey: "overflow_toggle",
|
||||
type: "overflow-toggle",
|
||||
appId: "__OVERFLOW_TOGGLE__",
|
||||
toplevel: null,
|
||||
isPinned: false,
|
||||
isRunning: false,
|
||||
overflowCount: totalOverflow
|
||||
});
|
||||
}
|
||||
|
||||
dockItems = items;
|
||||
return finalItems;
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
const baseResult = buildBaseItems();
|
||||
dockItems = applyOverflow(baseResult);
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
|
||||
property var dockButton: itemData.type === "launcher" ? launcherButton : button
|
||||
property var itemData: modelData
|
||||
readonly property bool isOverflowToggle: itemData.type === "overflow-toggle"
|
||||
readonly property bool isInOverflow: itemData.isInOverflow === true
|
||||
|
||||
clip: false
|
||||
z: (itemData.type === "launcher" ? launcherButton.dragging : button.dragging) ? 100 : 0
|
||||
visible: !isInOverflow || root.overflowExpanded
|
||||
opacity: (isInOverflow && !root.overflowExpanded) ? 0 : 1
|
||||
scale: (isInOverflow && !root.overflowExpanded) ? 0.8 : 1
|
||||
|
||||
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
||||
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
||||
width: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2))
|
||||
height: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize))
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
property real shiftOffset: {
|
||||
if (root.draggedIndex < 0 || !itemData.isPinned || itemData.type === "separator")
|
||||
@@ -363,34 +514,42 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
DockOverflowButton {
|
||||
id: overflowButton
|
||||
visible: isOverflowToggle
|
||||
anchors.centerIn: parent
|
||||
width: delegateItem.width
|
||||
height: delegateItem.height
|
||||
actualIconSize: root.iconSize
|
||||
overflowCount: itemData.overflowCount || 0
|
||||
overflowExpanded: root.overflowExpanded
|
||||
isVertical: root.isVertical
|
||||
onClicked: root.overflowExpanded = !root.overflowExpanded
|
||||
}
|
||||
|
||||
DockLauncherButton {
|
||||
id: launcherButton
|
||||
visible: itemData.type === "launcher"
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: delegateItem.width
|
||||
height: delegateItem.height
|
||||
actualIconSize: root.iconSize
|
||||
|
||||
dockApps: root
|
||||
index: model.index
|
||||
}
|
||||
|
||||
DockAppButton {
|
||||
id: button
|
||||
visible: itemData.type !== "separator" && itemData.type !== "launcher"
|
||||
visible: !isOverflowToggle && itemData.type !== "separator" && itemData.type !== "launcher"
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: delegateItem.width
|
||||
height: delegateItem.height
|
||||
actualIconSize: root.iconSize
|
||||
|
||||
appData: itemData
|
||||
contextMenu: root.contextMenu
|
||||
dockApps: root
|
||||
index: model.index
|
||||
parentDockScreen: root.dockScreen
|
||||
|
||||
showWindowTitle: itemData?.type === "window" || itemData?.type === "grouped"
|
||||
windowTitle: {
|
||||
const title = itemData?.toplevel?.title || "(Unnamed)";
|
||||
@@ -420,10 +579,17 @@ Item {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
function onDockLauncherPositionChanged() {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
repeater.updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onGroupByAppChanged: repeater.updateModel()
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onDockIsolateDisplaysChanged() {
|
||||
@@ -438,18 +604,13 @@ Item {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onDockLauncherPositionChanged() {
|
||||
root.suppressShiftAnimation = true;
|
||||
root.draggedIndex = -1;
|
||||
root.dropTargetIndex = -1;
|
||||
function onDockMaxVisibleAppsChanged() {
|
||||
repeater.updateModel();
|
||||
}
|
||||
function onDockMaxVisibleRunningAppsChanged() {
|
||||
repeater.updateModel();
|
||||
Qt.callLater(() => {
|
||||
root.suppressShiftAnimation = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onGroupByAppChanged: repeater.updateModel()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -19,22 +18,24 @@ PanelWindow {
|
||||
property bool hidePin: false
|
||||
property var desktopEntry: null
|
||||
property bool isDmsWindow: appData?.appId === "org.quickshell"
|
||||
property var dockApps: null
|
||||
|
||||
function showForButton(button, data, dockHeight, hidePinOption, entry, dockScreen) {
|
||||
function showForButton(button, data, dockHeight, hidePinOption, entry, dockScreen, parentDockApps) {
|
||||
if (dockScreen) {
|
||||
root.screen = dockScreen
|
||||
root.screen = dockScreen;
|
||||
}
|
||||
|
||||
anchorItem = button
|
||||
appData = data
|
||||
dockVisibleHeight = dockHeight || 40
|
||||
hidePin = hidePinOption || false
|
||||
desktopEntry = entry || null
|
||||
anchorItem = button;
|
||||
appData = data;
|
||||
dockVisibleHeight = dockHeight || 40;
|
||||
hidePin = hidePinOption || false;
|
||||
desktopEntry = entry || null;
|
||||
dockApps = parentDockApps || null;
|
||||
|
||||
visible = true
|
||||
visible = true;
|
||||
}
|
||||
function close() {
|
||||
visible = false
|
||||
visible = false;
|
||||
}
|
||||
|
||||
screen: null
|
||||
@@ -55,110 +56,110 @@ PanelWindow {
|
||||
onAnchorItemChanged: updatePosition()
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
updatePosition()
|
||||
updatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
if (!anchorItem) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
|
||||
return
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const dockWindow = anchorItem.Window.window
|
||||
const dockWindow = anchorItem.Window.window;
|
||||
if (!dockWindow) {
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
|
||||
return
|
||||
anchorPos = Qt.point(screen.width / 2, screen.height - 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const buttonPosInDock = anchorItem.mapToItem(dockWindow.contentItem, 0, 0)
|
||||
let actualDockHeight = root.dockVisibleHeight
|
||||
const buttonPosInDock = anchorItem.mapToItem(dockWindow.contentItem, 0, 0);
|
||||
let actualDockHeight = root.dockVisibleHeight;
|
||||
|
||||
function findDockBackground(item) {
|
||||
if (item.objectName === "dockBackground") {
|
||||
return item
|
||||
return item;
|
||||
}
|
||||
for (var i = 0; i < item.children.length; i++) {
|
||||
const found = findDockBackground(item.children[i])
|
||||
const found = findDockBackground(item.children[i]);
|
||||
if (found) {
|
||||
return found
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const dockBackground = findDockBackground(dockWindow.contentItem)
|
||||
let actualDockWidth = dockWindow.width
|
||||
const dockBackground = findDockBackground(dockWindow.contentItem);
|
||||
let actualDockWidth = dockWindow.width;
|
||||
if (dockBackground) {
|
||||
actualDockHeight = dockBackground.height
|
||||
actualDockWidth = dockBackground.width
|
||||
actualDockHeight = dockBackground.height;
|
||||
actualDockWidth = dockBackground.width;
|
||||
}
|
||||
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const dockMargin = SettingsData.dockMargin + 16
|
||||
let buttonScreenX, buttonScreenY
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
const dockMargin = SettingsData.dockMargin + 16;
|
||||
let buttonScreenX, buttonScreenY;
|
||||
|
||||
if (isVertical) {
|
||||
const dockContentHeight = dockWindow.height
|
||||
const screenHeight = root.screen.height
|
||||
const dockTopMargin = Math.round((screenHeight - dockContentHeight) / 2)
|
||||
buttonScreenY = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2
|
||||
const dockContentHeight = dockWindow.height;
|
||||
const screenHeight = root.screen.height;
|
||||
const dockTopMargin = Math.round((screenHeight - dockContentHeight) / 2);
|
||||
buttonScreenY = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2;
|
||||
|
||||
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
||||
buttonScreenX = root.screen.width - actualDockWidth - dockMargin - 20
|
||||
buttonScreenX = root.screen.width - actualDockWidth - dockMargin - 20;
|
||||
} else {
|
||||
buttonScreenX = actualDockWidth + dockMargin + 20
|
||||
buttonScreenX = actualDockWidth + dockMargin + 20;
|
||||
}
|
||||
} else {
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
||||
|
||||
if (isDockAtBottom) {
|
||||
buttonScreenY = root.screen.height - actualDockHeight - dockMargin - 20
|
||||
buttonScreenY = root.screen.height - actualDockHeight - dockMargin - 20;
|
||||
} else {
|
||||
buttonScreenY = actualDockHeight + dockMargin + 20
|
||||
buttonScreenY = actualDockHeight + dockMargin + 20;
|
||||
}
|
||||
|
||||
const dockContentWidth = dockWindow.width
|
||||
const screenWidth = root.screen.width
|
||||
const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2)
|
||||
buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2
|
||||
const dockContentWidth = dockWindow.width;
|
||||
const screenWidth = root.screen.width;
|
||||
const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2);
|
||||
buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2;
|
||||
}
|
||||
|
||||
anchorPos = Qt.point(buttonScreenX, buttonScreenY)
|
||||
anchorPos = Qt.point(buttonScreenX, buttonScreenY);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menuContainer
|
||||
|
||||
x: {
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isVertical) {
|
||||
const isDockAtRight = SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isDockAtRight = SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isDockAtRight) {
|
||||
return Math.max(10, root.anchorPos.x - width + 30)
|
||||
return Math.max(10, root.anchorPos.x - width + 30);
|
||||
} else {
|
||||
return Math.min(root.width - width - 10, root.anchorPos.x - 30)
|
||||
return Math.min(root.width - width - 10, root.anchorPos.x - 30);
|
||||
}
|
||||
} else {
|
||||
const left = 10
|
||||
const right = root.width - width - 10
|
||||
const want = root.anchorPos.x - width / 2
|
||||
return Math.max(left, Math.min(right, want))
|
||||
const left = 10;
|
||||
const right = root.width - width - 10;
|
||||
const want = root.anchorPos.x - width / 2;
|
||||
return Math.max(left, Math.min(right, want));
|
||||
}
|
||||
}
|
||||
y: {
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right;
|
||||
if (isVertical) {
|
||||
const top = 10
|
||||
const bottom = root.height - height - 10
|
||||
const want = root.anchorPos.y - height / 2
|
||||
return Math.max(top, Math.min(bottom, want))
|
||||
const top = 10;
|
||||
const bottom = root.height - height - 10;
|
||||
const want = root.anchorPos.y - height / 2;
|
||||
return Math.max(top, Math.min(bottom, want));
|
||||
} else {
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
|
||||
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom;
|
||||
if (isDockAtBottom) {
|
||||
return Math.max(10, root.anchorPos.y - height + 30)
|
||||
return Math.max(10, root.anchorPos.y - height + 30);
|
||||
} else {
|
||||
return Math.min(root.height - height - 10, root.anchorPos.y - 30)
|
||||
return Math.min(root.height - height - 10, root.anchorPos.y - 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,17 +203,18 @@ PanelWindow {
|
||||
// Window list for grouped apps
|
||||
Repeater {
|
||||
model: {
|
||||
if (!root.appData || root.appData.type !== "grouped") return []
|
||||
if (!root.appData || root.appData.type !== "grouped")
|
||||
return [];
|
||||
|
||||
const toplevels = []
|
||||
const allToplevels = ToplevelManager.toplevels.values
|
||||
const toplevels = [];
|
||||
const allToplevels = ToplevelManager.toplevels.values;
|
||||
for (let i = 0; i < allToplevels.length; i++) {
|
||||
const toplevel = allToplevels[i]
|
||||
const toplevel = allToplevels[i];
|
||||
if (toplevel.appId === root.appData.appId) {
|
||||
toplevels.push(toplevel)
|
||||
toplevels.push(toplevel);
|
||||
}
|
||||
}
|
||||
return toplevels
|
||||
return toplevels;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -227,7 +229,7 @@ PanelWindow {
|
||||
anchors.right: closeButton.left
|
||||
anchors.rightMargin: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: (modelData && modelData.title) ? modelData.title: I18n.tr("(Unnamed)")
|
||||
text: (modelData && modelData.title) ? modelData.title : I18n.tr("(Unnamed)")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
@@ -259,9 +261,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.close) {
|
||||
modelData.close()
|
||||
modelData.close();
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,9 +276,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData && modelData.activate) {
|
||||
modelData.activate()
|
||||
modelData.activate();
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,9 +286,11 @@ PanelWindow {
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.appData) return false
|
||||
if (root.appData.type !== "grouped") return false
|
||||
return root.appData.windowCount > 0
|
||||
if (!root.appData)
|
||||
return false;
|
||||
if (root.appData.type !== "grouped")
|
||||
return false;
|
||||
return root.appData.windowCount > 0;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -343,9 +347,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData) {
|
||||
SessionService.launchDesktopAction(root.desktopEntry, modelData)
|
||||
SessionService.launchDesktopAction(root.desktopEntry, modelData);
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,9 +358,9 @@ PanelWindow {
|
||||
Rectangle {
|
||||
visible: {
|
||||
if (!root.desktopEntry?.actions || root.desktopEntry.actions.length === 0) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand)
|
||||
return !root.hidePin || (!root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand);
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -390,26 +394,26 @@ PanelWindow {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (!root.appData) {
|
||||
return
|
||||
}
|
||||
if (!root.appData)
|
||||
return;
|
||||
|
||||
if (root.appData.isPinned) {
|
||||
SessionData.removePinnedApp(root.appData.appId)
|
||||
SessionData.removePinnedApp(root.appData.appId);
|
||||
} else {
|
||||
SessionData.addPinnedApp(root.appData.appId)
|
||||
SessionData.addPinnedApp(root.appData.appId);
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: {
|
||||
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand
|
||||
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0))
|
||||
const hasPinOption = !root.hidePin
|
||||
const hasContentAbove = hasPinOption || hasNvidia
|
||||
return hasContentAbove && hasWindow
|
||||
const hasNvidia = !root.isDmsWindow && root.desktopEntry && SessionService.nvidiaCommand;
|
||||
const hasWindow = root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0));
|
||||
const hasPinOption = !root.hidePin;
|
||||
const hasContentAbove = hasPinOption || hasNvidia;
|
||||
return hasContentAbove && hasWindow;
|
||||
}
|
||||
width: parent.width
|
||||
height: 1
|
||||
@@ -444,9 +448,9 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.desktopEntry) {
|
||||
SessionService.launchDesktopEntry(root.desktopEntry, true)
|
||||
SessionService.launchDesktopEntry(root.desktopEntry, true);
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,9 +470,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (root.appData && root.appData.type === "grouped") {
|
||||
return "Close All Windows"
|
||||
return "Close All Windows";
|
||||
}
|
||||
return "Close Window"
|
||||
return "Close Window";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
@@ -484,11 +488,11 @@ PanelWindow {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.appData?.type === "window") {
|
||||
root.appData?.toplevel?.close()
|
||||
root.appData?.toplevel?.close();
|
||||
} else if (root.appData?.type === "grouped") {
|
||||
root.appData?.allWindows?.forEach(window => window.toplevel?.close())
|
||||
root.appData?.allWindows?.forEach(window => window.toplevel?.close());
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
preventStealing: true
|
||||
preventStealing: dragging || longPressing
|
||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: mouse => {
|
||||
|
||||
75
quickshell/Modules/Dock/DockOverflowButton.qml
Normal file
75
quickshell/Modules/Dock/DockOverflowButton.qml
Normal file
@@ -0,0 +1,75 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real actualIconSize: 40
|
||||
property int overflowCount: 0
|
||||
property bool overflowExpanded: false
|
||||
property bool isVertical: false
|
||||
|
||||
signal clicked
|
||||
|
||||
Rectangle {
|
||||
id: buttonBackground
|
||||
anchors.centerIn: parent
|
||||
width: actualIconSize
|
||||
height: actualIconSize
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, mouseArea.containsMouse ? 0.2 : 0.1)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: arrowIcon
|
||||
anchors.centerIn: parent
|
||||
size: actualIconSize * 0.6
|
||||
name: "expand_more"
|
||||
color: Theme.surfaceText
|
||||
rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90)
|
||||
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: overflowCount > 0 && !overflowExpanded && SettingsData.dockShowOverflowBadge
|
||||
anchors.right: buttonBackground.right
|
||||
anchors.top: buttonBackground.top
|
||||
anchors.rightMargin: -4
|
||||
anchors.topMargin: -4
|
||||
width: Math.max(18, badgeText.width + 8)
|
||||
height: 18
|
||||
radius: 9
|
||||
color: Theme.primary
|
||||
z: 10
|
||||
|
||||
StyledText {
|
||||
id: badgeText
|
||||
anchors.centerIn: parent
|
||||
text: `+${overflowCount}`
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Bold
|
||||
color: Theme.onPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ Singleton {
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property bool use24HourClock: true
|
||||
property bool showSeconds: false
|
||||
property bool padHours12Hour: false
|
||||
property bool useFahrenheit: false
|
||||
property bool nightModeEnabled: false
|
||||
property string weatherLocation: "New York, NY"
|
||||
@@ -39,6 +40,7 @@ Singleton {
|
||||
property string widgetBackgroundColor: "sch"
|
||||
property string lockDateFormat: ""
|
||||
property bool lockScreenShowPowerActions: true
|
||||
property bool lockScreenShowProfileImage: true
|
||||
property var screenPreferences: ({})
|
||||
property int animationSpeed: 2
|
||||
property string wallpaperFillMode: "Fill"
|
||||
@@ -52,6 +54,7 @@ Singleton {
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot";
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false;
|
||||
padHours12Hour = settings.padHours12Hour !== undefined ? settings.padHours12Hour : false;
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
||||
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY";
|
||||
@@ -71,6 +74,7 @@ Singleton {
|
||||
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch";
|
||||
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
|
||||
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
|
||||
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
|
||||
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
|
||||
@@ -88,6 +92,14 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function getEffectiveTimeFormat() {
|
||||
if (use24HourClock)
|
||||
return showSeconds ? "hh:mm:ss" : "hh:mm";
|
||||
if (padHours12Hour)
|
||||
return showSeconds ? "hh:mm:ss AP" : "hh:mm AP";
|
||||
return showSeconds ? "h:mm:ss AP" : "h:mm AP";
|
||||
}
|
||||
|
||||
function getEffectiveLockDateFormat() {
|
||||
return lockDateFormat && lockDateFormat.length > 0 ? lockDateFormat : Locale.LongFormat;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ Item {
|
||||
|
||||
readonly property string xdgDataDirs: Quickshell.env("XDG_DATA_DIRS")
|
||||
property string screenName: ""
|
||||
property string randomFact: ""
|
||||
property string hyprlandCurrentLayout: ""
|
||||
property string hyprlandKeyboard: ""
|
||||
property int hyprlandLayoutCount: 0
|
||||
@@ -31,10 +30,6 @@ Item {
|
||||
|
||||
signal launchRequested
|
||||
|
||||
function pickRandomFact() {
|
||||
randomFact = Facts.getRandomFact();
|
||||
}
|
||||
|
||||
property bool weatherInitialized: false
|
||||
|
||||
function initWeatherService() {
|
||||
@@ -58,7 +53,6 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
pickRandomFact();
|
||||
initWeatherService();
|
||||
|
||||
if (isPrimaryScreen)
|
||||
@@ -223,7 +217,7 @@ Item {
|
||||
spacing: 0
|
||||
|
||||
property string fullTimeStr: {
|
||||
const format = GreetdSettings.use24HourClock ? (GreetdSettings.showSeconds ? "HH:mm:ss" : "HH:mm") : (GreetdSettings.showSeconds ? "h:mm:ss AP" : "h:mm AP");
|
||||
const format = GreetdSettings.getEffectiveTimeFormat();
|
||||
return systemClock.date.toLocaleTimeString(Qt.locale(), format);
|
||||
}
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
@@ -369,6 +363,7 @@ Item {
|
||||
return PortalService.profileImage;
|
||||
}
|
||||
fallbackIcon: "person"
|
||||
visible: GreetdSettings.lockScreenShowProfileImage
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -961,20 +956,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Theme.spacingL
|
||||
width: Math.min(parent.width - Theme.spacingXL * 2, implicitWidth)
|
||||
text: root.randomFact
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: "white"
|
||||
opacity: 0.8
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.NoWrap
|
||||
visible: root.randomFact !== ""
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -65,7 +65,7 @@ Scope {
|
||||
lockInitiatedLocally = true;
|
||||
lockPowerOffArmed = SettingsData.lockScreenPowerOffMonitorsOnLock;
|
||||
|
||||
if (!SessionService.active && SessionService.loginctlAvailable) {
|
||||
if (!SessionService.active && SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration) {
|
||||
pendingLock = true;
|
||||
notifyLoginctl(true);
|
||||
return;
|
||||
@@ -99,7 +99,7 @@ Scope {
|
||||
function onSessionLocked() {
|
||||
if (shouldLock || pendingLock)
|
||||
return;
|
||||
if (!SessionService.active && SessionService.loginctlAvailable) {
|
||||
if (!SessionService.active && SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration) {
|
||||
pendingLock = true;
|
||||
lockInitiatedLocally = false;
|
||||
return;
|
||||
|
||||
@@ -25,7 +25,6 @@ Item {
|
||||
property string screenName: ""
|
||||
property bool unlocking: false
|
||||
property string pamState: ""
|
||||
property string randomFact: ""
|
||||
property string hyprlandCurrentLayout: ""
|
||||
property string hyprlandKeyboard: ""
|
||||
property int hyprlandLayoutCount: 0
|
||||
@@ -41,15 +40,7 @@ Item {
|
||||
pamState = "";
|
||||
}
|
||||
|
||||
function pickRandomFact() {
|
||||
randomFact = Facts.getRandomFact();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (demoMode) {
|
||||
pickRandomFact();
|
||||
}
|
||||
|
||||
WeatherService.addRef();
|
||||
UserInfoService.getUserInfo();
|
||||
|
||||
@@ -61,11 +52,6 @@ Item {
|
||||
lockerReadyArmed = true;
|
||||
}
|
||||
|
||||
onDemoModeChanged: {
|
||||
if (demoMode) {
|
||||
pickRandomFact();
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
WeatherService.removeRef();
|
||||
if (CompositorService.isHyprland) {
|
||||
@@ -1195,12 +1181,12 @@ Item {
|
||||
height: 24
|
||||
color: Qt.rgba(255, 255, 255, 0.2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: MprisController.activePlayer
|
||||
visible: MprisController.activePlayer && SettingsData.lockScreenShowMediaPlayer
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
visible: MprisController.activePlayer
|
||||
visible: MprisController.activePlayer && SettingsData.lockScreenShowMediaPlayer
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Item {
|
||||
@@ -1369,7 +1355,7 @@ Item {
|
||||
height: 24
|
||||
color: Qt.rgba(255, 255, 255, 0.2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: MprisController.activePlayer && WeatherService.weather.available
|
||||
visible: MprisController.activePlayer && SettingsData.lockScreenShowMediaPlayer && WeatherService.weather.available
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -1606,20 +1592,6 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Theme.spacingL
|
||||
width: Math.min(parent.width - Theme.spacingXL * 2, implicitWidth)
|
||||
text: root.randomFact
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: "white"
|
||||
opacity: 0.8
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.NoWrap
|
||||
visible: root.randomFact !== ""
|
||||
}
|
||||
}
|
||||
|
||||
Pam {
|
||||
|
||||
@@ -42,8 +42,6 @@ DankPopout {
|
||||
if (!shouldBeVisible) {
|
||||
searchText = "";
|
||||
expandedPid = "";
|
||||
if (processesView)
|
||||
processesView.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +106,11 @@ DankPopout {
|
||||
Connections {
|
||||
target: processListPopout
|
||||
function onShouldBeVisibleChanged() {
|
||||
if (processListPopout.shouldBeVisible)
|
||||
if (processListPopout.shouldBeVisible) {
|
||||
Qt.callLater(() => processListContent.forceActiveFocus());
|
||||
} else {
|
||||
processesView.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,17 @@ Column {
|
||||
onToggled: checked => root.updateConfig("showAnalogSeconds", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.style === "digital" || cfg.style === "stacked"
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.style === "digital" || cfg.style === "stacked"
|
||||
text: I18n.tr("Show Seconds")
|
||||
checked: cfg.showDigitalSeconds ?? false
|
||||
onToggled: checked => root.updateConfig("showDigitalSeconds", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
|
||||
@@ -162,6 +162,39 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
settingKey: "dockMaxVisibleApps"
|
||||
tags: ["dock", "overflow", "max", "apps", "limit"]
|
||||
text: I18n.tr("Max Pinned Apps (0 = Unlimited)")
|
||||
minimum: 0
|
||||
maximum: 30
|
||||
value: SettingsData.dockMaxVisibleApps
|
||||
defaultValue: 0
|
||||
unit: ""
|
||||
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleApps", newValue)
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
settingKey: "dockMaxVisibleRunningApps"
|
||||
tags: ["dock", "overflow", "max", "running", "apps", "limit"]
|
||||
text: I18n.tr("Max Running Apps (0 = Unlimited)")
|
||||
minimum: 0
|
||||
maximum: 30
|
||||
value: SettingsData.dockMaxVisibleRunningApps
|
||||
defaultValue: 0
|
||||
unit: ""
|
||||
onSliderValueChanged: newValue => SettingsData.set("dockMaxVisibleRunningApps", newValue)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "dockShowOverflowBadge"
|
||||
tags: ["dock", "overflow", "badge", "count", "indicator"]
|
||||
text: I18n.tr("Show Overflow Badge Count")
|
||||
description: I18n.tr("Displays count when overflow is active")
|
||||
checked: SettingsData.dockShowOverflowBadge
|
||||
onToggled: checked => SettingsData.set("dockShowOverflowBadge", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
@@ -174,7 +207,6 @@ Item {
|
||||
settingKey: "dockLauncherEnabled"
|
||||
tags: ["dock", "launcher", "button", "apps"]
|
||||
text: I18n.tr("Show Launcher Button")
|
||||
description: I18n.tr("Add a draggable launcher button to the dock")
|
||||
checked: SettingsData.dockLauncherEnabled
|
||||
onToggled: checked => SettingsData.set("dockLauncherEnabled", checked)
|
||||
}
|
||||
@@ -184,20 +216,12 @@ Item {
|
||||
spacing: Theme.spacingL
|
||||
visible: SettingsData.dockLauncherEnabled
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Long press and drag the launcher button to reposition it in the dock")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Launcher Icon")
|
||||
text: I18n.tr("Icon")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
|
||||
@@ -722,7 +722,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
text: "DMS"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
color: Theme.primaryText
|
||||
color: Theme.primary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,14 @@ Item {
|
||||
onToggled: checked => SettingsData.set("lockScreenShowPasswordField", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "lockScreenShowMediaPlayer"
|
||||
tags: ["lock", "screen", "media", "player", "music", "mpris"]
|
||||
text: I18n.tr("Show Media Player", "Enable media player controls on the lock screen window")
|
||||
checked: SettingsData.lockScreenShowMediaPlayer
|
||||
onToggled: checked => SettingsData.set("lockScreenShowMediaPlayer", checked)
|
||||
}
|
||||
|
||||
SettingsDropdownRow {
|
||||
settingKey: "lockScreenNotificationMode"
|
||||
tags: ["lock", "screen", "notification", "notifications", "privacy"]
|
||||
|
||||
@@ -125,6 +125,19 @@ Item {
|
||||
return Theme.warning;
|
||||
}
|
||||
|
||||
function formatThemeAutoTime(isoString) {
|
||||
if (!isoString)
|
||||
return "";
|
||||
try {
|
||||
const date = new Date(isoString);
|
||||
if (isNaN(date.getTime()))
|
||||
return "";
|
||||
return date.toLocaleTimeString(Qt.locale(), "HH:mm");
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
SettingsData.detectAvailableIconThemes();
|
||||
SettingsData.detectAvailableCursorThemes();
|
||||
@@ -151,9 +164,7 @@ Item {
|
||||
detection[item.id] = item.detected;
|
||||
}
|
||||
themeColorsTab.templateDetection = detection;
|
||||
} catch (e) {
|
||||
console.warn("ThemeColorsTab: Failed to parse template check:", e);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -962,6 +973,439 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "theme"
|
||||
tags: ["automatic", "color", "mode", "schedule", "sunrise", "sunset"]
|
||||
title: I18n.tr("Automatic Color Mode")
|
||||
settingKey: "automaticColorMode"
|
||||
iconName: "schedule"
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
id: themeModeAutoToggle
|
||||
width: parent.width
|
||||
text: I18n.tr("Automatic Control")
|
||||
checked: SessionData.themeModeAutoEnabled
|
||||
onToggled: checked => {
|
||||
SessionData.setThemeModeAutoEnabled(checked);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onThemeModeAutoEnabledChanged() {
|
||||
themeModeAutoToggle.checked = SessionData.themeModeAutoEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: SessionData.themeModeAutoEnabled
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("Share Gamma Control Settings")
|
||||
checked: SessionData.themeModeShareGammaSettings
|
||||
onToggled: checked => {
|
||||
SessionData.setThemeModeShareGammaSettings(checked);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 45 + Theme.spacingM
|
||||
|
||||
DankTabBar {
|
||||
id: themeModeTabBar
|
||||
width: 200
|
||||
height: 45
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
model: [
|
||||
{
|
||||
"text": "Time",
|
||||
"icon": "access_time"
|
||||
},
|
||||
{
|
||||
"text": "Location",
|
||||
"icon": "place"
|
||||
}
|
||||
]
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = SessionData.themeModeAutoMode === "location" ? 1 : 0;
|
||||
Qt.callLater(updateIndicator);
|
||||
}
|
||||
|
||||
onTabClicked: index => {
|
||||
SessionData.setThemeModeAutoMode(index === 1 ? "location" : "time");
|
||||
currentIndex = index;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onThemeModeAutoModeChanged() {
|
||||
themeModeTabBar.currentIndex = SessionData.themeModeAutoMode === "location" ? 1 : 0;
|
||||
Qt.callLater(themeModeTabBar.updateIndicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: SessionData.themeModeAutoMode === "time" && !SessionData.themeModeShareGammaSettings
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: ""
|
||||
width: 50
|
||||
height: 20
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Hour")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: 70
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Minute")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: 70
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Start")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
width: 50
|
||||
height: 40
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
dropdownWidth: 70
|
||||
currentValue: SessionData.themeModeStartHour.toString()
|
||||
options: {
|
||||
var hours = [];
|
||||
for (var i = 0; i < 24; i++)
|
||||
hours.push(i.toString());
|
||||
return hours;
|
||||
}
|
||||
onValueChanged: value => {
|
||||
SessionData.setThemeModeStartHour(parseInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
dropdownWidth: 70
|
||||
currentValue: SessionData.themeModeStartMinute.toString().padStart(2, '0')
|
||||
options: {
|
||||
var minutes = [];
|
||||
for (var i = 0; i < 60; i += 5) {
|
||||
minutes.push(i.toString().padStart(2, '0'));
|
||||
}
|
||||
return minutes;
|
||||
}
|
||||
onValueChanged: value => {
|
||||
SessionData.setThemeModeStartMinute(parseInt(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("End")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
width: 50
|
||||
height: 40
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
dropdownWidth: 70
|
||||
currentValue: SessionData.themeModeEndHour.toString()
|
||||
options: {
|
||||
var hours = [];
|
||||
for (var i = 0; i < 24; i++)
|
||||
hours.push(i.toString());
|
||||
return hours;
|
||||
}
|
||||
onValueChanged: value => {
|
||||
SessionData.setThemeModeEndHour(parseInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
dropdownWidth: 70
|
||||
currentValue: SessionData.themeModeEndMinute.toString().padStart(2, '0')
|
||||
options: {
|
||||
var minutes = [];
|
||||
for (var i = 0; i < 60; i += 5) {
|
||||
minutes.push(i.toString().padStart(2, '0'));
|
||||
}
|
||||
return minutes;
|
||||
}
|
||||
onValueChanged: value => {
|
||||
SessionData.setThemeModeEndMinute(parseInt(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: SessionData.themeModeAutoMode === "location" && !SessionData.themeModeShareGammaSettings
|
||||
|
||||
DankToggle {
|
||||
id: themeModeIpLocationToggle
|
||||
width: parent.width
|
||||
text: I18n.tr("Use IP Location")
|
||||
checked: SessionData.nightModeUseIPLocation || false
|
||||
onToggled: checked => {
|
||||
SessionData.setNightModeUseIPLocation(checked);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
function onNightModeUseIPLocationChanged() {
|
||||
themeModeIpLocationToggle.checked = SessionData.nightModeUseIPLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: !SessionData.nightModeUseIPLocation
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Manual Coordinates")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingL
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Latitude")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: 120
|
||||
height: 40
|
||||
text: SessionData.latitude.toString()
|
||||
placeholderText: "0.0"
|
||||
onEditingFinished: {
|
||||
const lat = parseFloat(text);
|
||||
if (!isNaN(lat) && lat >= -90 && lat <= 90 && lat !== SessionData.latitude) {
|
||||
SessionData.setLatitude(lat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Longitude")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: 120
|
||||
height: 40
|
||||
text: SessionData.longitude.toString()
|
||||
placeholderText: "0.0"
|
||||
onEditingFinished: {
|
||||
const lon = parseFloat(text);
|
||||
if (!isNaN(lon) && lon >= -180 && lon <= 180 && lon !== SessionData.longitude) {
|
||||
SessionData.setLongitude(lon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Uses sunrise/sunset times based on your location.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Using shared settings from Gamma Control")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: SessionData.themeModeShareGammaSettings
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: statusRow.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
|
||||
Row {
|
||||
id: statusRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingL
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: (parent.width - Theme.spacingL * 2) / 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Rectangle {
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
color: SessionData.themeModeAutoEnabled ? Theme.success : Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Automation")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: SessionData.themeModeAutoEnabled ? I18n.tr("Enabled") : I18n.tr("Disabled")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: (parent.width - Theme.spacingL * 2) / 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: SessionData.isLightMode ? "light_mode" : "dark_mode"
|
||||
size: Theme.iconSize
|
||||
color: SessionData.isLightMode ? "#FFA726" : "#7E57C2"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: SessionData.isLightMode ? I18n.tr("Light Mode") : I18n.tr("Dark Mode")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Active")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: (parent.width - Theme.spacingL * 2) / 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: SessionData.themeModeAutoEnabled && SessionData.themeModeNextTransition
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "schedule"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Next Transition")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: themeColorsTab.formatThemeAutoTime(SessionData.themeModeNextTransition)
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "theme"
|
||||
tags: ["light", "dark", "mode", "appearance"]
|
||||
@@ -1040,6 +1484,42 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDropdownRow {
|
||||
tab: "theme"
|
||||
tags: ["control", "center", "tile", "button", "color", "active"]
|
||||
settingKey: "controlCenterTileColorMode"
|
||||
text: I18n.tr("Control Center Tile Color")
|
||||
description: I18n.tr("Active tile background and icon color")
|
||||
options: ["Primary", "Primary Container", "Secondary", "Surface Variant"]
|
||||
currentValue: {
|
||||
switch (SettingsData.controlCenterTileColorMode) {
|
||||
case "primaryContainer":
|
||||
return "Primary Container";
|
||||
case "secondary":
|
||||
return "Secondary";
|
||||
case "surfaceVariant":
|
||||
return "Surface Variant";
|
||||
default:
|
||||
return "Primary";
|
||||
}
|
||||
}
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case "Primary Container":
|
||||
SettingsData.set("controlCenterTileColorMode", "primaryContainer");
|
||||
return;
|
||||
case "Secondary":
|
||||
SettingsData.set("controlCenterTileColorMode", "secondary");
|
||||
return;
|
||||
case "Surface Variant":
|
||||
SettingsData.set("controlCenterTileColorMode", "surfaceVariant");
|
||||
return;
|
||||
default:
|
||||
SettingsData.set("controlCenterTileColorMode", "primary");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
tab: "theme"
|
||||
tags: ["popup", "transparency", "opacity", "modal"]
|
||||
|
||||
@@ -49,6 +49,17 @@ Item {
|
||||
checked: SettingsData.showSeconds
|
||||
onToggled: checked => SettingsData.set("showSeconds", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
tab: "time"
|
||||
tags: ["time", "12hour", "format", "padding", "leading", "zero"]
|
||||
settingKey: "padHours12Hour"
|
||||
text: I18n.tr("Pad Hours")
|
||||
description: "02:31 PM vs 2:31 PM"
|
||||
checked: SettingsData.padHours12Hour
|
||||
onToggled: checked => SettingsData.set("padHours12Hour", checked)
|
||||
visible: !SettingsData.use24HourClock
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
|
||||
@@ -68,6 +68,13 @@ Item {
|
||||
"icon": "apps",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "appsDock",
|
||||
"text": I18n.tr("Apps Dock"),
|
||||
"description": I18n.tr("Pinned and running apps with drag-and-drop"),
|
||||
"icon": "dock_to_bottom",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "clock",
|
||||
"text": I18n.tr("Clock"),
|
||||
|
||||
@@ -373,81 +373,42 @@ Column {
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: musicMenuButton
|
||||
visible: modelData.id === "music"
|
||||
buttonSize: 32
|
||||
iconName: "more_vert"
|
||||
iconSize: 18
|
||||
iconColor: Theme.outline
|
||||
onClicked: {
|
||||
musicContextMenu.widgetData = modelData;
|
||||
musicContextMenu.sectionId = root.sectionId;
|
||||
musicContextMenu.widgetIndex = index;
|
||||
|
||||
var buttonPos = musicMenuButton.mapToItem(root, 0, 0);
|
||||
var popupWidth = musicContextMenu.width;
|
||||
var popupHeight = musicContextMenu.height;
|
||||
|
||||
var xPos = buttonPos.x - popupWidth - Theme.spacingS;
|
||||
if (xPos < 0)
|
||||
xPos = buttonPos.x + musicMenuButton.width + Theme.spacingS;
|
||||
|
||||
var yPos = buttonPos.y - popupHeight / 2 + musicMenuButton.height / 2;
|
||||
if (yPos < 0) {
|
||||
yPos = Theme.spacingS;
|
||||
} else if (yPos + popupHeight > root.height) {
|
||||
yPos = root.height - popupHeight - Theme.spacingS;
|
||||
}
|
||||
|
||||
musicContextMenu.x = xPos;
|
||||
musicContextMenu.y = yPos;
|
||||
musicContextMenu.open();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
visible: modelData.id === "clock" || modelData.id === "music" || modelData.id === "focusedWindow" || modelData.id === "runningApps" || modelData.id === "keyboard_layout_name"
|
||||
|
||||
DankActionButton {
|
||||
id: smallSizeButton
|
||||
buttonSize: 28
|
||||
visible: modelData.id === "music"
|
||||
iconName: "photo_size_select_small"
|
||||
iconSize: 16
|
||||
iconColor: (modelData.mediaSize !== undefined ? modelData.mediaSize : SettingsData.mediaSize) === 0 ? Theme.primary : Theme.outline
|
||||
onClicked: {
|
||||
root.compactModeChanged("music", 0);
|
||||
}
|
||||
onEntered: {
|
||||
sharedTooltip.show("Small", smallSizeButton, 0, 0, "bottom");
|
||||
}
|
||||
onExited: {
|
||||
sharedTooltip.hide();
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: mediumSizeButton
|
||||
buttonSize: 28
|
||||
visible: modelData.id === "music"
|
||||
iconName: "photo_size_select_actual"
|
||||
iconSize: 16
|
||||
iconColor: (modelData.mediaSize !== undefined ? modelData.mediaSize : SettingsData.mediaSize) === 1 ? Theme.primary : Theme.outline
|
||||
onClicked: {
|
||||
root.compactModeChanged("music", 1);
|
||||
}
|
||||
onEntered: {
|
||||
sharedTooltip.show("Medium", mediumSizeButton, 0, 0, "bottom");
|
||||
}
|
||||
onExited: {
|
||||
sharedTooltip.hide();
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: largeSizeButton
|
||||
buttonSize: 28
|
||||
visible: modelData.id === "music"
|
||||
iconName: "photo_size_select_large"
|
||||
iconSize: 16
|
||||
iconColor: (modelData.mediaSize !== undefined ? modelData.mediaSize : SettingsData.mediaSize) === 2 ? Theme.primary : Theme.outline
|
||||
onClicked: {
|
||||
root.compactModeChanged("music", 2);
|
||||
}
|
||||
onEntered: {
|
||||
sharedTooltip.show("Large", largeSizeButton, 0, 0, "bottom");
|
||||
}
|
||||
onExited: {
|
||||
sharedTooltip.hide();
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: largerSizeButton
|
||||
buttonSize: 28
|
||||
visible: modelData.id === "music"
|
||||
iconName: "fit_screen"
|
||||
iconSize: 16
|
||||
iconColor: (modelData.mediaSize !== undefined ? modelData.mediaSize : SettingsData.mediaSize) === 3 ? Theme.primary : Theme.outline
|
||||
onClicked: {
|
||||
root.compactModeChanged("music", 3);
|
||||
}
|
||||
onEntered: {
|
||||
sharedTooltip.show("Largest", largerSizeButton, 0, 0, "bottom");
|
||||
}
|
||||
onExited: {
|
||||
sharedTooltip.hide();
|
||||
}
|
||||
}
|
||||
visible: modelData.id === "clock" || modelData.id === "focusedWindow" || modelData.id === "runningApps" || modelData.id === "keyboard_layout_name"
|
||||
|
||||
DankActionButton {
|
||||
id: compactModeButton
|
||||
@@ -1308,4 +1269,119 @@ Column {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: musicContextMenu
|
||||
|
||||
property var widgetData: null
|
||||
property string sectionId: ""
|
||||
property int widgetIndex: -1
|
||||
|
||||
width: 180
|
||||
height: musicMenuColumn.implicitHeight + Theme.spacingS * 2
|
||||
padding: 0
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Column {
|
||||
id: musicMenuColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
icon: "photo_size_select_small",
|
||||
label: I18n.tr("Small"),
|
||||
sizeValue: 0
|
||||
},
|
||||
{
|
||||
icon: "photo_size_select_actual",
|
||||
label: I18n.tr("Medium"),
|
||||
sizeValue: 1
|
||||
},
|
||||
{
|
||||
icon: "photo_size_select_large",
|
||||
label: I18n.tr("Large"),
|
||||
sizeValue: 2
|
||||
},
|
||||
{
|
||||
icon: "fit_screen",
|
||||
label: I18n.tr("Largest"),
|
||||
sizeValue: 3
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
function isSelected() {
|
||||
var wd = musicContextMenu.widgetData;
|
||||
var currentSize = wd?.mediaSize ?? SettingsData.mediaSize;
|
||||
return currentSize === modelData.sizeValue;
|
||||
}
|
||||
|
||||
width: musicMenuColumn.width
|
||||
height: Math.max(18, Theme.fontSizeSmall) + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: musicOptionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: modelData.icon
|
||||
size: 18
|
||||
color: isSelected() ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.label
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: isSelected() ? Font.Medium : Font.Normal
|
||||
color: isSelected() ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: "check"
|
||||
size: 16
|
||||
color: Theme.primary
|
||||
visible: isSelected()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: musicOptionArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.compactModeChanged("music", modelData.sizeValue);
|
||||
musicContextMenu.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,17 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSliderRow {
|
||||
visible: SettingsData.showWorkspaceApps
|
||||
text: I18n.tr("Icon Size")
|
||||
value: SettingsData.workspaceAppIconSizeOffset
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
unit: "px"
|
||||
defaultValue: 0
|
||||
onSliderValueChanged: newValue => SettingsData.set("workspaceAppIconSizeOffset", newValue)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "groupWorkspaceApps"
|
||||
tags: ["workspace", "apps", "icons", "group", "grouped", "collapse"]
|
||||
|
||||
@@ -219,7 +219,8 @@ Singleton {
|
||||
action: plugin.action,
|
||||
categories: plugin.categories,
|
||||
isCore: true,
|
||||
builtInPluginId: pluginId
|
||||
builtInPluginId: pluginId,
|
||||
cornerIcon: plugin.cornerIcon
|
||||
});
|
||||
}
|
||||
return apps;
|
||||
@@ -854,6 +855,21 @@ Singleton {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPluginPasteText(pluginId, item) {
|
||||
if (typeof PluginService === "undefined")
|
||||
return null;
|
||||
|
||||
const instance = PluginService.pluginInstances[pluginId];
|
||||
if (!instance)
|
||||
return null;
|
||||
|
||||
if (typeof instance.getPasteText === "function") {
|
||||
return instance.getPasteText(item);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function searchPluginItems(query) {
|
||||
if (typeof PluginService === "undefined")
|
||||
return [];
|
||||
@@ -868,4 +884,52 @@ Singleton {
|
||||
|
||||
return allItems;
|
||||
}
|
||||
|
||||
function getPluginLauncherCategories(pluginId) {
|
||||
if (typeof PluginService === "undefined")
|
||||
return [];
|
||||
|
||||
const instance = PluginService.pluginInstances[pluginId];
|
||||
if (!instance)
|
||||
return [];
|
||||
|
||||
if (typeof instance.getCategories !== "function")
|
||||
return [];
|
||||
|
||||
try {
|
||||
return instance.getCategories() || [];
|
||||
} catch (e) {
|
||||
console.warn("AppSearchService: Error getting categories from plugin", pluginId, ":", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function setPluginLauncherCategory(pluginId, categoryId) {
|
||||
if (typeof PluginService === "undefined")
|
||||
return;
|
||||
|
||||
const instance = PluginService.pluginInstances[pluginId];
|
||||
if (!instance)
|
||||
return;
|
||||
|
||||
if (typeof instance.setCategory !== "function")
|
||||
return;
|
||||
|
||||
try {
|
||||
instance.setCategory(categoryId);
|
||||
} catch (e) {
|
||||
console.warn("AppSearchService: Error setting category on plugin", pluginId, ":", e);
|
||||
}
|
||||
}
|
||||
|
||||
function pluginHasCategories(pluginId) {
|
||||
if (typeof PluginService === "undefined")
|
||||
return false;
|
||||
|
||||
const instance = PluginService.pluginInstances[pluginId];
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
return typeof instance.getCategories === "function";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
root.userPreference = SettingsData.networkPreference;
|
||||
lastConnectedVpnUuid = SettingsData.vpnLastConnected || "";
|
||||
lastConnectedVpnUuid = SessionData.vpnLastConnected || "";
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
checkDMSCapabilities();
|
||||
}
|
||||
@@ -293,7 +293,7 @@ Singleton {
|
||||
|
||||
if (vpnConnected && activeUuid) {
|
||||
lastConnectedVpnUuid = activeUuid;
|
||||
SettingsData.set("vpnLastConnected", activeUuid);
|
||||
SessionData.setVpnLastConnected(activeUuid);
|
||||
}
|
||||
|
||||
if (vpnIsBusy) {
|
||||
|
||||
@@ -56,6 +56,7 @@ Singleton {
|
||||
signal wlrOutputStateUpdate(var data)
|
||||
signal evdevStateUpdate(var data)
|
||||
signal gammaStateUpdate(var data)
|
||||
signal themeAutoStateUpdate(var data)
|
||||
signal openUrlRequested(string url)
|
||||
signal appPickerRequested(var data)
|
||||
signal screensaverStateUpdate(var data)
|
||||
@@ -64,7 +65,7 @@ Singleton {
|
||||
property bool screensaverInhibited: false
|
||||
property var screensaverInhibitors: []
|
||||
|
||||
property var activeSubscriptions: ["network", "network.credentials", "loginctl", "freedesktop", "freedesktop.screensaver", "gamma", "bluetooth", "bluetooth.pairing", "dwl", "brightness", "wlroutput", "evdev", "browser", "dbus"]
|
||||
property var activeSubscriptions: ["network", "network.credentials", "loginctl", "freedesktop", "freedesktop.screensaver", "gamma", "theme.auto", "bluetooth", "bluetooth.pairing", "dwl", "brightness", "wlroutput", "evdev", "browser", "dbus"]
|
||||
|
||||
Component.onCompleted: {
|
||||
if (socketPath && socketPath.length > 0) {
|
||||
@@ -304,7 +305,7 @@ Singleton {
|
||||
excludeServices = [excludeServices];
|
||||
}
|
||||
|
||||
const allServices = ["network", "loginctl", "freedesktop", "gamma", "bluetooth", "cups", "dwl", "brightness", "extworkspace", "browser", "dbus"];
|
||||
const allServices = ["network", "loginctl", "freedesktop", "gamma", "theme.auto", "bluetooth", "cups", "dwl", "brightness", "extworkspace", "browser", "dbus"];
|
||||
const filtered = allServices.filter(s => !excludeServices.includes(s));
|
||||
subscribe(filtered);
|
||||
}
|
||||
@@ -373,6 +374,8 @@ Singleton {
|
||||
evdevStateUpdate(data);
|
||||
} else if (service === "gamma") {
|
||||
gammaStateUpdate(data);
|
||||
} else if (service === "theme.auto") {
|
||||
themeAutoStateUpdate(data);
|
||||
} else if (service === "browser.open_requested") {
|
||||
if (data.target) {
|
||||
if (data.requestType === "url" || !data.requestType) {
|
||||
@@ -737,4 +740,10 @@ Singleton {
|
||||
if (callback) callback(response);
|
||||
});
|
||||
}
|
||||
|
||||
function renameWorkspace(name, callback) {
|
||||
sendRequest("extworkspace.renameWorkspace", {
|
||||
"name": name
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ Singleton {
|
||||
if (transform !== 0)
|
||||
monitorLine += ", transform, " + transform;
|
||||
|
||||
if (output.vrr_supported && output.vrr_enabled)
|
||||
monitorLine += ", vrr, 1";
|
||||
if (output.vrr_supported)
|
||||
monitorLine += ", vrr, " + (output.vrr_enabled ? "1" : "0");
|
||||
|
||||
if (output.mirror && output.mirror.length > 0)
|
||||
monitorLine += ", mirror, " + output.mirror;
|
||||
@@ -308,4 +308,18 @@ decoration {
|
||||
reloadConfig();
|
||||
});
|
||||
}
|
||||
|
||||
function renameWorkspace(newName) {
|
||||
if (!Hyprland.focusedWorkspace)
|
||||
return;
|
||||
const wsId = Hyprland.focusedWorkspace.id;
|
||||
if (!wsId)
|
||||
return;
|
||||
const fullName = wsId + " " + newName;
|
||||
Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("HyprlandService: Failed to rename workspace:", output);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,6 +1422,17 @@ Singleton {
|
||||
return block;
|
||||
}
|
||||
|
||||
function renameWorkspace(name) {
|
||||
return send({
|
||||
"Action": {
|
||||
"SetWorkspaceName": {
|
||||
"name": name,
|
||||
"workspace": null
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function screenshot(): string {
|
||||
if (!CompositorService.isNiri) {
|
||||
|
||||
@@ -592,6 +592,13 @@ Singleton {
|
||||
return SettingsData.getPluginSetting(pluginId, key, defaultValue);
|
||||
}
|
||||
|
||||
function getPluginPath(pluginId) {
|
||||
const plugin = availablePlugins[pluginId];
|
||||
if (!plugin)
|
||||
return "";
|
||||
return plugin.pluginDirectory || "";
|
||||
}
|
||||
|
||||
function saveAllPluginSettings() {
|
||||
SettingsData.savePluginSettings();
|
||||
}
|
||||
|
||||
@@ -416,6 +416,17 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDankLauncherV2WithQuery(query: string) {
|
||||
if (dankLauncherV2Modal) {
|
||||
dankLauncherV2Modal.toggleWithQuery(query);
|
||||
} else if (dankLauncherV2ModalLoader) {
|
||||
_dankLauncherV2PendingQuery = query;
|
||||
_dankLauncherV2WantsOpen = true;
|
||||
_dankLauncherV2WantsToggle = false;
|
||||
dankLauncherV2ModalLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
function _onDankLauncherV2ModalLoaded() {
|
||||
if (_dankLauncherV2WantsOpen) {
|
||||
_dankLauncherV2WantsOpen = false;
|
||||
|
||||
@@ -29,6 +29,7 @@ Singleton {
|
||||
}
|
||||
|
||||
property bool loginctlAvailable: false
|
||||
property bool wtypeAvailable: false
|
||||
property string sessionId: ""
|
||||
property string sessionPath: ""
|
||||
property bool locked: false
|
||||
@@ -59,6 +60,7 @@ Singleton {
|
||||
detectElogindProcess.running = true;
|
||||
detectHibernateProcess.running = true;
|
||||
detectPrimeRunProcess.running = true;
|
||||
detectWtypeProcess.running = true;
|
||||
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable);
|
||||
if (!SettingsData.loginctlLockIntegration) {
|
||||
console.log("SessionService: loginctl lock integration disabled by user");
|
||||
@@ -124,6 +126,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectWtypeProcess
|
||||
running: false
|
||||
command: ["which", "wtype"]
|
||||
onExited: exitCode => {
|
||||
wtypeAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectPrimeRunProcess
|
||||
running: false
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Image {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string imagePath: ""
|
||||
property int maxCacheSize: 512
|
||||
property int status: isAnimated ? animatedImg.status : staticImg.status
|
||||
property int fillMode: Image.PreserveAspectCrop
|
||||
|
||||
readonly property bool isRemoteUrl: imagePath.startsWith("http://") || imagePath.startsWith("https://")
|
||||
readonly property bool isAnimated: {
|
||||
if (!imagePath)
|
||||
return false;
|
||||
const lower = imagePath.toLowerCase();
|
||||
return lower.endsWith(".gif") || lower.endsWith(".webp");
|
||||
}
|
||||
readonly property string normalizedPath: {
|
||||
if (!imagePath)
|
||||
return "";
|
||||
@@ -30,7 +38,7 @@ Image {
|
||||
}
|
||||
|
||||
readonly property string imageHash: normalizedPath ? djb2Hash(normalizedPath) : ""
|
||||
readonly property string cachePath: imageHash && !isRemoteUrl ? `${Paths.stringify(Paths.imagecache)}/${imageHash}@${maxCacheSize}x${maxCacheSize}.png` : ""
|
||||
readonly property string cachePath: imageHash && !isRemoteUrl && !isAnimated ? `${Paths.stringify(Paths.imagecache)}/${imageHash}@${maxCacheSize}x${maxCacheSize}.png` : ""
|
||||
readonly property string encodedImagePath: {
|
||||
if (!normalizedPath)
|
||||
return "";
|
||||
@@ -39,39 +47,56 @@ Image {
|
||||
return "file://" + normalizedPath.split('/').map(s => encodeURIComponent(s)).join('/');
|
||||
}
|
||||
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
sourceSize.width: maxCacheSize
|
||||
sourceSize.height: maxCacheSize
|
||||
smooth: true
|
||||
AnimatedImage {
|
||||
id: animatedImg
|
||||
anchors.fill: parent
|
||||
visible: root.isAnimated
|
||||
asynchronous: true
|
||||
fillMode: root.fillMode
|
||||
source: root.isAnimated ? root.imagePath : ""
|
||||
playing: visible && status === AnimatedImage.Ready
|
||||
}
|
||||
|
||||
Image {
|
||||
id: staticImg
|
||||
anchors.fill: parent
|
||||
visible: !root.isAnimated
|
||||
asynchronous: true
|
||||
fillMode: root.fillMode
|
||||
sourceSize.width: root.maxCacheSize
|
||||
sourceSize.height: root.maxCacheSize
|
||||
smooth: true
|
||||
|
||||
onStatusChanged: {
|
||||
if (source == root.cachePath && status === Image.Error) {
|
||||
source = root.encodedImagePath;
|
||||
return;
|
||||
}
|
||||
if (root.isRemoteUrl || source != root.encodedImagePath || status !== Image.Ready || !root.cachePath)
|
||||
return;
|
||||
Paths.mkdir(Paths.imagecache);
|
||||
const grabPath = root.cachePath;
|
||||
if (visible && width > 0 && height > 0 && Window.window?.visible) {
|
||||
grabToImage(res => res.saveToFile(grabPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onImagePathChanged: {
|
||||
if (!imagePath) {
|
||||
source = "";
|
||||
staticImg.source = "";
|
||||
return;
|
||||
}
|
||||
if (isAnimated)
|
||||
return;
|
||||
if (isRemoteUrl) {
|
||||
source = imagePath;
|
||||
staticImg.source = imagePath;
|
||||
return;
|
||||
}
|
||||
Paths.mkdir(Paths.imagecache);
|
||||
const hash = djb2Hash(normalizedPath);
|
||||
const cPath = hash ? `${Paths.stringify(Paths.imagecache)}/${hash}@${maxCacheSize}x${maxCacheSize}.png` : "";
|
||||
const encoded = "file://" + normalizedPath.split('/').map(s => encodeURIComponent(s)).join('/');
|
||||
source = cPath || encoded;
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (source == cachePath && status === Image.Error) {
|
||||
source = encodedImagePath;
|
||||
return;
|
||||
}
|
||||
if (isRemoteUrl || source != encodedImagePath || status !== Image.Ready || !cachePath)
|
||||
return;
|
||||
Paths.mkdir(Paths.imagecache);
|
||||
const grabPath = cachePath;
|
||||
if (visible && width > 0 && height > 0 && Window.window?.visible) {
|
||||
grabToImage(res => res.saveToFile(grabPath));
|
||||
}
|
||||
staticImg.source = cPath || encoded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,6 @@ Item {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.currentValue = delegateRoot.modelData;
|
||||
root.valueChanged(delegateRoot.modelData);
|
||||
dropdownMenu.close();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import qs.Widgets
|
||||
Flickable {
|
||||
id: flickable
|
||||
|
||||
property alias verticalScrollBar: vbar
|
||||
property real mouseWheelSpeed: 60
|
||||
property real momentumVelocity: 0
|
||||
property bool isMomentumActive: false
|
||||
|
||||
@@ -5,12 +5,15 @@ import qs.Common
|
||||
ScrollBar {
|
||||
id: scrollbar
|
||||
|
||||
property var targetFlickable: null
|
||||
readonly property var _target: targetFlickable ?? parent
|
||||
|
||||
property bool _scrollBarActive: false
|
||||
property alias hideTimer: hideScrollBarTimer
|
||||
property bool _isParentMoving: parent && (parent.moving || parent.flicking || parent.isMomentumActive)
|
||||
property bool _isParentMoving: _target && (_target.moving || _target.flicking || _target.isMomentumActive)
|
||||
property bool _shouldShow: pressed || hovered || active || _isParentMoving || _scrollBarActive
|
||||
|
||||
policy: (parent && parent.contentHeight > parent.height) ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
|
||||
policy: (_target && _target.contentHeight > _target.height) ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
|
||||
minimumSize: 0.08
|
||||
implicitWidth: 10
|
||||
interactive: true
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
/* customize things here */
|
||||
:root {
|
||||
/* font, change to 'gg sans' for default discord font*/
|
||||
--font: 'figtree';
|
||||
--font: 'gg sans';
|
||||
|
||||
/* top left corner text */
|
||||
--corner-text: 'Midnight';
|
||||
|
||||
/* color of status indicators and window controls */
|
||||
--online-indicator: {{colors.inverse_primary.default.hex}}; /* change to #23a55a for default green */
|
||||
--online-indicator: {{colors.inverse_primary.default.hex}}; /* change to #23a55a for default green */
|
||||
--dnd-indicator: {{colors.error.default.hex}}; /* change to #f13f43 for default red */
|
||||
--idle-indicator: {{colors.tertiary_container.default.hex}}; /* change to #f0b232 for default yellow */
|
||||
--streaming-indicator: {{colors.on_primary.default.hex}}; /* change to #593695 for default purple */
|
||||
@@ -34,11 +34,11 @@
|
||||
--accent-3: {{colors.primary.default.hex}}; /* accent buttons */
|
||||
--accent-4: {{colors.surface_bright.default.hex}}; /* accent buttons when hovered */
|
||||
--accent-5: {{colors.primary_fixed_dim.default.hex}}; /* accent buttons when clicked */
|
||||
--mention: {{colors.background.default.hex}}; /* mentions & mention messages */
|
||||
--mention: {{colors.surface.default.hex}}; /* mentions & mention messages */
|
||||
--mention-hover: {{colors.surface_bright.default.hex}}; /* mentions & mention messages when hovered */
|
||||
|
||||
/* text colors */
|
||||
--text-0: {{colors.background.default.hex}}; /* text on colored elements */
|
||||
--text-0: {{colors.surface.default.hex}}; /* text on colored elements */
|
||||
--text-1: {{colors.on_surface.default.hex}}; /* other normally white text */
|
||||
--text-2: {{colors.on_surface.default.hex}}; /* headings and important text */
|
||||
--text-3: {{colors.on_surface_variant.default.hex}}; /* normal text */
|
||||
@@ -46,10 +46,10 @@
|
||||
--text-5: {{colors.outline.default.hex}}; /* muted channels/chats and timestamps */
|
||||
|
||||
/* background and dark colors */
|
||||
--bg-1: {{colors.primary.default.hex}}; /* dark buttons when clicked */
|
||||
--bg-2: {{colors.surface_container.default.hex}}; /* dark buttons */
|
||||
--bg-1: {{colors.surface_variant.default.hex}}; /* dark buttons when clicked */
|
||||
--bg-2: {{colors.surface_container_high.default.hex}}; /* dark buttons */
|
||||
--bg-3: {{colors.surface_container_low.default.hex}}; /* spacing, secondary elements */
|
||||
--bg-4: {{colors.background.default.hex}}; /* main background color */
|
||||
--bg-4: {{colors.surface.default.hex}}; /* main background color */
|
||||
--hover: {{colors.surface_bright.default.hex}}; /* channels and buttons when hovered */
|
||||
--active: {{colors.surface_bright.default.hex}}; /* channels and buttons when clicked or selected */
|
||||
--message-hover: {{colors.surface_bright.default.hex}}; /* messages when hovered */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -281,6 +281,9 @@
|
||||
"Anonymous Identity (optional)": {
|
||||
"Anonymous Identity (optional)": "Identidad anónima (opcional)"
|
||||
},
|
||||
"App Customizations": {
|
||||
"App Customizations": ""
|
||||
},
|
||||
"App ID Substitutions": {
|
||||
"App ID Substitutions": ""
|
||||
},
|
||||
@@ -305,12 +308,21 @@
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": {
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": "Aplicar un tono cálido para disminuir la fatiga visual. Configure abajo la automatización para definir cuándo se activa."
|
||||
},
|
||||
"Apps": {
|
||||
"Apps": ""
|
||||
},
|
||||
"Apps Dock": {
|
||||
"Apps Dock": ""
|
||||
},
|
||||
"Apps Icon": {
|
||||
"Apps Icon": "Apps"
|
||||
},
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": {
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": "Las aplicaciones son ordenadas por frecuencia de uso, luego por último utilizado, y luego alfabéticamente."
|
||||
},
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": {
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": ""
|
||||
},
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": {
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": "Organice las pantallas y configure la resolución, la frecuencia de actualización y el VRR."
|
||||
},
|
||||
@@ -410,6 +422,9 @@
|
||||
"Autoconnect enabled": {
|
||||
"Autoconnect enabled": "Conexión automática activada"
|
||||
},
|
||||
"Automatic Color Mode": {
|
||||
"Automatic Color Mode": ""
|
||||
},
|
||||
"Automatic Control": {
|
||||
"Automatic Control": "Control Automático"
|
||||
},
|
||||
@@ -434,6 +449,9 @@
|
||||
"Automatically lock the screen when the system prepares to suspend": {
|
||||
"Automatically lock the screen when the system prepares to suspend": "Bloquear pantalla cuando el sistema se vaya a suspender"
|
||||
},
|
||||
"Automation": {
|
||||
"Automation": ""
|
||||
},
|
||||
"Available": {
|
||||
"Available": "Disponible"
|
||||
},
|
||||
@@ -578,6 +596,9 @@
|
||||
"Browse Plugins": {
|
||||
"Browse Plugins": "Explorar Complementos"
|
||||
},
|
||||
"Browse or search plugins": {
|
||||
"Browse or search plugins": ""
|
||||
},
|
||||
"CPU": {
|
||||
"CPU": "CPU"
|
||||
},
|
||||
@@ -608,6 +629,12 @@
|
||||
"CUPS not available": {
|
||||
"CUPS not available": "CUPS no esta disponibl"
|
||||
},
|
||||
"Calc": {
|
||||
"Calc": ""
|
||||
},
|
||||
"Calculator": {
|
||||
"Calculator": ""
|
||||
},
|
||||
"Camera": {
|
||||
"Camera": "Cámara"
|
||||
},
|
||||
@@ -659,6 +686,9 @@
|
||||
"Choose Color": {
|
||||
"Choose Color": "Elegir color"
|
||||
},
|
||||
"Choose Dock Launcher Logo Color": {
|
||||
"Choose Dock Launcher Logo Color": ""
|
||||
},
|
||||
"Choose Launcher Logo Color": {
|
||||
"Choose Launcher Logo Color": "Elegir Color del Icono en el Lanzador"
|
||||
},
|
||||
@@ -695,6 +725,9 @@
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": {
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": "Escoger en cuál monitor se muestra la interfaz de la pantalla de bloqueo. Otros mostrarán un color sólido para proteger las pantallas OLED contra quemaduras."
|
||||
},
|
||||
"Chroma Style": {
|
||||
"Chroma Style": ""
|
||||
},
|
||||
"Cipher": {
|
||||
"Cipher": "Cifrado"
|
||||
},
|
||||
@@ -770,9 +803,15 @@
|
||||
"Close": {
|
||||
"Close": "Cerrar"
|
||||
},
|
||||
"Close All Windows": {
|
||||
"Close All Windows": ""
|
||||
},
|
||||
"Close Overview on Launch": {
|
||||
"Close Overview on Launch": "Cerrar vista general al iniciar"
|
||||
},
|
||||
"Close Window": {
|
||||
"Close Window": ""
|
||||
},
|
||||
"Color": {
|
||||
"Color": "Color"
|
||||
},
|
||||
@@ -803,6 +842,12 @@
|
||||
"Color temperature for night mode": {
|
||||
"Color temperature for night mode": "Temperatura de color para el modo nocturno"
|
||||
},
|
||||
"Color theme for syntax highlighting.": {
|
||||
"Color theme for syntax highlighting.": ""
|
||||
},
|
||||
"Color theme for syntax highlighting. %1 themes available.": {
|
||||
"Color theme for syntax highlighting. %1 themes available.": ""
|
||||
},
|
||||
"Colorful mix of bright contrasting accents.": {
|
||||
"Colorful mix of bright contrasting accents.": "Colores vivos con contrastes luminosos."
|
||||
},
|
||||
@@ -812,6 +857,9 @@
|
||||
"Command": {
|
||||
"Command": "Comando"
|
||||
},
|
||||
"Commands": {
|
||||
"Commands": ""
|
||||
},
|
||||
"Communication": {
|
||||
"Communication": "Comunicación"
|
||||
},
|
||||
@@ -905,6 +953,9 @@
|
||||
"Control currently playing media": {
|
||||
"Control currently playing media": "Controlar la reproducción en curso"
|
||||
},
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": {
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": ""
|
||||
},
|
||||
"Control workspaces and columns by scrolling on the bar": {
|
||||
"Control workspaces and columns by scrolling on the bar": "Controle los espacios de trabajo y las columnas desplazándose por la barra"
|
||||
},
|
||||
@@ -926,6 +977,9 @@
|
||||
"Copy Full Command": {
|
||||
"Copy Full Command": ""
|
||||
},
|
||||
"Copy HTML": {
|
||||
"Copy HTML": ""
|
||||
},
|
||||
"Copy Name": {
|
||||
"Copy Name": ""
|
||||
},
|
||||
@@ -935,6 +989,15 @@
|
||||
"Copy Process Name": {
|
||||
"Copy Process Name": "Copiar Nombre del Proceso"
|
||||
},
|
||||
"Copy Text": {
|
||||
"Copy Text": ""
|
||||
},
|
||||
"Copy URL": {
|
||||
"Copy URL": ""
|
||||
},
|
||||
"Copy path": {
|
||||
"Copy path": ""
|
||||
},
|
||||
"Corner Radius": {
|
||||
"Corner Radius": "Radio de la esquina"
|
||||
},
|
||||
@@ -1343,6 +1406,9 @@
|
||||
"Edge Spacing": {
|
||||
"Edge Spacing": "Espacio entre bordes"
|
||||
},
|
||||
"Edit App": {
|
||||
"Edit App": ""
|
||||
},
|
||||
"Education": {
|
||||
"Education": "Educación"
|
||||
},
|
||||
@@ -1391,6 +1457,9 @@
|
||||
"Enable loginctl lock integration": {
|
||||
"Enable loginctl lock integration": "Habilitar integración con loginctl"
|
||||
},
|
||||
"Enable media player controls on the lock screen window": {
|
||||
"Show Media Player": ""
|
||||
},
|
||||
"Enable password field display on the lock screen window": {
|
||||
"Show Password Field": "Mostrar campo de contraseña"
|
||||
},
|
||||
@@ -1427,6 +1496,9 @@
|
||||
"Enter PIN for ": {
|
||||
"Enter PIN for ": "Ingresar PIN para "
|
||||
},
|
||||
"Enter a new name for this workspace": {
|
||||
"Enter a new name for this workspace": ""
|
||||
},
|
||||
"Enter a search query": {
|
||||
"Enter a search query": "Ingresar texto a buscar"
|
||||
},
|
||||
@@ -1466,6 +1538,9 @@
|
||||
"Entry unpinned": {
|
||||
"Entry unpinned": ""
|
||||
},
|
||||
"Environment Variables": {
|
||||
"Environment Variables": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "Error"
|
||||
},
|
||||
@@ -1481,6 +1556,9 @@
|
||||
"Exponential": {
|
||||
"Exponential": "Exponencial"
|
||||
},
|
||||
"Extra Arguments": {
|
||||
"Extra Arguments": ""
|
||||
},
|
||||
"F1/I: Toggle • F10: Help": {
|
||||
"F1/I: Toggle • F10: Help": "F1/I: Accionar • F10: Ayudar"
|
||||
},
|
||||
@@ -1670,6 +1748,9 @@
|
||||
"File Information": {
|
||||
"File Information": "Información del archivo"
|
||||
},
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": {
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": ""
|
||||
},
|
||||
"Files": {
|
||||
"Files": "Archivos"
|
||||
},
|
||||
@@ -1868,6 +1949,9 @@
|
||||
"HSV": {
|
||||
"HSV": "HSV"
|
||||
},
|
||||
"HTML copied to clipboard": {
|
||||
"HTML copied to clipboard": ""
|
||||
},
|
||||
"Health": {
|
||||
"Health": "Salud"
|
||||
},
|
||||
@@ -1886,9 +1970,18 @@
|
||||
"Hidden": {
|
||||
"Hidden": ""
|
||||
},
|
||||
"Hidden Apps": {
|
||||
"Hidden Apps": ""
|
||||
},
|
||||
"Hidden Network": {
|
||||
"Hidden Network": ""
|
||||
},
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": {
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": ""
|
||||
},
|
||||
"Hide App": {
|
||||
"Hide App": ""
|
||||
},
|
||||
"Hide Delay": {
|
||||
"Hide Delay": "Ocultar Retardo"
|
||||
},
|
||||
@@ -1970,6 +2063,9 @@
|
||||
"IP Address:": {
|
||||
"IP Address:": "Dirección IP:"
|
||||
},
|
||||
"Icon": {
|
||||
"Icon": ""
|
||||
},
|
||||
"Icon Size": {
|
||||
"Icon Size": "Tamaño de icono"
|
||||
},
|
||||
@@ -2009,6 +2105,9 @@
|
||||
"Include Transitions": {
|
||||
"Include Transitions": "Incluir transiciones"
|
||||
},
|
||||
"Include desktop actions (shortcuts) in search results.": {
|
||||
"Include desktop actions (shortcuts) in search results.": ""
|
||||
},
|
||||
"Incompatible Plugins Loaded": {
|
||||
"Incompatible Plugins Loaded": "Plugins incompatibles cargados"
|
||||
},
|
||||
@@ -2114,11 +2213,18 @@
|
||||
"Failed to reject pairing": "",
|
||||
"Failed to ring device": "",
|
||||
"Failed to send clipboard": "",
|
||||
"Failed to send file": "",
|
||||
"Failed to send ping": "",
|
||||
"Failed to share": "",
|
||||
"Pairing failed": "",
|
||||
"Unpair failed": ""
|
||||
},
|
||||
"KDE Connect file browser title": {
|
||||
"Select File to Send": ""
|
||||
},
|
||||
"KDE Connect file send": {
|
||||
"Sending": ""
|
||||
},
|
||||
"KDE Connect file share notification": {
|
||||
"File received from": ""
|
||||
},
|
||||
@@ -2184,15 +2290,24 @@
|
||||
"KDE Connect ring tooltip": {
|
||||
"Ring": ""
|
||||
},
|
||||
"KDE Connect send file button": {
|
||||
"Send File": ""
|
||||
},
|
||||
"KDE Connect service unavailable message": {
|
||||
"KDE Connect unavailable": ""
|
||||
},
|
||||
"KDE Connect share URL button": {
|
||||
"Share URL": ""
|
||||
},
|
||||
"KDE Connect share button": {
|
||||
"Share Text": ""
|
||||
},
|
||||
"KDE Connect share button | KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share input placeholder": {
|
||||
"Enter URL or text to share": ""
|
||||
},
|
||||
@@ -2265,6 +2380,12 @@
|
||||
"LED device": {
|
||||
"LED device": "Dispositivo LED"
|
||||
},
|
||||
"Large": {
|
||||
"Large": ""
|
||||
},
|
||||
"Largest": {
|
||||
"Largest": ""
|
||||
},
|
||||
"Last launched %1": {
|
||||
"Last launched %1": "Usado hace %1"
|
||||
},
|
||||
@@ -2295,6 +2416,9 @@
|
||||
"Launcher": {
|
||||
"Launcher": "Lanzador"
|
||||
},
|
||||
"Launcher Button": {
|
||||
"Launcher Button": ""
|
||||
},
|
||||
"Launcher Button Logo": {
|
||||
"Launcher Button Logo": "Icono del lanzador"
|
||||
},
|
||||
@@ -2334,6 +2458,9 @@
|
||||
"Loading plugins...": {
|
||||
"Loading plugins...": "Cargando complementos..."
|
||||
},
|
||||
"Loading trending...": {
|
||||
"Loading trending...": ""
|
||||
},
|
||||
"Loading...": {
|
||||
"Loading...": "Cargando..."
|
||||
},
|
||||
@@ -2709,6 +2836,12 @@
|
||||
"No adapters": {
|
||||
"No adapters": "Sin adaptadores"
|
||||
},
|
||||
"No app customizations.": {
|
||||
"No app customizations.": ""
|
||||
},
|
||||
"No apps found": {
|
||||
"No apps found": ""
|
||||
},
|
||||
"No battery": {
|
||||
"No battery": "Sin bateria"
|
||||
},
|
||||
@@ -2739,15 +2872,24 @@
|
||||
"No files found": {
|
||||
"No files found": "Archivos no encontrados"
|
||||
},
|
||||
"No hidden apps.": {
|
||||
"No hidden apps.": ""
|
||||
},
|
||||
"No items added yet": {
|
||||
"No items added yet": "No hay nada añadido todavía"
|
||||
},
|
||||
"No keybinds found": {
|
||||
"No keybinds found": "No se encontraron atajos"
|
||||
},
|
||||
"No launcher plugins installed.": {
|
||||
"No launcher plugins installed.": ""
|
||||
},
|
||||
"No matches": {
|
||||
"No matches": "Sin coincidencias"
|
||||
},
|
||||
"No plugin results": {
|
||||
"No plugin results": ""
|
||||
},
|
||||
"No plugins found": {
|
||||
"No plugins found": "No hay complementos encontrados"
|
||||
},
|
||||
@@ -2766,9 +2908,15 @@
|
||||
"No recent clipboard entries found": {
|
||||
"No recent clipboard entries found": ""
|
||||
},
|
||||
"No results found": {
|
||||
"No results found": ""
|
||||
},
|
||||
"No saved clipboard entries": {
|
||||
"No saved clipboard entries": ""
|
||||
},
|
||||
"No trigger": {
|
||||
"No trigger": ""
|
||||
},
|
||||
"No variants created. Click Add to create a new monitor widget.": {
|
||||
"No variants created. Click Add to create a new monitor widget.": "No hay variantes creadas. Haga clic en Añadir para crear un nuevo widget de monitor."
|
||||
},
|
||||
@@ -2880,6 +3028,12 @@
|
||||
"Open Notepad File": {
|
||||
"Open Notepad File": "Abrir archivo de Notas"
|
||||
},
|
||||
"Open folder": {
|
||||
"Open folder": ""
|
||||
},
|
||||
"Open in Browser": {
|
||||
"Open in Browser": ""
|
||||
},
|
||||
"Open search bar to find text": {
|
||||
"Open search bar to find text": "Abrir barra de búsqueda para encontrar texto"
|
||||
},
|
||||
@@ -2946,6 +3100,9 @@
|
||||
"PIN": {
|
||||
"PIN": "PIN"
|
||||
},
|
||||
"Pad Hours": {
|
||||
"Pad Hours": ""
|
||||
},
|
||||
"Padding": {
|
||||
"Padding": "Relleno"
|
||||
},
|
||||
@@ -3012,6 +3169,9 @@
|
||||
"Pinned": {
|
||||
"Pinned": "Fijado"
|
||||
},
|
||||
"Pinned and running apps with drag-and-drop": {
|
||||
"Pinned and running apps with drag-and-drop": ""
|
||||
},
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": {
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": "Pon aquí el directorio de complementos. Cada complemento debe tener un archivo plugin.json adjunto."
|
||||
},
|
||||
@@ -3048,6 +3208,9 @@
|
||||
"Plugin Management": {
|
||||
"Plugin Management": "Gestión de complementos"
|
||||
},
|
||||
"Plugin Visibility": {
|
||||
"Plugin Visibility": ""
|
||||
},
|
||||
"Plugin is disabled - enable in Plugins settings to use": {
|
||||
"Plugin is disabled - enable in Plugins settings to use": "Complemento desactivado – actívalo en los ajustes de complementos"
|
||||
},
|
||||
@@ -3126,6 +3289,9 @@
|
||||
"Prevent screen timeout": {
|
||||
"Prevent screen timeout": "Evitar que la pantalla se apague por inactividad"
|
||||
},
|
||||
"Preview": {
|
||||
"Preview": ""
|
||||
},
|
||||
"Primary": {
|
||||
"Primary": "Primario"
|
||||
},
|
||||
@@ -3240,6 +3406,12 @@
|
||||
"Remove gaps and border when windows are maximized": {
|
||||
"Remove gaps and border when windows are maximized": "Eliminar espacios y bordes cuando las ventanas estan maximizadas"
|
||||
},
|
||||
"Rename": {
|
||||
"Rename": ""
|
||||
},
|
||||
"Rename Workspace": {
|
||||
"Rename Workspace": ""
|
||||
},
|
||||
"Repeat": {
|
||||
"Repeat": ""
|
||||
},
|
||||
@@ -3417,6 +3589,12 @@
|
||||
"Scrolling": {
|
||||
"Scrolling": "Desplazamiento"
|
||||
},
|
||||
"Search App Actions": {
|
||||
"Search App Actions": ""
|
||||
},
|
||||
"Search Options": {
|
||||
"Search Options": ""
|
||||
},
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": {
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": ""
|
||||
},
|
||||
@@ -3456,6 +3634,9 @@
|
||||
"Security": {
|
||||
"Security": "Seguridad"
|
||||
},
|
||||
"Select": {
|
||||
"Select": ""
|
||||
},
|
||||
"Select Application": {
|
||||
"Select Application": "Seleccionar una aplicación"
|
||||
},
|
||||
@@ -3534,12 +3715,18 @@
|
||||
"Setup": {
|
||||
"Setup": "Configurar"
|
||||
},
|
||||
"Share Gamma Control Settings": {
|
||||
"Share Gamma Control Settings": ""
|
||||
},
|
||||
"Shell": {
|
||||
"Shell": "Shell"
|
||||
},
|
||||
"Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Del: Clear All • Esc: Close": "Shift+Del: Borrar todo • Esc: Cerrar "
|
||||
},
|
||||
"Shift+Enter to paste": {
|
||||
"Shift+Enter to paste": ""
|
||||
},
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": "Mayús+Intro: Pegar - Mayús+Supr: Borrar todo - Esc: Cerrar"
|
||||
},
|
||||
@@ -3597,6 +3784,9 @@
|
||||
"Show Humidity": {
|
||||
"Show Humidity": ""
|
||||
},
|
||||
"Show Launcher Button": {
|
||||
"Show Launcher Button": ""
|
||||
},
|
||||
"Show Line Numbers": {
|
||||
"Show Line Numbers": "Mostrar números de líneas"
|
||||
},
|
||||
@@ -3774,6 +3964,9 @@
|
||||
"Sizing": {
|
||||
"Sizing": "Redimensionamiento"
|
||||
},
|
||||
"Small": {
|
||||
"Small": ""
|
||||
},
|
||||
"Smartcard Authentication": {
|
||||
"Smartcard Authentication": "Autenticación con tarjeta inteligente"
|
||||
},
|
||||
@@ -4095,12 +4288,24 @@
|
||||
"Transparency": {
|
||||
"Transparency": "Transparencia"
|
||||
},
|
||||
"Trending GIFs": {
|
||||
"Trending GIFs": ""
|
||||
},
|
||||
"Trending Stickers": {
|
||||
"Trending Stickers": ""
|
||||
},
|
||||
"Trigger": {
|
||||
"Trigger": ""
|
||||
},
|
||||
"Trigger Prefix": {
|
||||
"Trigger Prefix": ""
|
||||
},
|
||||
"Trigger: %1": {
|
||||
"Trigger: %1": ""
|
||||
},
|
||||
"Try a different search": {
|
||||
"Try a different search": ""
|
||||
},
|
||||
"Turn off all displays immediately when the lock screen activates": {
|
||||
"Turn off all displays immediately when the lock screen activates": ""
|
||||
},
|
||||
@@ -4110,9 +4315,21 @@
|
||||
"Type": {
|
||||
"Type": "Tipo"
|
||||
},
|
||||
"Type at least 2 characters": {
|
||||
"Type at least 2 characters": ""
|
||||
},
|
||||
"Type this prefix to search keybinds": {
|
||||
"Type this prefix to search keybinds": ""
|
||||
},
|
||||
"Type to search": {
|
||||
"Type to search": ""
|
||||
},
|
||||
"Type to search apps": {
|
||||
"Type to search apps": ""
|
||||
},
|
||||
"Type to search files": {
|
||||
"Type to search files": ""
|
||||
},
|
||||
"Typography": {
|
||||
"Typography": "Tipografía"
|
||||
},
|
||||
@@ -4230,6 +4447,9 @@
|
||||
"Use light theme instead of dark theme": {
|
||||
"Use light theme instead of dark theme": "Usar tema claro en lugar del tema oscuro"
|
||||
},
|
||||
"Use meters per second instead of km/h for wind speed": {
|
||||
"Use meters per second instead of km/h for wind speed": ""
|
||||
},
|
||||
"Use smaller notification cards": {
|
||||
"Use smaller notification cards": ""
|
||||
},
|
||||
@@ -4257,9 +4477,15 @@
|
||||
"Username": {
|
||||
"Username": "Nombre de usuario"
|
||||
},
|
||||
"Uses sunrise/sunset times based on your location.": {
|
||||
"Uses sunrise/sunset times based on your location.": ""
|
||||
},
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": {
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": "Usar las horas de amanecer/anochecer para ajustar automáticamente el modo noche basándose en tu localización."
|
||||
},
|
||||
"Using shared settings from Gamma Control": {
|
||||
"Using shared settings from Gamma Control": ""
|
||||
},
|
||||
"Utilities": {
|
||||
"Utilities": "Utilidades"
|
||||
},
|
||||
@@ -4460,6 +4686,9 @@
|
||||
"Wind Speed": {
|
||||
"Wind Speed": "Velocidad del viento"
|
||||
},
|
||||
"Wind Speed in m/s": {
|
||||
"Wind Speed in m/s": ""
|
||||
},
|
||||
"Window Corner Radius": {
|
||||
"Window Corner Radius": "Radio de la esquina de la ventana"
|
||||
},
|
||||
@@ -4493,6 +4722,9 @@
|
||||
"Workspace Switcher": {
|
||||
"Workspace Switcher": "Espacios de trabajo"
|
||||
},
|
||||
"Workspace name": {
|
||||
"Workspace name": ""
|
||||
},
|
||||
"Workspaces": {
|
||||
"Workspaces": "Espacios de trabajo"
|
||||
},
|
||||
@@ -4520,6 +4752,9 @@
|
||||
"You have unsaved changes. Save before opening a file?": {
|
||||
"You have unsaved changes. Save before opening a file?": "Tienes cambios sin guardar. ¿Guardar antes de abrir otro archivo?"
|
||||
},
|
||||
"actions": {
|
||||
"actions": ""
|
||||
},
|
||||
"apps": {
|
||||
"apps": "apps"
|
||||
},
|
||||
@@ -4529,6 +4764,12 @@
|
||||
"bar shadow settings card": {
|
||||
"Shadow": ""
|
||||
},
|
||||
"border color": {
|
||||
"Color": ""
|
||||
},
|
||||
"border thickness": {
|
||||
"Thickness": ""
|
||||
},
|
||||
"browse themes button | theme browser header | theme browser window title": {
|
||||
"Browse Themes": "Busqueda de temas"
|
||||
},
|
||||
@@ -4786,6 +5027,21 @@
|
||||
"installed status": {
|
||||
"Installed": "Instalado"
|
||||
},
|
||||
"launcher appearance settings": {
|
||||
"Appearance": ""
|
||||
},
|
||||
"launcher border option": {
|
||||
"Border": ""
|
||||
},
|
||||
"launcher footer description": {
|
||||
"Show mode tabs and keyboard hints at the bottom.": ""
|
||||
},
|
||||
"launcher footer visibility": {
|
||||
"Show Footer": ""
|
||||
},
|
||||
"launcher size option": {
|
||||
"Size": ""
|
||||
},
|
||||
"leave empty for default": {
|
||||
"leave empty for default": "deja vacio para valor por defecto"
|
||||
},
|
||||
@@ -4826,6 +5082,9 @@
|
||||
"ms": {
|
||||
"ms": "ms"
|
||||
},
|
||||
"nav": {
|
||||
"nav": ""
|
||||
},
|
||||
"no custom theme file status": {
|
||||
"No custom theme file": "Ningún archivo de tema personalizado"
|
||||
},
|
||||
@@ -4882,6 +5141,12 @@
|
||||
"official": {
|
||||
"official": "oficial"
|
||||
},
|
||||
"open": {
|
||||
"open": ""
|
||||
},
|
||||
"outline color": {
|
||||
"Outline": ""
|
||||
},
|
||||
"plugin browser description": {
|
||||
"Install plugins from the DMS plugin registry": "Instalar plugins desde el registro de plugins de DMS"
|
||||
},
|
||||
@@ -4897,6 +5162,9 @@
|
||||
"plugin search placeholder": {
|
||||
"Search plugins...": "Buscar complementos..."
|
||||
},
|
||||
"primary color": {
|
||||
"Primary": ""
|
||||
},
|
||||
"process count label in footer": {
|
||||
"Processes:": ""
|
||||
},
|
||||
@@ -4915,6 +5183,9 @@
|
||||
"registry theme description": {
|
||||
"Color theme from DMS registry": "Tema de color del registro DMS"
|
||||
},
|
||||
"secondary color": {
|
||||
"Secondary": ""
|
||||
},
|
||||
"seconds": {
|
||||
"seconds": "segundos"
|
||||
},
|
||||
@@ -4928,6 +5199,9 @@
|
||||
"Surface": "",
|
||||
"Text": ""
|
||||
},
|
||||
"shadow color option | text color": {
|
||||
"Text": ""
|
||||
},
|
||||
"shadow intensity slider": {
|
||||
"Intensity": ""
|
||||
},
|
||||
@@ -5007,6 +5281,9 @@
|
||||
"wallpaper settings external management": {
|
||||
"External Wallpaper Management": "Manejo externo del fondo de pantalla"
|
||||
},
|
||||
"weather feels like temperature": {
|
||||
"Feels Like %1°": ""
|
||||
},
|
||||
"wtype not available - install wtype for paste support": {
|
||||
"wtype not available - install wtype for paste support": "wtype no disponible - instala wtype para soporte de pegado"
|
||||
},
|
||||
|
||||
@@ -281,6 +281,9 @@
|
||||
"Anonymous Identity (optional)": {
|
||||
"Anonymous Identity (optional)": "هویت ناشناس (اختیاری)"
|
||||
},
|
||||
"App Customizations": {
|
||||
"App Customizations": ""
|
||||
},
|
||||
"App ID Substitutions": {
|
||||
"App ID Substitutions": ""
|
||||
},
|
||||
@@ -305,12 +308,21 @@
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": {
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": "برای کاهش خستگی چشم دمای رنگ گرم را اعمال کن. از تنظیمات خودکارسازی پایین برای زمان فعال شدن آن استفاده کنید."
|
||||
},
|
||||
"Apps": {
|
||||
"Apps": ""
|
||||
},
|
||||
"Apps Dock": {
|
||||
"Apps Dock": ""
|
||||
},
|
||||
"Apps Icon": {
|
||||
"Apps Icon": "آیکون برنامهها"
|
||||
},
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": {
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": "برنامهها بر اساس دفعات استفاده، سپس آخرین استفاده و در نهایت بر اساس حروف الفبا مرتب شدهاند."
|
||||
},
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": {
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": ""
|
||||
},
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": {
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": "چیدمان نمایشگرها و پیکربندی وضوح، نرخ تازهسازی و VRR"
|
||||
},
|
||||
@@ -410,6 +422,9 @@
|
||||
"Autoconnect enabled": {
|
||||
"Autoconnect enabled": "اتصال خودکار فعال"
|
||||
},
|
||||
"Automatic Color Mode": {
|
||||
"Automatic Color Mode": ""
|
||||
},
|
||||
"Automatic Control": {
|
||||
"Automatic Control": "کنترل خودکار"
|
||||
},
|
||||
@@ -434,6 +449,9 @@
|
||||
"Automatically lock the screen when the system prepares to suspend": {
|
||||
"Automatically lock the screen when the system prepares to suspend": "قفل خودکار صفحه هنگام آمادهشدن سیستم برای تعلیق"
|
||||
},
|
||||
"Automation": {
|
||||
"Automation": ""
|
||||
},
|
||||
"Available": {
|
||||
"Available": "در دسترس"
|
||||
},
|
||||
@@ -578,6 +596,9 @@
|
||||
"Browse Plugins": {
|
||||
"Browse Plugins": "مرور افزونهها"
|
||||
},
|
||||
"Browse or search plugins": {
|
||||
"Browse or search plugins": ""
|
||||
},
|
||||
"CPU": {
|
||||
"CPU": "CPU"
|
||||
},
|
||||
@@ -608,6 +629,12 @@
|
||||
"CUPS not available": {
|
||||
"CUPS not available": "CUPS در دسترس نیست"
|
||||
},
|
||||
"Calc": {
|
||||
"Calc": ""
|
||||
},
|
||||
"Calculator": {
|
||||
"Calculator": ""
|
||||
},
|
||||
"Camera": {
|
||||
"Camera": "دوربین"
|
||||
},
|
||||
@@ -659,6 +686,9 @@
|
||||
"Choose Color": {
|
||||
"Choose Color": "انتخاب رنگ"
|
||||
},
|
||||
"Choose Dock Launcher Logo Color": {
|
||||
"Choose Dock Launcher Logo Color": ""
|
||||
},
|
||||
"Choose Launcher Logo Color": {
|
||||
"Choose Launcher Logo Color": "انتخاب رنگ لوگوی لانچر"
|
||||
},
|
||||
@@ -695,6 +725,9 @@
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": {
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": "انتخاب کنید که کدام مانیتور رابط صفحه قفل را نشان دهد. مانیتورهای دیگر برای محافظت در برابر سوختگی OLED، رنگ ثابتی را نمایش میدهند."
|
||||
},
|
||||
"Chroma Style": {
|
||||
"Chroma Style": ""
|
||||
},
|
||||
"Cipher": {
|
||||
"Cipher": "رمزگذار"
|
||||
},
|
||||
@@ -770,9 +803,15 @@
|
||||
"Close": {
|
||||
"Close": "بستن"
|
||||
},
|
||||
"Close All Windows": {
|
||||
"Close All Windows": ""
|
||||
},
|
||||
"Close Overview on Launch": {
|
||||
"Close Overview on Launch": "بستن نمای کلی هنگام اجرا"
|
||||
},
|
||||
"Close Window": {
|
||||
"Close Window": ""
|
||||
},
|
||||
"Color": {
|
||||
"Color": "رنگ"
|
||||
},
|
||||
@@ -803,6 +842,12 @@
|
||||
"Color temperature for night mode": {
|
||||
"Color temperature for night mode": "دمای رنگ برای حالت شب"
|
||||
},
|
||||
"Color theme for syntax highlighting.": {
|
||||
"Color theme for syntax highlighting.": ""
|
||||
},
|
||||
"Color theme for syntax highlighting. %1 themes available.": {
|
||||
"Color theme for syntax highlighting. %1 themes available.": ""
|
||||
},
|
||||
"Colorful mix of bright contrasting accents.": {
|
||||
"Colorful mix of bright contrasting accents.": "ترکیب رنگارنگ از رنگهای تأکیدی متضاد."
|
||||
},
|
||||
@@ -812,6 +857,9 @@
|
||||
"Command": {
|
||||
"Command": "دستور"
|
||||
},
|
||||
"Commands": {
|
||||
"Commands": ""
|
||||
},
|
||||
"Communication": {
|
||||
"Communication": "ارتباطات"
|
||||
},
|
||||
@@ -905,6 +953,9 @@
|
||||
"Control currently playing media": {
|
||||
"Control currently playing media": "کنترل رسانه درحال پخش"
|
||||
},
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": {
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": ""
|
||||
},
|
||||
"Control workspaces and columns by scrolling on the bar": {
|
||||
"Control workspaces and columns by scrolling on the bar": "کنترل workspaceها و ستونها با اسکرول روی نوار"
|
||||
},
|
||||
@@ -926,6 +977,9 @@
|
||||
"Copy Full Command": {
|
||||
"Copy Full Command": ""
|
||||
},
|
||||
"Copy HTML": {
|
||||
"Copy HTML": ""
|
||||
},
|
||||
"Copy Name": {
|
||||
"Copy Name": ""
|
||||
},
|
||||
@@ -935,6 +989,15 @@
|
||||
"Copy Process Name": {
|
||||
"Copy Process Name": "کپی نام فرایند"
|
||||
},
|
||||
"Copy Text": {
|
||||
"Copy Text": ""
|
||||
},
|
||||
"Copy URL": {
|
||||
"Copy URL": ""
|
||||
},
|
||||
"Copy path": {
|
||||
"Copy path": ""
|
||||
},
|
||||
"Corner Radius": {
|
||||
"Corner Radius": "شعاع گوشه"
|
||||
},
|
||||
@@ -1343,6 +1406,9 @@
|
||||
"Edge Spacing": {
|
||||
"Edge Spacing": "فاصله لبه"
|
||||
},
|
||||
"Edit App": {
|
||||
"Edit App": ""
|
||||
},
|
||||
"Education": {
|
||||
"Education": "آموزش"
|
||||
},
|
||||
@@ -1391,6 +1457,9 @@
|
||||
"Enable loginctl lock integration": {
|
||||
"Enable loginctl lock integration": "فعالکردن یکپارچهسازی قفل loginctl"
|
||||
},
|
||||
"Enable media player controls on the lock screen window": {
|
||||
"Show Media Player": ""
|
||||
},
|
||||
"Enable password field display on the lock screen window": {
|
||||
"Show Password Field": "نمایش فیلد گذرواژه"
|
||||
},
|
||||
@@ -1427,6 +1496,9 @@
|
||||
"Enter PIN for ": {
|
||||
"Enter PIN for ": "ورود PIN برای "
|
||||
},
|
||||
"Enter a new name for this workspace": {
|
||||
"Enter a new name for this workspace": ""
|
||||
},
|
||||
"Enter a search query": {
|
||||
"Enter a search query": "یک عبارت جستجو وارد کنید"
|
||||
},
|
||||
@@ -1466,6 +1538,9 @@
|
||||
"Entry unpinned": {
|
||||
"Entry unpinned": ""
|
||||
},
|
||||
"Environment Variables": {
|
||||
"Environment Variables": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "خطا"
|
||||
},
|
||||
@@ -1481,6 +1556,9 @@
|
||||
"Exponential": {
|
||||
"Exponential": "نمایی"
|
||||
},
|
||||
"Extra Arguments": {
|
||||
"Extra Arguments": ""
|
||||
},
|
||||
"F1/I: Toggle • F10: Help": {
|
||||
"F1/I: Toggle • F10: Help": "F1/I: تغییر حالت • F10: راهنما"
|
||||
},
|
||||
@@ -1670,6 +1748,9 @@
|
||||
"File Information": {
|
||||
"File Information": "اطلاعات فایل"
|
||||
},
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": {
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": ""
|
||||
},
|
||||
"Files": {
|
||||
"Files": "فایلها"
|
||||
},
|
||||
@@ -1868,6 +1949,9 @@
|
||||
"HSV": {
|
||||
"HSV": "HSV"
|
||||
},
|
||||
"HTML copied to clipboard": {
|
||||
"HTML copied to clipboard": ""
|
||||
},
|
||||
"Health": {
|
||||
"Health": "سلامت"
|
||||
},
|
||||
@@ -1886,9 +1970,18 @@
|
||||
"Hidden": {
|
||||
"Hidden": ""
|
||||
},
|
||||
"Hidden Apps": {
|
||||
"Hidden Apps": ""
|
||||
},
|
||||
"Hidden Network": {
|
||||
"Hidden Network": ""
|
||||
},
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": {
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": ""
|
||||
},
|
||||
"Hide App": {
|
||||
"Hide App": ""
|
||||
},
|
||||
"Hide Delay": {
|
||||
"Hide Delay": "تأخیر پنهانشدن"
|
||||
},
|
||||
@@ -1970,6 +2063,9 @@
|
||||
"IP Address:": {
|
||||
"IP Address:": "آدرس IP:"
|
||||
},
|
||||
"Icon": {
|
||||
"Icon": ""
|
||||
},
|
||||
"Icon Size": {
|
||||
"Icon Size": "اندازه آیکون"
|
||||
},
|
||||
@@ -2009,6 +2105,9 @@
|
||||
"Include Transitions": {
|
||||
"Include Transitions": "اضافهکردن گذارها"
|
||||
},
|
||||
"Include desktop actions (shortcuts) in search results.": {
|
||||
"Include desktop actions (shortcuts) in search results.": ""
|
||||
},
|
||||
"Incompatible Plugins Loaded": {
|
||||
"Incompatible Plugins Loaded": "افزونههای ناسازگار بارگذاری شدهاند"
|
||||
},
|
||||
@@ -2114,11 +2213,18 @@
|
||||
"Failed to reject pairing": "",
|
||||
"Failed to ring device": "",
|
||||
"Failed to send clipboard": "",
|
||||
"Failed to send file": "",
|
||||
"Failed to send ping": "",
|
||||
"Failed to share": "",
|
||||
"Pairing failed": "",
|
||||
"Unpair failed": ""
|
||||
},
|
||||
"KDE Connect file browser title": {
|
||||
"Select File to Send": ""
|
||||
},
|
||||
"KDE Connect file send": {
|
||||
"Sending": ""
|
||||
},
|
||||
"KDE Connect file share notification": {
|
||||
"File received from": ""
|
||||
},
|
||||
@@ -2184,15 +2290,24 @@
|
||||
"KDE Connect ring tooltip": {
|
||||
"Ring": ""
|
||||
},
|
||||
"KDE Connect send file button": {
|
||||
"Send File": ""
|
||||
},
|
||||
"KDE Connect service unavailable message": {
|
||||
"KDE Connect unavailable": ""
|
||||
},
|
||||
"KDE Connect share URL button": {
|
||||
"Share URL": ""
|
||||
},
|
||||
"KDE Connect share button": {
|
||||
"Share Text": ""
|
||||
},
|
||||
"KDE Connect share button | KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share input placeholder": {
|
||||
"Enter URL or text to share": ""
|
||||
},
|
||||
@@ -2265,6 +2380,12 @@
|
||||
"LED device": {
|
||||
"LED device": "دستگاه LED"
|
||||
},
|
||||
"Large": {
|
||||
"Large": ""
|
||||
},
|
||||
"Largest": {
|
||||
"Largest": ""
|
||||
},
|
||||
"Last launched %1": {
|
||||
"Last launched %1": "آخرین اجرا %1"
|
||||
},
|
||||
@@ -2295,6 +2416,9 @@
|
||||
"Launcher": {
|
||||
"Launcher": "لانچر"
|
||||
},
|
||||
"Launcher Button": {
|
||||
"Launcher Button": ""
|
||||
},
|
||||
"Launcher Button Logo": {
|
||||
"Launcher Button Logo": "لوگوی دکمه لانچر"
|
||||
},
|
||||
@@ -2334,6 +2458,9 @@
|
||||
"Loading plugins...": {
|
||||
"Loading plugins...": "آمادهسازی افزونهها..."
|
||||
},
|
||||
"Loading trending...": {
|
||||
"Loading trending...": ""
|
||||
},
|
||||
"Loading...": {
|
||||
"Loading...": "درحال بارگذاری..."
|
||||
},
|
||||
@@ -2709,6 +2836,12 @@
|
||||
"No adapters": {
|
||||
"No adapters": "آداپتوری یافت نشد"
|
||||
},
|
||||
"No app customizations.": {
|
||||
"No app customizations.": ""
|
||||
},
|
||||
"No apps found": {
|
||||
"No apps found": ""
|
||||
},
|
||||
"No battery": {
|
||||
"No battery": "بدون باتری"
|
||||
},
|
||||
@@ -2739,15 +2872,24 @@
|
||||
"No files found": {
|
||||
"No files found": "هیچ فایلی یافت نشد"
|
||||
},
|
||||
"No hidden apps.": {
|
||||
"No hidden apps.": ""
|
||||
},
|
||||
"No items added yet": {
|
||||
"No items added yet": "هنوز آیتمی اضافه نشده"
|
||||
},
|
||||
"No keybinds found": {
|
||||
"No keybinds found": "هیچ کلید میانبری یافت نشد"
|
||||
},
|
||||
"No launcher plugins installed.": {
|
||||
"No launcher plugins installed.": ""
|
||||
},
|
||||
"No matches": {
|
||||
"No matches": "موردی پیدا نشد"
|
||||
},
|
||||
"No plugin results": {
|
||||
"No plugin results": ""
|
||||
},
|
||||
"No plugins found": {
|
||||
"No plugins found": "افزونهای یافت نشد"
|
||||
},
|
||||
@@ -2766,9 +2908,15 @@
|
||||
"No recent clipboard entries found": {
|
||||
"No recent clipboard entries found": ""
|
||||
},
|
||||
"No results found": {
|
||||
"No results found": ""
|
||||
},
|
||||
"No saved clipboard entries": {
|
||||
"No saved clipboard entries": ""
|
||||
},
|
||||
"No trigger": {
|
||||
"No trigger": ""
|
||||
},
|
||||
"No variants created. Click Add to create a new monitor widget.": {
|
||||
"No variants created. Click Add to create a new monitor widget.": "هیچ گونهی دیگری ایجاد نشد. برای ایجاد یک ابزارک مانیتور جدید، روی افزودن کلیک کنید."
|
||||
},
|
||||
@@ -2880,6 +3028,12 @@
|
||||
"Open Notepad File": {
|
||||
"Open Notepad File": "بازکردن فایل دفترچه یادداشت"
|
||||
},
|
||||
"Open folder": {
|
||||
"Open folder": ""
|
||||
},
|
||||
"Open in Browser": {
|
||||
"Open in Browser": ""
|
||||
},
|
||||
"Open search bar to find text": {
|
||||
"Open search bar to find text": "بازکردن نوار جستجو برای یافتن متن"
|
||||
},
|
||||
@@ -2946,6 +3100,9 @@
|
||||
"PIN": {
|
||||
"PIN": "PIN"
|
||||
},
|
||||
"Pad Hours": {
|
||||
"Pad Hours": ""
|
||||
},
|
||||
"Padding": {
|
||||
"Padding": "فاصله درونی"
|
||||
},
|
||||
@@ -3012,6 +3169,9 @@
|
||||
"Pinned": {
|
||||
"Pinned": "سنجاق شده"
|
||||
},
|
||||
"Pinned and running apps with drag-and-drop": {
|
||||
"Pinned and running apps with drag-and-drop": ""
|
||||
},
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": {
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": "دایرکتوری افزونهها را اینجا قرار دهید. هر افزونه باید فایل مشخصات plugins.json داشته باشد."
|
||||
},
|
||||
@@ -3048,6 +3208,9 @@
|
||||
"Plugin Management": {
|
||||
"Plugin Management": "مدیریت افزونه"
|
||||
},
|
||||
"Plugin Visibility": {
|
||||
"Plugin Visibility": ""
|
||||
},
|
||||
"Plugin is disabled - enable in Plugins settings to use": {
|
||||
"Plugin is disabled - enable in Plugins settings to use": "افزونه غیرفعال است - برای استفاده آن را در تنظیمات افزونهها فعال کنید"
|
||||
},
|
||||
@@ -3126,6 +3289,9 @@
|
||||
"Prevent screen timeout": {
|
||||
"Prevent screen timeout": "جلوگیری از خاموششدن صفحه"
|
||||
},
|
||||
"Preview": {
|
||||
"Preview": ""
|
||||
},
|
||||
"Primary": {
|
||||
"Primary": "اصلی"
|
||||
},
|
||||
@@ -3240,6 +3406,12 @@
|
||||
"Remove gaps and border when windows are maximized": {
|
||||
"Remove gaps and border when windows are maximized": "حذف فاصلهها و حاشیه هنگام بزرگکردن پنجرهها"
|
||||
},
|
||||
"Rename": {
|
||||
"Rename": ""
|
||||
},
|
||||
"Rename Workspace": {
|
||||
"Rename Workspace": ""
|
||||
},
|
||||
"Repeat": {
|
||||
"Repeat": ""
|
||||
},
|
||||
@@ -3417,6 +3589,12 @@
|
||||
"Scrolling": {
|
||||
"Scrolling": "اسکرولینگ"
|
||||
},
|
||||
"Search App Actions": {
|
||||
"Search App Actions": ""
|
||||
},
|
||||
"Search Options": {
|
||||
"Search Options": ""
|
||||
},
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": {
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": ""
|
||||
},
|
||||
@@ -3456,6 +3634,9 @@
|
||||
"Security": {
|
||||
"Security": "امنیت"
|
||||
},
|
||||
"Select": {
|
||||
"Select": ""
|
||||
},
|
||||
"Select Application": {
|
||||
"Select Application": "انتخاب برنامه"
|
||||
},
|
||||
@@ -3534,12 +3715,18 @@
|
||||
"Setup": {
|
||||
"Setup": "راهاندازی"
|
||||
},
|
||||
"Share Gamma Control Settings": {
|
||||
"Share Gamma Control Settings": ""
|
||||
},
|
||||
"Shell": {
|
||||
"Shell": "شِل"
|
||||
},
|
||||
"Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Del: Clear All • Esc: Close": "Shift+Del: پاککردن همه • Esc: بستن"
|
||||
},
|
||||
"Shift+Enter to paste": {
|
||||
"Shift+Enter to paste": ""
|
||||
},
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": "Shift+Enter: الصاق • Shift+Del: پاککردن همه • Esc: بستن"
|
||||
},
|
||||
@@ -3597,6 +3784,9 @@
|
||||
"Show Humidity": {
|
||||
"Show Humidity": "نمایش رطوبت"
|
||||
},
|
||||
"Show Launcher Button": {
|
||||
"Show Launcher Button": ""
|
||||
},
|
||||
"Show Line Numbers": {
|
||||
"Show Line Numbers": "نمایش شماره خطوط"
|
||||
},
|
||||
@@ -3774,6 +3964,9 @@
|
||||
"Sizing": {
|
||||
"Sizing": "اندازهدهی"
|
||||
},
|
||||
"Small": {
|
||||
"Small": ""
|
||||
},
|
||||
"Smartcard Authentication": {
|
||||
"Smartcard Authentication": "احراز هویت با کارت هوشمند"
|
||||
},
|
||||
@@ -4095,12 +4288,24 @@
|
||||
"Transparency": {
|
||||
"Transparency": "شفافیت"
|
||||
},
|
||||
"Trending GIFs": {
|
||||
"Trending GIFs": ""
|
||||
},
|
||||
"Trending Stickers": {
|
||||
"Trending Stickers": ""
|
||||
},
|
||||
"Trigger": {
|
||||
"Trigger": ""
|
||||
},
|
||||
"Trigger Prefix": {
|
||||
"Trigger Prefix": ""
|
||||
},
|
||||
"Trigger: %1": {
|
||||
"Trigger: %1": ""
|
||||
},
|
||||
"Try a different search": {
|
||||
"Try a different search": ""
|
||||
},
|
||||
"Turn off all displays immediately when the lock screen activates": {
|
||||
"Turn off all displays immediately when the lock screen activates": ""
|
||||
},
|
||||
@@ -4110,9 +4315,21 @@
|
||||
"Type": {
|
||||
"Type": "نوع"
|
||||
},
|
||||
"Type at least 2 characters": {
|
||||
"Type at least 2 characters": ""
|
||||
},
|
||||
"Type this prefix to search keybinds": {
|
||||
"Type this prefix to search keybinds": ""
|
||||
},
|
||||
"Type to search": {
|
||||
"Type to search": ""
|
||||
},
|
||||
"Type to search apps": {
|
||||
"Type to search apps": ""
|
||||
},
|
||||
"Type to search files": {
|
||||
"Type to search files": ""
|
||||
},
|
||||
"Typography": {
|
||||
"Typography": "تایپوگرافی"
|
||||
},
|
||||
@@ -4230,6 +4447,9 @@
|
||||
"Use light theme instead of dark theme": {
|
||||
"Use light theme instead of dark theme": "استفاده از تم روشن به جای تم تاریک"
|
||||
},
|
||||
"Use meters per second instead of km/h for wind speed": {
|
||||
"Use meters per second instead of km/h for wind speed": ""
|
||||
},
|
||||
"Use smaller notification cards": {
|
||||
"Use smaller notification cards": ""
|
||||
},
|
||||
@@ -4257,9 +4477,15 @@
|
||||
"Username": {
|
||||
"Username": "نام کاربری"
|
||||
},
|
||||
"Uses sunrise/sunset times based on your location.": {
|
||||
"Uses sunrise/sunset times based on your location.": ""
|
||||
},
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": {
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": "از زمان طلوع/غروب خورشید برای تنظیم خودکار حالت شب بر اساس موقعیت مکانی شما استفاده میکند."
|
||||
},
|
||||
"Using shared settings from Gamma Control": {
|
||||
"Using shared settings from Gamma Control": ""
|
||||
},
|
||||
"Utilities": {
|
||||
"Utilities": "برنامههای کمکی"
|
||||
},
|
||||
@@ -4460,6 +4686,9 @@
|
||||
"Wind Speed": {
|
||||
"Wind Speed": "سرعت باد"
|
||||
},
|
||||
"Wind Speed in m/s": {
|
||||
"Wind Speed in m/s": ""
|
||||
},
|
||||
"Window Corner Radius": {
|
||||
"Window Corner Radius": "شعاع گوشه پنجرهها"
|
||||
},
|
||||
@@ -4493,6 +4722,9 @@
|
||||
"Workspace Switcher": {
|
||||
"Workspace Switcher": "تغییردهنده workspace"
|
||||
},
|
||||
"Workspace name": {
|
||||
"Workspace name": ""
|
||||
},
|
||||
"Workspaces": {
|
||||
"Workspaces": "Workspaceها"
|
||||
},
|
||||
@@ -4520,6 +4752,9 @@
|
||||
"You have unsaved changes. Save before opening a file?": {
|
||||
"You have unsaved changes. Save before opening a file?": "شما تغییرات ذخیره نشده دارید. پیش از باز کردن فایل، ذخیره شوند؟"
|
||||
},
|
||||
"actions": {
|
||||
"actions": ""
|
||||
},
|
||||
"apps": {
|
||||
"apps": "برنامه"
|
||||
},
|
||||
@@ -4529,6 +4764,12 @@
|
||||
"bar shadow settings card": {
|
||||
"Shadow": ""
|
||||
},
|
||||
"border color": {
|
||||
"Color": ""
|
||||
},
|
||||
"border thickness": {
|
||||
"Thickness": ""
|
||||
},
|
||||
"browse themes button | theme browser header | theme browser window title": {
|
||||
"Browse Themes": "مرور تمها"
|
||||
},
|
||||
@@ -4696,7 +4937,7 @@
|
||||
},
|
||||
"greeter feature card title": {
|
||||
"App Theming": "تم برنامه",
|
||||
"Control Center": "",
|
||||
"Control Center": "مرکز کنترل",
|
||||
"Display Control": "کنترل نمایشگر",
|
||||
"Dynamic Theming": "",
|
||||
"Multi-Monitor": "چند مانیتوره",
|
||||
@@ -4722,7 +4963,7 @@
|
||||
"niri shortcuts config": "پیکربندی میانبرهای نیری"
|
||||
},
|
||||
"greeter keybinds section header": {
|
||||
"DMS Shortcuts": ""
|
||||
"DMS Shortcuts": "میانبرهای DMS"
|
||||
},
|
||||
"greeter modal window title": {
|
||||
"Welcome": "خوش آمدید"
|
||||
@@ -4766,7 +5007,7 @@
|
||||
"Features": "ویژگیها"
|
||||
},
|
||||
"greeter welcome page tagline": {
|
||||
"A modern desktop shell for Wayland compositors": ""
|
||||
"A modern desktop shell for Wayland compositors": "یک پوسته دسکتاپ مدرن برای کامپازیتورهای وِیلند"
|
||||
},
|
||||
"greeter welcome page title": {
|
||||
"Welcome to DankMaterialShell": "به DankMaterialShell خوش آمدید"
|
||||
@@ -4786,6 +5027,21 @@
|
||||
"installed status": {
|
||||
"Installed": "نصب شده"
|
||||
},
|
||||
"launcher appearance settings": {
|
||||
"Appearance": ""
|
||||
},
|
||||
"launcher border option": {
|
||||
"Border": ""
|
||||
},
|
||||
"launcher footer description": {
|
||||
"Show mode tabs and keyboard hints at the bottom.": ""
|
||||
},
|
||||
"launcher footer visibility": {
|
||||
"Show Footer": ""
|
||||
},
|
||||
"launcher size option": {
|
||||
"Size": ""
|
||||
},
|
||||
"leave empty for default": {
|
||||
"leave empty for default": "برای پیشفرض خالی رها کنید"
|
||||
},
|
||||
@@ -4826,6 +5082,9 @@
|
||||
"ms": {
|
||||
"ms": "ms"
|
||||
},
|
||||
"nav": {
|
||||
"nav": ""
|
||||
},
|
||||
"no custom theme file status": {
|
||||
"No custom theme file": "هیچ تم سفارشی یافت نشد"
|
||||
},
|
||||
@@ -4882,6 +5141,12 @@
|
||||
"official": {
|
||||
"official": "رسمی"
|
||||
},
|
||||
"open": {
|
||||
"open": ""
|
||||
},
|
||||
"outline color": {
|
||||
"Outline": ""
|
||||
},
|
||||
"plugin browser description": {
|
||||
"Install plugins from the DMS plugin registry": "نصب افزونهها از مخزن افزونه DMS"
|
||||
},
|
||||
@@ -4897,6 +5162,9 @@
|
||||
"plugin search placeholder": {
|
||||
"Search plugins...": "جستجوی افزونهها..."
|
||||
},
|
||||
"primary color": {
|
||||
"Primary": ""
|
||||
},
|
||||
"process count label in footer": {
|
||||
"Processes:": ""
|
||||
},
|
||||
@@ -4915,6 +5183,9 @@
|
||||
"registry theme description": {
|
||||
"Color theme from DMS registry": "رنگ تم از مخزن DMS"
|
||||
},
|
||||
"secondary color": {
|
||||
"Secondary": ""
|
||||
},
|
||||
"seconds": {
|
||||
"seconds": "ثانیه"
|
||||
},
|
||||
@@ -4928,6 +5199,9 @@
|
||||
"Surface": "",
|
||||
"Text": ""
|
||||
},
|
||||
"shadow color option | text color": {
|
||||
"Text": ""
|
||||
},
|
||||
"shadow intensity slider": {
|
||||
"Intensity": ""
|
||||
},
|
||||
@@ -5007,6 +5281,9 @@
|
||||
"wallpaper settings external management": {
|
||||
"External Wallpaper Management": "مدیریت تصویر پسزمینه خارجی"
|
||||
},
|
||||
"weather feels like temperature": {
|
||||
"Feels Like %1°": ""
|
||||
},
|
||||
"wtype not available - install wtype for paste support": {
|
||||
"wtype not available - install wtype for paste support": "wtype در دسترس نیست - wtype را برای پشتیبانی از الصاق نصب کنید"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -281,6 +281,9 @@
|
||||
"Anonymous Identity (optional)": {
|
||||
"Anonymous Identity (optional)": "זהות אנונימית (אופציונלי)"
|
||||
},
|
||||
"App Customizations": {
|
||||
"App Customizations": ""
|
||||
},
|
||||
"App ID Substitutions": {
|
||||
"App ID Substitutions": "החלפת ID לאפליקציות"
|
||||
},
|
||||
@@ -305,12 +308,21 @@
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": {
|
||||
"Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates.": "הגדרת טמפרטורת צבע חמה כדי להפחית מאמץ בעיניים. השתמש/י בהגדרות האוטומציה למטה כדי לשלוט מתי ההגדרה מופעלת."
|
||||
},
|
||||
"Apps": {
|
||||
"Apps": ""
|
||||
},
|
||||
"Apps Dock": {
|
||||
"Apps Dock": ""
|
||||
},
|
||||
"Apps Icon": {
|
||||
"Apps Icon": "סמל אפליקציות"
|
||||
},
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": {
|
||||
"Apps are ordered by usage frequency, then last used, then alphabetically.": "האפליקציות ממוינות לפי תדירות השימוש, אחר כך לפי שימוש אחרון ולבסוף לפי סדר אלפביתי."
|
||||
},
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": {
|
||||
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": ""
|
||||
},
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": {
|
||||
"Arrange displays and configure resolution, refresh rate, and VRR": "סדר/י מסכים והגדר/י רזולוציה, קצב רענון וVRR"
|
||||
},
|
||||
@@ -410,6 +422,9 @@
|
||||
"Autoconnect enabled": {
|
||||
"Autoconnect enabled": "התחברות אוטומטית מופעלת"
|
||||
},
|
||||
"Automatic Color Mode": {
|
||||
"Automatic Color Mode": ""
|
||||
},
|
||||
"Automatic Control": {
|
||||
"Automatic Control": "בקרה אוטומטית"
|
||||
},
|
||||
@@ -434,6 +449,9 @@
|
||||
"Automatically lock the screen when the system prepares to suspend": {
|
||||
"Automatically lock the screen when the system prepares to suspend": "נעילה אוטומטית של המסך כשהמערכת מתכוננת למצב השהיה"
|
||||
},
|
||||
"Automation": {
|
||||
"Automation": ""
|
||||
},
|
||||
"Available": {
|
||||
"Available": "זמין"
|
||||
},
|
||||
@@ -578,6 +596,9 @@
|
||||
"Browse Plugins": {
|
||||
"Browse Plugins": "עיון בתוספים"
|
||||
},
|
||||
"Browse or search plugins": {
|
||||
"Browse or search plugins": ""
|
||||
},
|
||||
"CPU": {
|
||||
"CPU": "CPU"
|
||||
},
|
||||
@@ -608,6 +629,12 @@
|
||||
"CUPS not available": {
|
||||
"CUPS not available": "CUPS אינו זמין"
|
||||
},
|
||||
"Calc": {
|
||||
"Calc": ""
|
||||
},
|
||||
"Calculator": {
|
||||
"Calculator": ""
|
||||
},
|
||||
"Camera": {
|
||||
"Camera": "מצלמה"
|
||||
},
|
||||
@@ -659,6 +686,9 @@
|
||||
"Choose Color": {
|
||||
"Choose Color": "בחר/י צבע"
|
||||
},
|
||||
"Choose Dock Launcher Logo Color": {
|
||||
"Choose Dock Launcher Logo Color": ""
|
||||
},
|
||||
"Choose Launcher Logo Color": {
|
||||
"Choose Launcher Logo Color": "בחר/י צבע לוגו למשגר"
|
||||
},
|
||||
@@ -695,6 +725,9 @@
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": {
|
||||
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": "בחר/י איזה מסך מציג את ממשק מסך הנעילה. מסכים אחרים יציגו צבע אחיד להגנה מפני צריבת OLED."
|
||||
},
|
||||
"Chroma Style": {
|
||||
"Chroma Style": ""
|
||||
},
|
||||
"Cipher": {
|
||||
"Cipher": "צופן"
|
||||
},
|
||||
@@ -770,9 +803,15 @@
|
||||
"Close": {
|
||||
"Close": "סגירה"
|
||||
},
|
||||
"Close All Windows": {
|
||||
"Close All Windows": ""
|
||||
},
|
||||
"Close Overview on Launch": {
|
||||
"Close Overview on Launch": "סגור/י את הסקירה בעת הפעלה"
|
||||
},
|
||||
"Close Window": {
|
||||
"Close Window": ""
|
||||
},
|
||||
"Color": {
|
||||
"Color": "צבע"
|
||||
},
|
||||
@@ -803,6 +842,12 @@
|
||||
"Color temperature for night mode": {
|
||||
"Color temperature for night mode": "טמפרטורת צבע למצב לילה"
|
||||
},
|
||||
"Color theme for syntax highlighting.": {
|
||||
"Color theme for syntax highlighting.": ""
|
||||
},
|
||||
"Color theme for syntax highlighting. %1 themes available.": {
|
||||
"Color theme for syntax highlighting. %1 themes available.": ""
|
||||
},
|
||||
"Colorful mix of bright contrasting accents.": {
|
||||
"Colorful mix of bright contrasting accents.": "שילוב צבעוני של דגשים מנוגדים ובהירים."
|
||||
},
|
||||
@@ -812,6 +857,9 @@
|
||||
"Command": {
|
||||
"Command": "פקודה"
|
||||
},
|
||||
"Commands": {
|
||||
"Commands": ""
|
||||
},
|
||||
"Communication": {
|
||||
"Communication": "תקשורת"
|
||||
},
|
||||
@@ -905,6 +953,9 @@
|
||||
"Control currently playing media": {
|
||||
"Control currently playing media": "שלוט/שלטי במדיה שמתנגנת כעת"
|
||||
},
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": {
|
||||
"Control which plugins appear in 'All' mode without requiring a trigger prefix. Drag to reorder.": ""
|
||||
},
|
||||
"Control workspaces and columns by scrolling on the bar": {
|
||||
"Control workspaces and columns by scrolling on the bar": "שלוט/י בסביבות העבודה ועמודות באמצעות גלילה על הסרגל"
|
||||
},
|
||||
@@ -926,6 +977,9 @@
|
||||
"Copy Full Command": {
|
||||
"Copy Full Command": ""
|
||||
},
|
||||
"Copy HTML": {
|
||||
"Copy HTML": ""
|
||||
},
|
||||
"Copy Name": {
|
||||
"Copy Name": ""
|
||||
},
|
||||
@@ -935,6 +989,15 @@
|
||||
"Copy Process Name": {
|
||||
"Copy Process Name": "העתק/י שם תהליך"
|
||||
},
|
||||
"Copy Text": {
|
||||
"Copy Text": ""
|
||||
},
|
||||
"Copy URL": {
|
||||
"Copy URL": ""
|
||||
},
|
||||
"Copy path": {
|
||||
"Copy path": ""
|
||||
},
|
||||
"Corner Radius": {
|
||||
"Corner Radius": "רדיוס פינות"
|
||||
},
|
||||
@@ -1343,6 +1406,9 @@
|
||||
"Edge Spacing": {
|
||||
"Edge Spacing": "ריווח קצוות"
|
||||
},
|
||||
"Edit App": {
|
||||
"Edit App": ""
|
||||
},
|
||||
"Education": {
|
||||
"Education": "חינוך"
|
||||
},
|
||||
@@ -1391,6 +1457,9 @@
|
||||
"Enable loginctl lock integration": {
|
||||
"Enable loginctl lock integration": "הפעלת האינטגרציה עם loginctl"
|
||||
},
|
||||
"Enable media player controls on the lock screen window": {
|
||||
"Show Media Player": ""
|
||||
},
|
||||
"Enable password field display on the lock screen window": {
|
||||
"Show Password Field": "הצג/י שדה סיסמה"
|
||||
},
|
||||
@@ -1427,6 +1496,9 @@
|
||||
"Enter PIN for ": {
|
||||
"Enter PIN for ": "הזן/י קוד PIN עבור "
|
||||
},
|
||||
"Enter a new name for this workspace": {
|
||||
"Enter a new name for this workspace": ""
|
||||
},
|
||||
"Enter a search query": {
|
||||
"Enter a search query": "הזן/י שאילתת חיפוש"
|
||||
},
|
||||
@@ -1466,6 +1538,9 @@
|
||||
"Entry unpinned": {
|
||||
"Entry unpinned": ""
|
||||
},
|
||||
"Environment Variables": {
|
||||
"Environment Variables": ""
|
||||
},
|
||||
"Error": {
|
||||
"Error": "שגיאה"
|
||||
},
|
||||
@@ -1481,6 +1556,9 @@
|
||||
"Exponential": {
|
||||
"Exponential": "אקספוננציאלי"
|
||||
},
|
||||
"Extra Arguments": {
|
||||
"Extra Arguments": ""
|
||||
},
|
||||
"F1/I: Toggle • F10: Help": {
|
||||
"F1/I: Toggle • F10: Help": "F1/I: החלפה • F10: עזרה"
|
||||
},
|
||||
@@ -1670,6 +1748,9 @@
|
||||
"File Information": {
|
||||
"File Information": "פרטי קובץ"
|
||||
},
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": {
|
||||
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": ""
|
||||
},
|
||||
"Files": {
|
||||
"Files": "קבצים"
|
||||
},
|
||||
@@ -1868,6 +1949,9 @@
|
||||
"HSV": {
|
||||
"HSV": "HSV"
|
||||
},
|
||||
"HTML copied to clipboard": {
|
||||
"HTML copied to clipboard": ""
|
||||
},
|
||||
"Health": {
|
||||
"Health": "בריאות"
|
||||
},
|
||||
@@ -1886,9 +1970,18 @@
|
||||
"Hidden": {
|
||||
"Hidden": "מוסתר"
|
||||
},
|
||||
"Hidden Apps": {
|
||||
"Hidden Apps": ""
|
||||
},
|
||||
"Hidden Network": {
|
||||
"Hidden Network": "רשת מוסתרת"
|
||||
},
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": {
|
||||
"Hidden apps won't appear in the launcher. Right-click an app and select 'Hide App' to hide it.": ""
|
||||
},
|
||||
"Hide App": {
|
||||
"Hide App": ""
|
||||
},
|
||||
"Hide Delay": {
|
||||
"Hide Delay": "עיכוב הסתרה"
|
||||
},
|
||||
@@ -1970,6 +2063,9 @@
|
||||
"IP Address:": {
|
||||
"IP Address:": "כתובת IP:"
|
||||
},
|
||||
"Icon": {
|
||||
"Icon": ""
|
||||
},
|
||||
"Icon Size": {
|
||||
"Icon Size": "גודל סמל"
|
||||
},
|
||||
@@ -2009,6 +2105,9 @@
|
||||
"Include Transitions": {
|
||||
"Include Transitions": "כלול/כללי מעברים"
|
||||
},
|
||||
"Include desktop actions (shortcuts) in search results.": {
|
||||
"Include desktop actions (shortcuts) in search results.": ""
|
||||
},
|
||||
"Incompatible Plugins Loaded": {
|
||||
"Incompatible Plugins Loaded": "נטענו תוספים לא תואמים"
|
||||
},
|
||||
@@ -2114,11 +2213,18 @@
|
||||
"Failed to reject pairing": "",
|
||||
"Failed to ring device": "",
|
||||
"Failed to send clipboard": "",
|
||||
"Failed to send file": "",
|
||||
"Failed to send ping": "",
|
||||
"Failed to share": "",
|
||||
"Pairing failed": "",
|
||||
"Unpair failed": ""
|
||||
},
|
||||
"KDE Connect file browser title": {
|
||||
"Select File to Send": ""
|
||||
},
|
||||
"KDE Connect file send": {
|
||||
"Sending": ""
|
||||
},
|
||||
"KDE Connect file share notification": {
|
||||
"File received from": ""
|
||||
},
|
||||
@@ -2184,15 +2290,24 @@
|
||||
"KDE Connect ring tooltip": {
|
||||
"Ring": ""
|
||||
},
|
||||
"KDE Connect send file button": {
|
||||
"Send File": ""
|
||||
},
|
||||
"KDE Connect service unavailable message": {
|
||||
"KDE Connect unavailable": ""
|
||||
},
|
||||
"KDE Connect share URL button": {
|
||||
"Share URL": ""
|
||||
},
|
||||
"KDE Connect share button": {
|
||||
"Share Text": ""
|
||||
},
|
||||
"KDE Connect share button | KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share dialog title | KDE Connect share tooltip": {
|
||||
"Share": ""
|
||||
},
|
||||
"KDE Connect share input placeholder": {
|
||||
"Enter URL or text to share": ""
|
||||
},
|
||||
@@ -2265,6 +2380,12 @@
|
||||
"LED device": {
|
||||
"LED device": "התקן LED"
|
||||
},
|
||||
"Large": {
|
||||
"Large": ""
|
||||
},
|
||||
"Largest": {
|
||||
"Largest": ""
|
||||
},
|
||||
"Last launched %1": {
|
||||
"Last launched %1": "ההפעלה האחרונה הייתה %1"
|
||||
},
|
||||
@@ -2295,6 +2416,9 @@
|
||||
"Launcher": {
|
||||
"Launcher": "משגר"
|
||||
},
|
||||
"Launcher Button": {
|
||||
"Launcher Button": ""
|
||||
},
|
||||
"Launcher Button Logo": {
|
||||
"Launcher Button Logo": "סמל כפתור המשגר"
|
||||
},
|
||||
@@ -2334,6 +2458,9 @@
|
||||
"Loading plugins...": {
|
||||
"Loading plugins...": "טוען תוספים..."
|
||||
},
|
||||
"Loading trending...": {
|
||||
"Loading trending...": ""
|
||||
},
|
||||
"Loading...": {
|
||||
"Loading...": "טוען..."
|
||||
},
|
||||
@@ -2709,6 +2836,12 @@
|
||||
"No adapters": {
|
||||
"No adapters": "אין מתאמים"
|
||||
},
|
||||
"No app customizations.": {
|
||||
"No app customizations.": ""
|
||||
},
|
||||
"No apps found": {
|
||||
"No apps found": ""
|
||||
},
|
||||
"No battery": {
|
||||
"No battery": "אין סוללה"
|
||||
},
|
||||
@@ -2739,15 +2872,24 @@
|
||||
"No files found": {
|
||||
"No files found": "לא נמצאו קבצים"
|
||||
},
|
||||
"No hidden apps.": {
|
||||
"No hidden apps.": ""
|
||||
},
|
||||
"No items added yet": {
|
||||
"No items added yet": "לא נוספו פריטים עדיין"
|
||||
},
|
||||
"No keybinds found": {
|
||||
"No keybinds found": "לא נמצאו קיצורי מקלדת"
|
||||
},
|
||||
"No launcher plugins installed.": {
|
||||
"No launcher plugins installed.": ""
|
||||
},
|
||||
"No matches": {
|
||||
"No matches": "אין התאמות"
|
||||
},
|
||||
"No plugin results": {
|
||||
"No plugin results": ""
|
||||
},
|
||||
"No plugins found": {
|
||||
"No plugins found": "לא נמצאו תוספים"
|
||||
},
|
||||
@@ -2766,9 +2908,15 @@
|
||||
"No recent clipboard entries found": {
|
||||
"No recent clipboard entries found": ""
|
||||
},
|
||||
"No results found": {
|
||||
"No results found": ""
|
||||
},
|
||||
"No saved clipboard entries": {
|
||||
"No saved clipboard entries": ""
|
||||
},
|
||||
"No trigger": {
|
||||
"No trigger": ""
|
||||
},
|
||||
"No variants created. Click Add to create a new monitor widget.": {
|
||||
"No variants created. Click Add to create a new monitor widget.": "לא נוצרו גרסאות. לחץ/י על הוספה כדי ליצור ווידג׳ט תצוגה חדש."
|
||||
},
|
||||
@@ -2880,6 +3028,12 @@
|
||||
"Open Notepad File": {
|
||||
"Open Notepad File": "פתח/י קובץ בפנקס"
|
||||
},
|
||||
"Open folder": {
|
||||
"Open folder": ""
|
||||
},
|
||||
"Open in Browser": {
|
||||
"Open in Browser": ""
|
||||
},
|
||||
"Open search bar to find text": {
|
||||
"Open search bar to find text": "פתח/י שורת חיפוש כדי למצוא טקסט"
|
||||
},
|
||||
@@ -2946,6 +3100,9 @@
|
||||
"PIN": {
|
||||
"PIN": "קוד PIN"
|
||||
},
|
||||
"Pad Hours": {
|
||||
"Pad Hours": ""
|
||||
},
|
||||
"Padding": {
|
||||
"Padding": "ריווח פנימי"
|
||||
},
|
||||
@@ -3012,6 +3169,9 @@
|
||||
"Pinned": {
|
||||
"Pinned": "מוצמד"
|
||||
},
|
||||
"Pinned and running apps with drag-and-drop": {
|
||||
"Pinned and running apps with drag-and-drop": ""
|
||||
},
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": {
|
||||
"Place plugin directories here. Each plugin should have a plugin.json manifest file.": "מקם/י כאן את תיקיות התוספים. לכל תוסף צריך להיות קובץ מניפסט בשם plugin.json."
|
||||
},
|
||||
@@ -3048,6 +3208,9 @@
|
||||
"Plugin Management": {
|
||||
"Plugin Management": "ניהול תוספים"
|
||||
},
|
||||
"Plugin Visibility": {
|
||||
"Plugin Visibility": ""
|
||||
},
|
||||
"Plugin is disabled - enable in Plugins settings to use": {
|
||||
"Plugin is disabled - enable in Plugins settings to use": "התוסף מושבת – הפעל/י אותו בהגדרות התוספים כדי להשתמש בו"
|
||||
},
|
||||
@@ -3126,6 +3289,9 @@
|
||||
"Prevent screen timeout": {
|
||||
"Prevent screen timeout": "מנע/י כיבוי מסך אוטומטי"
|
||||
},
|
||||
"Preview": {
|
||||
"Preview": ""
|
||||
},
|
||||
"Primary": {
|
||||
"Primary": "ראשי"
|
||||
},
|
||||
@@ -3240,6 +3406,12 @@
|
||||
"Remove gaps and border when windows are maximized": {
|
||||
"Remove gaps and border when windows are maximized": "הסר/י רווחים ומסגרת כאשר חלונות ממוקסמים"
|
||||
},
|
||||
"Rename": {
|
||||
"Rename": ""
|
||||
},
|
||||
"Rename Workspace": {
|
||||
"Rename Workspace": ""
|
||||
},
|
||||
"Repeat": {
|
||||
"Repeat": "חזרה"
|
||||
},
|
||||
@@ -3417,6 +3589,12 @@
|
||||
"Scrolling": {
|
||||
"Scrolling": "גלילה"
|
||||
},
|
||||
"Search App Actions": {
|
||||
"Search App Actions": ""
|
||||
},
|
||||
"Search Options": {
|
||||
"Search Options": ""
|
||||
},
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": {
|
||||
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": "חפש/י לפי צירוף מקשים, תיאור או שם פעולה.\\n\\nפעולת ברירת המחדל מעתיקה את קיצור המקלדת ללוח ההעתקה.\\nלחץ/י לחיצה ימנית או לחץ/י על חץ ימינה כדי להצמיד קיצורים בשימוש תכוף - הם יופיעו בראש הרשימה כשלא מתבצע חיפוש."
|
||||
},
|
||||
@@ -3456,6 +3634,9 @@
|
||||
"Security": {
|
||||
"Security": "אבטחה"
|
||||
},
|
||||
"Select": {
|
||||
"Select": ""
|
||||
},
|
||||
"Select Application": {
|
||||
"Select Application": "בחר/י אפליקציה"
|
||||
},
|
||||
@@ -3534,12 +3715,18 @@
|
||||
"Setup": {
|
||||
"Setup": "התקנה"
|
||||
},
|
||||
"Share Gamma Control Settings": {
|
||||
"Share Gamma Control Settings": ""
|
||||
},
|
||||
"Shell": {
|
||||
"Shell": "מעטפת"
|
||||
},
|
||||
"Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Del: Clear All • Esc: Close": "Shift+Del: ניקוי הכל • Esc: סגירה"
|
||||
},
|
||||
"Shift+Enter to paste": {
|
||||
"Shift+Enter to paste": ""
|
||||
},
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": {
|
||||
"Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close": "Shift+Enter: הדבקה • Shift+Del: ניקוי הכל • Esc: סגירה"
|
||||
},
|
||||
@@ -3597,6 +3784,9 @@
|
||||
"Show Humidity": {
|
||||
"Show Humidity": "הצג/י לחות"
|
||||
},
|
||||
"Show Launcher Button": {
|
||||
"Show Launcher Button": ""
|
||||
},
|
||||
"Show Line Numbers": {
|
||||
"Show Line Numbers": "הצג/י מספרי שורות"
|
||||
},
|
||||
@@ -3774,6 +3964,9 @@
|
||||
"Sizing": {
|
||||
"Sizing": "גודל"
|
||||
},
|
||||
"Small": {
|
||||
"Small": ""
|
||||
},
|
||||
"Smartcard Authentication": {
|
||||
"Smartcard Authentication": "אימות עם כרטיס חכם"
|
||||
},
|
||||
@@ -4095,12 +4288,24 @@
|
||||
"Transparency": {
|
||||
"Transparency": "שקיפות"
|
||||
},
|
||||
"Trending GIFs": {
|
||||
"Trending GIFs": ""
|
||||
},
|
||||
"Trending Stickers": {
|
||||
"Trending Stickers": ""
|
||||
},
|
||||
"Trigger": {
|
||||
"Trigger": "מפעיל"
|
||||
},
|
||||
"Trigger Prefix": {
|
||||
"Trigger Prefix": "קידומת מפעיל"
|
||||
},
|
||||
"Trigger: %1": {
|
||||
"Trigger: %1": ""
|
||||
},
|
||||
"Try a different search": {
|
||||
"Try a different search": ""
|
||||
},
|
||||
"Turn off all displays immediately when the lock screen activates": {
|
||||
"Turn off all displays immediately when the lock screen activates": ""
|
||||
},
|
||||
@@ -4110,9 +4315,21 @@
|
||||
"Type": {
|
||||
"Type": "סוג"
|
||||
},
|
||||
"Type at least 2 characters": {
|
||||
"Type at least 2 characters": ""
|
||||
},
|
||||
"Type this prefix to search keybinds": {
|
||||
"Type this prefix to search keybinds": "הקלד/י קידומת זו לחיפוש קיצורי מקלדת"
|
||||
},
|
||||
"Type to search": {
|
||||
"Type to search": ""
|
||||
},
|
||||
"Type to search apps": {
|
||||
"Type to search apps": ""
|
||||
},
|
||||
"Type to search files": {
|
||||
"Type to search files": ""
|
||||
},
|
||||
"Typography": {
|
||||
"Typography": "טיפוגרפיה"
|
||||
},
|
||||
@@ -4230,6 +4447,9 @@
|
||||
"Use light theme instead of dark theme": {
|
||||
"Use light theme instead of dark theme": "השתמש/י בערכת נושא בהירה במקום כהה"
|
||||
},
|
||||
"Use meters per second instead of km/h for wind speed": {
|
||||
"Use meters per second instead of km/h for wind speed": ""
|
||||
},
|
||||
"Use smaller notification cards": {
|
||||
"Use smaller notification cards": "השתמש/י בכרטיסי התראות קטנים יותר"
|
||||
},
|
||||
@@ -4257,9 +4477,15 @@
|
||||
"Username": {
|
||||
"Username": "שם משתמש"
|
||||
},
|
||||
"Uses sunrise/sunset times based on your location.": {
|
||||
"Uses sunrise/sunset times based on your location.": ""
|
||||
},
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": {
|
||||
"Uses sunrise/sunset times to automatically adjust night mode based on your location.": "משתמש בזמני זריחה/שקיעה כדי לכוונן אוטומטית את מצב הלילה (גאמה) לפי המיקום שלך."
|
||||
},
|
||||
"Using shared settings from Gamma Control": {
|
||||
"Using shared settings from Gamma Control": ""
|
||||
},
|
||||
"Utilities": {
|
||||
"Utilities": "כלי עזר"
|
||||
},
|
||||
@@ -4460,6 +4686,9 @@
|
||||
"Wind Speed": {
|
||||
"Wind Speed": "מהירות הרוח"
|
||||
},
|
||||
"Wind Speed in m/s": {
|
||||
"Wind Speed in m/s": ""
|
||||
},
|
||||
"Window Corner Radius": {
|
||||
"Window Corner Radius": "רדיוס הפינות של החלון"
|
||||
},
|
||||
@@ -4493,6 +4722,9 @@
|
||||
"Workspace Switcher": {
|
||||
"Workspace Switcher": "מחליף סביבות העבודה"
|
||||
},
|
||||
"Workspace name": {
|
||||
"Workspace name": ""
|
||||
},
|
||||
"Workspaces": {
|
||||
"Workspaces": "סביבות עבודה"
|
||||
},
|
||||
@@ -4520,6 +4752,9 @@
|
||||
"You have unsaved changes. Save before opening a file?": {
|
||||
"You have unsaved changes. Save before opening a file?": "יש לך שינויים שלא נשמרו. האם ברצונך לשמור לפני פתיחת קובץ?"
|
||||
},
|
||||
"actions": {
|
||||
"actions": ""
|
||||
},
|
||||
"apps": {
|
||||
"apps": "אפליקציות"
|
||||
},
|
||||
@@ -4529,6 +4764,12 @@
|
||||
"bar shadow settings card": {
|
||||
"Shadow": "צל"
|
||||
},
|
||||
"border color": {
|
||||
"Color": ""
|
||||
},
|
||||
"border thickness": {
|
||||
"Thickness": ""
|
||||
},
|
||||
"browse themes button | theme browser header | theme browser window title": {
|
||||
"Browse Themes": "עיין/י בערכות נושא"
|
||||
},
|
||||
@@ -4786,6 +5027,21 @@
|
||||
"installed status": {
|
||||
"Installed": "מותקן"
|
||||
},
|
||||
"launcher appearance settings": {
|
||||
"Appearance": ""
|
||||
},
|
||||
"launcher border option": {
|
||||
"Border": ""
|
||||
},
|
||||
"launcher footer description": {
|
||||
"Show mode tabs and keyboard hints at the bottom.": ""
|
||||
},
|
||||
"launcher footer visibility": {
|
||||
"Show Footer": ""
|
||||
},
|
||||
"launcher size option": {
|
||||
"Size": ""
|
||||
},
|
||||
"leave empty for default": {
|
||||
"leave empty for default": "השאר/י ריק לברירת מחדל"
|
||||
},
|
||||
@@ -4826,6 +5082,9 @@
|
||||
"ms": {
|
||||
"ms": "מילישניות"
|
||||
},
|
||||
"nav": {
|
||||
"nav": ""
|
||||
},
|
||||
"no custom theme file status": {
|
||||
"No custom theme file": "אין קובץ ערכת נושא מותאמת אישית"
|
||||
},
|
||||
@@ -4882,6 +5141,12 @@
|
||||
"official": {
|
||||
"official": "רשמי"
|
||||
},
|
||||
"open": {
|
||||
"open": ""
|
||||
},
|
||||
"outline color": {
|
||||
"Outline": ""
|
||||
},
|
||||
"plugin browser description": {
|
||||
"Install plugins from the DMS plugin registry": "התקן/י תוספים ממאגר התוספים של DMS"
|
||||
},
|
||||
@@ -4897,6 +5162,9 @@
|
||||
"plugin search placeholder": {
|
||||
"Search plugins...": "חפש/י תוספים..."
|
||||
},
|
||||
"primary color": {
|
||||
"Primary": ""
|
||||
},
|
||||
"process count label in footer": {
|
||||
"Processes:": ""
|
||||
},
|
||||
@@ -4915,6 +5183,9 @@
|
||||
"registry theme description": {
|
||||
"Color theme from DMS registry": "ערכת צבעים מהמאגר של DMS"
|
||||
},
|
||||
"secondary color": {
|
||||
"Secondary": ""
|
||||
},
|
||||
"seconds": {
|
||||
"seconds": "שניות"
|
||||
},
|
||||
@@ -4928,6 +5199,9 @@
|
||||
"Surface": "משטח",
|
||||
"Text": "טקסט"
|
||||
},
|
||||
"shadow color option | text color": {
|
||||
"Text": ""
|
||||
},
|
||||
"shadow intensity slider": {
|
||||
"Intensity": "עוצמה"
|
||||
},
|
||||
@@ -5007,6 +5281,9 @@
|
||||
"wallpaper settings external management": {
|
||||
"External Wallpaper Management": "ניהול רקעים חיצוני"
|
||||
},
|
||||
"weather feels like temperature": {
|
||||
"Feels Like %1°": ""
|
||||
},
|
||||
"wtype not available - install wtype for paste support": {
|
||||
"wtype not available - install wtype for paste support": "wtype אינו זמין - התקן/י את wtype כדי לאפשר תמיכה בהדבקה"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user