mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 23:55:21 -04:00
Compare commits
6 Commits
hover
...
1df7e478df
| Author | SHA1 | Date | |
|---|---|---|---|
| 1df7e478df | |||
| 1fc4890857 | |||
| f5d52f1506 | |||
| 2026ba5bd2 | |||
| db56c8d74d | |||
| 9d1a81c93c |
@@ -19,7 +19,12 @@ var (
|
|||||||
var colorCmd = &cobra.Command{
|
var colorCmd = &cobra.Command{
|
||||||
Use: "color",
|
Use: "color",
|
||||||
Short: "Color utilities",
|
Short: "Color utilities",
|
||||||
Long: "Color utilities including picking colors from the screen",
|
Long: `Color utilities including picking colors from the screen.
|
||||||
|
|
||||||
|
This is the screen eyedropper CLI. To open the in-shell color modal, use:
|
||||||
|
dms ipc call color-picker toggle
|
||||||
|
|
||||||
|
See: https://danklinux.com/docs/dankmaterialshell/keybinds-ipc`,
|
||||||
}
|
}
|
||||||
|
|
||||||
var colorPickCmd = &cobra.Command{
|
var colorPickCmd = &cobra.Command{
|
||||||
@@ -29,6 +34,9 @@ var colorPickCmd = &cobra.Command{
|
|||||||
|
|
||||||
Click on any pixel to capture its color, or press Escape to cancel.
|
Click on any pixel to capture its color, or press Escape to cancel.
|
||||||
|
|
||||||
|
This is the screen eyedropper CLI. To open the in-shell color modal, use:
|
||||||
|
dms ipc call color-picker toggle
|
||||||
|
|
||||||
Output format flags (mutually exclusive, default: --hex):
|
Output format flags (mutually exclusive, default: --hex):
|
||||||
--hex - Hexadecimal (#RRGGBB)
|
--hex - Hexadecimal (#RRGGBB)
|
||||||
--rgb - RGB values (R G B)
|
--rgb - RGB values (R G B)
|
||||||
|
|||||||
@@ -77,10 +77,15 @@ var killCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ipcCmd = &cobra.Command{
|
var ipcCmd = &cobra.Command{
|
||||||
Use: "ipc [target] [function] [args...]",
|
Use: "ipc",
|
||||||
Short: "Send IPC commands to running DMS shell",
|
Short: "Send IPC commands to running DMS shell",
|
||||||
|
Long: `Send IPC commands to the running DMS shell.
|
||||||
|
|
||||||
|
dms ipc call <target> <function> [args...] invoke a command
|
||||||
|
dms ipc list list all targets and functions
|
||||||
|
|
||||||
|
Full reference: https://danklinux.com/docs/dankmaterialshell/keybinds-ipc`,
|
||||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
_ = findConfig(cmd, args)
|
|
||||||
return getShellIPCCompletions(args, toComplete), cobra.ShellCompDirectiveNoFileComp
|
return getShellIPCCompletions(args, toComplete), cobra.ShellCompDirectiveNoFileComp
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
@@ -88,9 +93,17 @@ var ipcCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipcListCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List all IPC targets and functions",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
printIPCHelp()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
ipcCmd.AddCommand(ipcListCmd)
|
||||||
ipcCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
|
ipcCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
|
||||||
_ = findConfig(cmd, args)
|
|
||||||
printIPCHelp()
|
printIPCHelp()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-24
@@ -601,12 +601,30 @@ func parseTargetsFromIPCShowOutput(output string) ipcTargets {
|
|||||||
return targets
|
return targets
|
||||||
}
|
}
|
||||||
|
|
||||||
func getShellIPCCompletions(args []string, _ string) []string {
|
func buildQsIPCBaseArgs() ([]string, error) {
|
||||||
cmdArgs := []string{"ipc"}
|
cmdArgs := []string{"ipc"}
|
||||||
|
switch pid, ok := getFirstDMSPID(); {
|
||||||
|
case ok:
|
||||||
|
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
|
||||||
|
default:
|
||||||
|
if err := findConfig(nil, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if qsHasAnyDisplay() {
|
if qsHasAnyDisplay() {
|
||||||
cmdArgs = append(cmdArgs, "--any-display")
|
cmdArgs = append(cmdArgs, "--any-display")
|
||||||
}
|
}
|
||||||
cmdArgs = append(cmdArgs, "-p", configPath, "show")
|
cmdArgs = append(cmdArgs, "-p", configPath)
|
||||||
|
}
|
||||||
|
return cmdArgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getShellIPCCompletions(args []string, _ string) []string {
|
||||||
|
baseArgs, err := buildQsIPCBaseArgs()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error building IPC args for completions: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cmdArgs := append(baseArgs, "show")
|
||||||
cmd := exec.Command("qs", cmdArgs...)
|
cmd := exec.Command("qs", cmdArgs...)
|
||||||
var targets ipcTargets
|
var targets ipcTargets
|
||||||
|
|
||||||
@@ -623,7 +641,7 @@ func getShellIPCCompletions(args []string, _ string) []string {
|
|||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
targetNames := make([]string, 0)
|
targetNames := make([]string, 0)
|
||||||
targetNames = append(targetNames, "call")
|
targetNames = append(targetNames, "call", "list")
|
||||||
for k := range targets {
|
for k := range targets {
|
||||||
targetNames = append(targetNames, k)
|
targetNames = append(targetNames, k)
|
||||||
}
|
}
|
||||||
@@ -696,23 +714,11 @@ func runShellIPCCommand(args []string) {
|
|||||||
args = append([]string{"call"}, args...)
|
args = append([]string{"call"}, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdArgs := []string{"ipc"}
|
baseArgs, err := buildQsIPCBaseArgs()
|
||||||
|
if err != nil {
|
||||||
switch pid, ok := getFirstDMSPID(); {
|
|
||||||
case ok:
|
|
||||||
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
|
|
||||||
default:
|
|
||||||
if err := findConfig(nil, nil); err != nil {
|
|
||||||
log.Fatalf("Error finding config: %v", err)
|
log.Fatalf("Error finding config: %v", err)
|
||||||
}
|
}
|
||||||
// ! TODO - remove check when QS 0.3 is released
|
cmdArgs := append(baseArgs, args...)
|
||||||
if qsHasAnyDisplay() {
|
|
||||||
cmdArgs = append(cmdArgs, "--any-display")
|
|
||||||
}
|
|
||||||
cmdArgs = append(cmdArgs, "-p", configPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdArgs = append(cmdArgs, args...)
|
|
||||||
cmd := exec.Command("qs", cmdArgs...)
|
cmd := exec.Command("qs", cmdArgs...)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@@ -724,19 +730,20 @@ func runShellIPCCommand(args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printIPCHelp() {
|
func printIPCHelp() {
|
||||||
fmt.Println("Usage: dms ipc <target> <function> [args...]")
|
fmt.Println("Usage: dms ipc call <target> <function> [args...]")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
cmdArgs := []string{"ipc"}
|
baseArgs, err := buildQsIPCBaseArgs()
|
||||||
if qsHasAnyDisplay() {
|
if err != nil {
|
||||||
cmdArgs = append(cmdArgs, "--any-display")
|
printIPCHelpFailure(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
cmdArgs = append(cmdArgs, "-p", configPath, "show")
|
cmdArgs := append(baseArgs, "show")
|
||||||
cmd := exec.Command("qs", cmdArgs...)
|
cmd := exec.Command("qs", cmdArgs...)
|
||||||
|
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Could not retrieve available IPC targets (is DMS running?)")
|
printIPCHelpFailure(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,6 +772,16 @@ func printIPCHelp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printIPCHelpFailure(err error) {
|
||||||
|
fmt.Println("Could not retrieve IPC targets.")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" %v\n", err)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(" Full docs: https://danklinux.com/docs/dankmaterialshell/keybinds-ipc")
|
||||||
|
fmt.Println(" Try: dms ipc call <target> <function>")
|
||||||
|
}
|
||||||
|
|
||||||
// ensureFontCache rebuilds the fontconfig cache if user-configured fonts are missing while skipping defaults
|
// ensureFontCache rebuilds the fontconfig cache if user-configured fonts are missing while skipping defaults
|
||||||
func ensureFontCache() {
|
func ensureFontCache() {
|
||||||
if _, err := exec.LookPath("fc-list"); err != nil {
|
if _, err := exec.LookPath("fc-list"); err != nil {
|
||||||
|
|||||||
+21
-1
@@ -6,6 +6,18 @@ DankMaterialShell provides comprehensive IPC (Inter-Process Communication) funct
|
|||||||
dms ipc call <target> <function> [parameters...]
|
dms ipc call <target> <function> [parameters...]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Discovering IPC commands
|
||||||
|
|
||||||
|
List all available targets and functions while DMS is running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dms ipc list
|
||||||
|
dms ipc # same
|
||||||
|
dms ipc --help # same, plus usage text
|
||||||
|
```
|
||||||
|
|
||||||
|
Live listing requires DMS to be running. If listing fails, use this document or the [Keybinds & IPC docs](https://danklinux.com/docs/dankmaterialshell/keybinds-ipc) as an offline reference.
|
||||||
|
|
||||||
## Target: `audio`
|
## Target: `audio`
|
||||||
|
|
||||||
Audio system control and information.
|
Audio system control and information.
|
||||||
@@ -707,7 +719,7 @@ File browser controls for selecting wallpapers and profile images.
|
|||||||
- Both browsers support common image formats (jpg, jpeg, png, bmp, gif, webp)
|
- Both browsers support common image formats (jpg, jpeg, png, bmp, gif, webp)
|
||||||
|
|
||||||
### Target: `color-picker`
|
### Target: `color-picker`
|
||||||
Color picker modal control.
|
In-shell color picker modal for theme and settings color selection.
|
||||||
|
|
||||||
**Functions:**
|
**Functions:**
|
||||||
- `open` - Show color picker modal
|
- `open` - Show color picker modal
|
||||||
@@ -718,6 +730,14 @@ Color picker modal control.
|
|||||||
- `toggle` - Toggle color picker modal visibility
|
- `toggle` - Toggle color picker modal visibility
|
||||||
- `toggleInstant` - Toggle color picker modal visibility without animation on hide
|
- `toggleInstant` - Toggle color picker modal visibility without animation on hide
|
||||||
|
|
||||||
|
**Note:** This controls the in-shell modal. To pick a pixel from the screen via CLI, use `dms color pick` instead (see [Color Picker CLI](https://danklinux.com/docs/dankmaterialshell/cli-color-picker)).
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
dms ipc call color-picker toggle
|
||||||
|
dms ipc call color-picker openColor "#3f51b5"
|
||||||
|
```
|
||||||
|
|
||||||
### Target: `hypr`
|
### Target: `hypr`
|
||||||
Hyprland-specific controls including keybinds cheatsheet and workspace overview (Hyprland only).
|
Hyprland-specific controls including keybinds cheatsheet and workspace overview (Hyprland only).
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ const DMS_ACTIONS = [
|
|||||||
{ id: "spawn dms ipc call dankdash wallpaper", label: "Wallpaper Browser" },
|
{ id: "spawn dms ipc call dankdash wallpaper", label: "Wallpaper Browser" },
|
||||||
{ id: "spawn dms ipc call file browse wallpaper", label: "File: Browse Wallpaper" },
|
{ id: "spawn dms ipc call file browse wallpaper", label: "File: Browse Wallpaper" },
|
||||||
{ id: "spawn dms ipc call file browse profile", label: "File: Browse Profile" },
|
{ id: "spawn dms ipc call file browse profile", label: "File: Browse Profile" },
|
||||||
|
{ id: "spawn dms ipc call color-picker toggle", label: "Color Picker: Toggle" },
|
||||||
|
{ id: "spawn dms ipc call color-picker open", label: "Color Picker: Open" },
|
||||||
|
{ id: "spawn dms ipc call color-picker close", label: "Color Picker: Close" },
|
||||||
{ id: "spawn dms ipc call keybinds toggle niri", label: "Keybinds Cheatsheet: Toggle", compositor: "niri" },
|
{ id: "spawn dms ipc call keybinds toggle niri", label: "Keybinds Cheatsheet: Toggle", compositor: "niri" },
|
||||||
{ id: "spawn dms ipc call keybinds open niri", label: "Keybinds Cheatsheet: Open", compositor: "niri" },
|
{ id: "spawn dms ipc call keybinds open niri", label: "Keybinds Cheatsheet: Open", compositor: "niri" },
|
||||||
{ id: "spawn dms ipc call keybinds close", label: "Keybinds Cheatsheet: Close" },
|
{ id: "spawn dms ipc call keybinds close", label: "Keybinds Cheatsheet: Close" },
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ Singleton {
|
|||||||
property bool audioVisualizerEnabled: true
|
property bool audioVisualizerEnabled: true
|
||||||
property string audioScrollMode: "volume"
|
property string audioScrollMode: "volume"
|
||||||
property int audioWheelScrollAmount: 5
|
property int audioWheelScrollAmount: 5
|
||||||
|
property bool audioDeviceScrollVolumeEnabled: false
|
||||||
property bool clockCompactMode: false
|
property bool clockCompactMode: false
|
||||||
property int focusedWindowSize: 1
|
property int focusedWindowSize: 1
|
||||||
property bool focusedWindowCompactMode: false
|
property bool focusedWindowCompactMode: false
|
||||||
@@ -404,6 +405,9 @@ Singleton {
|
|||||||
property int barMaxVisibleApps: 0
|
property int barMaxVisibleApps: 0
|
||||||
property int barMaxVisibleRunningApps: 0
|
property int barMaxVisibleRunningApps: 0
|
||||||
property bool barShowOverflowBadge: true
|
property bool barShowOverflowBadge: true
|
||||||
|
property bool trayAutoOverflow: true
|
||||||
|
property bool trayPopupSingleLine: true
|
||||||
|
property int trayMaxVisibleItems: 0
|
||||||
property bool appsDockHideIndicators: false
|
property bool appsDockHideIndicators: false
|
||||||
property bool appsDockColorizeActive: false
|
property bool appsDockColorizeActive: false
|
||||||
property string appsDockActiveColorMode: "primary"
|
property string appsDockActiveColorMode: "primary"
|
||||||
@@ -519,13 +523,39 @@ Singleton {
|
|||||||
property real notificationSummaryFontSize: Spec.SPEC.notificationSummaryFontSize.def
|
property real notificationSummaryFontSize: Spec.SPEC.notificationSummaryFontSize.def
|
||||||
property real notificationBodyFontSize: Spec.SPEC.notificationBodyFontSize.def
|
property real notificationBodyFontSize: Spec.SPEC.notificationBodyFontSize.def
|
||||||
property bool notepadShowLineNumbers: false
|
property bool notepadShowLineNumbers: false
|
||||||
|
property bool notepadAutoSave: false
|
||||||
|
property string notepadSlideoutSide: "right"
|
||||||
|
property string notepadDefaultMode: "slideout"
|
||||||
property real notepadTransparencyOverride: -1
|
property real notepadTransparencyOverride: -1
|
||||||
property real notepadLastCustomTransparency: 0.7
|
property real notepadLastCustomTransparency: 0.7
|
||||||
|
property bool notepadUseCompositorGap: false
|
||||||
|
property int notepadEdgeGap: 0
|
||||||
|
|
||||||
|
// Compositor layout gap when enabled and available, else the manual value.
|
||||||
|
readonly property int notepadEffectiveEdgeGap: {
|
||||||
|
if (notepadUseCompositorGap) {
|
||||||
|
var g = -1;
|
||||||
|
if (CompositorService.isNiri)
|
||||||
|
g = niriLayoutGapsOverride;
|
||||||
|
else if (CompositorService.isHyprland)
|
||||||
|
g = hyprlandLayoutGapsOverride;
|
||||||
|
else if (CompositorService.isMango)
|
||||||
|
g = mangoLayoutGapsOverride;
|
||||||
|
if (g >= 0)
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
return Math.max(0, notepadEdgeGap);
|
||||||
|
}
|
||||||
|
|
||||||
onNotepadUseMonospaceChanged: saveSettings()
|
onNotepadUseMonospaceChanged: saveSettings()
|
||||||
onNotepadFontFamilyChanged: saveSettings()
|
onNotepadFontFamilyChanged: saveSettings()
|
||||||
onNotepadFontSizeChanged: saveSettings()
|
onNotepadFontSizeChanged: saveSettings()
|
||||||
onNotepadShowLineNumbersChanged: saveSettings()
|
onNotepadShowLineNumbersChanged: saveSettings()
|
||||||
|
onNotepadAutoSaveChanged: saveSettings()
|
||||||
|
onNotepadSlideoutSideChanged: saveSettings()
|
||||||
|
onNotepadDefaultModeChanged: saveSettings()
|
||||||
|
onNotepadUseCompositorGapChanged: saveSettings()
|
||||||
|
onNotepadEdgeGapChanged: saveSettings()
|
||||||
// onCenteringModeChanged: saveSettings()
|
// onCenteringModeChanged: saveSettings()
|
||||||
onNotepadTransparencyOverrideChanged: {
|
onNotepadTransparencyOverrideChanged: {
|
||||||
if (notepadTransparencyOverride > 0) {
|
if (notepadTransparencyOverride > 0) {
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ var SPEC = {
|
|||||||
audioVisualizerEnabled: { def: true },
|
audioVisualizerEnabled: { def: true },
|
||||||
audioScrollMode: { def: "volume" },
|
audioScrollMode: { def: "volume" },
|
||||||
audioWheelScrollAmount: { def: 5 },
|
audioWheelScrollAmount: { def: 5 },
|
||||||
|
audioDeviceScrollVolumeEnabled: { def: false },
|
||||||
clockCompactMode: { def: false },
|
clockCompactMode: { def: false },
|
||||||
focusedWindowCompactMode: { def: false },
|
focusedWindowCompactMode: { def: false },
|
||||||
focusedWindowSize: { def: 1 },
|
focusedWindowSize: { def: 1 },
|
||||||
@@ -163,6 +164,9 @@ var SPEC = {
|
|||||||
barMaxVisibleApps: { def: 0 },
|
barMaxVisibleApps: { def: 0 },
|
||||||
barMaxVisibleRunningApps: { def: 0 },
|
barMaxVisibleRunningApps: { def: 0 },
|
||||||
barShowOverflowBadge: { def: true },
|
barShowOverflowBadge: { def: true },
|
||||||
|
trayAutoOverflow: { def: true },
|
||||||
|
trayPopupSingleLine: { def: true },
|
||||||
|
trayMaxVisibleItems: { def: 0 },
|
||||||
appsDockHideIndicators: { def: false },
|
appsDockHideIndicators: { def: false },
|
||||||
appsDockColorizeActive: { def: false },
|
appsDockColorizeActive: { def: false },
|
||||||
appsDockActiveColorMode: { def: "primary" },
|
appsDockActiveColorMode: { def: "primary" },
|
||||||
@@ -263,8 +267,13 @@ var SPEC = {
|
|||||||
notificationSummaryFontSize: { def: 0 },
|
notificationSummaryFontSize: { def: 0 },
|
||||||
notificationBodyFontSize: { def: 0 },
|
notificationBodyFontSize: { def: 0 },
|
||||||
notepadShowLineNumbers: { def: false },
|
notepadShowLineNumbers: { def: false },
|
||||||
|
notepadAutoSave: { def: false },
|
||||||
|
notepadSlideoutSide: { def: "right" },
|
||||||
|
notepadDefaultMode: { def: "slideout" },
|
||||||
notepadTransparencyOverride: { def: -1 },
|
notepadTransparencyOverride: { def: -1 },
|
||||||
notepadLastCustomTransparency: { def: 0.7 },
|
notepadLastCustomTransparency: { def: 0.7 },
|
||||||
|
notepadUseCompositorGap: { def: false },
|
||||||
|
notepadEdgeGap: { def: 0 },
|
||||||
|
|
||||||
soundsEnabled: { def: true },
|
soundsEnabled: { def: true },
|
||||||
useSystemSoundTheme: { def: false },
|
useSystemSoundTheme: { def: false },
|
||||||
|
|||||||
@@ -1093,11 +1093,22 @@ Item {
|
|||||||
slideoutWidth: 480
|
slideoutWidth: 480
|
||||||
expandable: true
|
expandable: true
|
||||||
expandedWidthValue: 960
|
expandedWidthValue: 960
|
||||||
|
edgeGap: SettingsData.notepadEffectiveEdgeGap
|
||||||
|
slideEdge: SettingsData.notepadSlideoutSide
|
||||||
|
|
||||||
|
onIsVisibleChanged: {
|
||||||
|
if (isVisible)
|
||||||
|
PopoutService.notepadPopout?.hide();
|
||||||
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Notepad {
|
Notepad {
|
||||||
slideout: notepadSlideout
|
slideout: notepadSlideout
|
||||||
onHideRequested: notepadSlideout.hide()
|
onHideRequested: notepadSlideout.hide()
|
||||||
|
onPopoutRequested: {
|
||||||
|
notepadSlideout.hide();
|
||||||
|
PopoutService.openNotepadPopout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,6 +1125,24 @@ Item {
|
|||||||
Component.onCompleted: PopoutService.notepadSlideouts = instances
|
Component.onCompleted: PopoutService.notepadSlideouts = instances
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notepadPopoutLoader
|
||||||
|
active: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.notepadPopoutLoader = notepadPopoutLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item) {
|
||||||
|
PopoutService.notepadPopout = item;
|
||||||
|
PopoutService._onNotepadPopoutLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotepadPopoutWindow {}
|
||||||
|
}
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: powerMenuModalLoader
|
id: powerMenuModalLoader
|
||||||
|
|
||||||
|
|||||||
@@ -373,6 +373,10 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function open(): string {
|
function open(): string {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
PopoutService.openNotepadPopout();
|
||||||
|
return "NOTEPAD_OPEN_SUCCESS";
|
||||||
|
}
|
||||||
var instance = getActiveNotepadInstance();
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.show();
|
instance.show();
|
||||||
@@ -382,6 +386,10 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
PopoutService.notepadPopout?.hide();
|
||||||
|
return "NOTEPAD_CLOSE_SUCCESS";
|
||||||
|
}
|
||||||
var instance = getActiveNotepadInstance();
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.hide();
|
instance.hide();
|
||||||
@@ -391,6 +399,10 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
PopoutService.toggleNotepadPopout();
|
||||||
|
return "NOTEPAD_TOGGLE_SUCCESS";
|
||||||
|
}
|
||||||
var instance = getActiveNotepadInstance();
|
var instance = getActiveNotepadInstance();
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.toggle();
|
instance.toggle();
|
||||||
|
|||||||
@@ -201,6 +201,21 @@ FocusScope {
|
|||||||
keyboardSelectionRequested = true;
|
keyboardSelectionRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function activateFile(path, name, isDir) {
|
||||||
|
if (isDir) {
|
||||||
|
navigateTo(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (saveMode) {
|
||||||
|
saveRow.fileName = name;
|
||||||
|
pendingFilePath = path;
|
||||||
|
showOverwriteConfirmation = true;
|
||||||
|
} else {
|
||||||
|
fileSelected(path);
|
||||||
|
closeRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleSaveFile(filePath) {
|
function handleSaveFile(filePath) {
|
||||||
var normalizedPath = filePath;
|
var normalizedPath = filePath;
|
||||||
if (!normalizedPath.startsWith("file://")) {
|
if (!normalizedPath.startsWith("file://")) {
|
||||||
@@ -652,6 +667,7 @@ FocusScope {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.bottomMargin: root.saveMode ? 40 + Theme.spacingL * 2 : 0
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -756,12 +772,7 @@ FocusScope {
|
|||||||
onItemClicked: (index, path, name, isDir) => {
|
onItemClicked: (index, path, name, isDir) => {
|
||||||
selectedIndex = index;
|
selectedIndex = index;
|
||||||
setSelectedFileData(path, name, isDir);
|
setSelectedFileData(path, name, isDir);
|
||||||
if (isDir) {
|
root.activateFile(path, name, isDir);
|
||||||
navigateTo(path);
|
|
||||||
} else {
|
|
||||||
fileSelected(path);
|
|
||||||
root.closeRequested();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onItemSelected: (index, path, name, isDir) => {
|
onItemSelected: (index, path, name, isDir) => {
|
||||||
setSelectedFileData(path, name, isDir);
|
setSelectedFileData(path, name, isDir);
|
||||||
@@ -776,12 +787,7 @@ FocusScope {
|
|||||||
root.keyboardSelectionRequested = false;
|
root.keyboardSelectionRequested = false;
|
||||||
selectedIndex = index;
|
selectedIndex = index;
|
||||||
setSelectedFileData(filePath, fileName, fileIsDir);
|
setSelectedFileData(filePath, fileName, fileIsDir);
|
||||||
if (fileIsDir) {
|
root.activateFile(filePath, fileName, fileIsDir);
|
||||||
navigateTo(filePath);
|
|
||||||
} else {
|
|
||||||
fileSelected(filePath);
|
|
||||||
root.closeRequested();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,12 +823,7 @@ FocusScope {
|
|||||||
onItemClicked: (index, path, name, isDir) => {
|
onItemClicked: (index, path, name, isDir) => {
|
||||||
selectedIndex = index;
|
selectedIndex = index;
|
||||||
setSelectedFileData(path, name, isDir);
|
setSelectedFileData(path, name, isDir);
|
||||||
if (isDir) {
|
root.activateFile(path, name, isDir);
|
||||||
navigateTo(path);
|
|
||||||
} else {
|
|
||||||
fileSelected(path);
|
|
||||||
root.closeRequested();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onItemSelected: (index, path, name, isDir) => {
|
onItemSelected: (index, path, name, isDir) => {
|
||||||
setSelectedFileData(path, name, isDir);
|
setSelectedFileData(path, name, isDir);
|
||||||
@@ -837,12 +838,7 @@ FocusScope {
|
|||||||
root.keyboardSelectionRequested = false;
|
root.keyboardSelectionRequested = false;
|
||||||
selectedIndex = index;
|
selectedIndex = index;
|
||||||
setSelectedFileData(filePath, fileName, fileIsDir);
|
setSelectedFileData(filePath, fileName, fileIsDir);
|
||||||
if (fileIsDir) {
|
root.activateFile(filePath, fileName, fileIsDir);
|
||||||
navigateTo(filePath);
|
|
||||||
} else {
|
|
||||||
fileSelected(filePath);
|
|
||||||
root.closeRequested();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,6 +851,7 @@ FocusScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileBrowserSaveRow {
|
FileBrowserSaveRow {
|
||||||
|
id: saveRow
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -913,6 +910,7 @@ FocusScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileBrowserOverwriteDialog {
|
FileBrowserOverwriteDialog {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -929,7 +927,6 @@ FocusScope {
|
|||||||
pendingFilePath = "";
|
pendingFilePath = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FileBrowserItemContextMenu {
|
FileBrowserItemContextMenu {
|
||||||
id: itemContextMenu
|
id: itemContextMenu
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Item {
|
|||||||
width: 80
|
width: 80
|
||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: cancelArea.containsMouse ? Theme.surfaceVariantHover : Theme.surfaceVariant
|
color: cancelArea.containsMouse ? Qt.lighter(Theme.surfaceVariant, 1.2) : Theme.surfaceVariant
|
||||||
border.color: Theme.outline
|
border.color: Theme.outline
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ Row {
|
|||||||
property bool saveMode: false
|
property bool saveMode: false
|
||||||
property string defaultFileName: ""
|
property string defaultFileName: ""
|
||||||
property string currentPath: ""
|
property string currentPath: ""
|
||||||
|
property alias fileName: fileNameInput.text
|
||||||
|
|
||||||
signal saveRequested(string filePath)
|
signal saveRequested(string filePath)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Item {
|
|||||||
property real barSpacing: 4
|
property real barSpacing: 4
|
||||||
property var barConfig: null
|
property var barConfig: null
|
||||||
property var blurBarWindow: null
|
property var blurBarWindow: null
|
||||||
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool overrideAxisLayout: false
|
property bool overrideAxisLayout: false
|
||||||
property bool forceVerticalLayout: false
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
@@ -359,6 +360,7 @@ Item {
|
|||||||
barSpacing: root.barSpacing
|
barSpacing: root.barSpacing
|
||||||
barConfig: root.barConfig
|
barConfig: root.barConfig
|
||||||
blurBarWindow: root.blurBarWindow
|
blurBarWindow: root.blurBarWindow
|
||||||
|
sectionAvailablePrimarySize: root.sectionAvailablePrimarySize
|
||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === centerRepeater.count - 1
|
isLast: index === centerRepeater.count - 1
|
||||||
sectionSpacing: parent.itemSpacing
|
sectionSpacing: parent.itemSpacing
|
||||||
|
|||||||
@@ -497,6 +497,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, hCenterSection.x > 0 ? hCenterSection.x : parent.width / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
@@ -529,6 +530,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, hCenterSection.x > 0 ? parent.width - (hCenterSection.x + hCenterSection.width) : parent.width / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
@@ -561,6 +563,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, hRightSection.x > 0 ? hRightSection.x - (hLeftSection.x + hLeftSection.width) : parent.width / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
@@ -600,6 +603,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, vCenterSection.y > 0 ? vCenterSection.y : parent.height / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
@@ -633,6 +637,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, vRightSection.y > 0 ? vRightSection.y - (vLeftSection.y + vLeftSection.height) : parent.height / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
@@ -667,6 +672,7 @@ Item {
|
|||||||
widgetThickness: barWindow.widgetThickness
|
widgetThickness: barWindow.widgetThickness
|
||||||
barThickness: barWindow.effectiveBarThickness
|
barThickness: barWindow.effectiveBarThickness
|
||||||
barSpacing: barConfig?.spacing ?? 4
|
barSpacing: barConfig?.spacing ?? 4
|
||||||
|
sectionAvailablePrimarySize: Math.max(1, vCenterSection.y > 0 ? parent.height - (vCenterSection.y + vCenterSection.height) : parent.height / 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Item {
|
|||||||
property real barSpacing: 4
|
property real barSpacing: 4
|
||||||
property var barConfig: null
|
property var barConfig: null
|
||||||
property var blurBarWindow: null
|
property var blurBarWindow: null
|
||||||
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool overrideAxisLayout: false
|
property bool overrideAxisLayout: false
|
||||||
property bool forceVerticalLayout: false
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ Item {
|
|||||||
barSpacing: root.barSpacing
|
barSpacing: root.barSpacing
|
||||||
barConfig: root.barConfig
|
barConfig: root.barConfig
|
||||||
blurBarWindow: root.blurBarWindow
|
blurBarWindow: root.blurBarWindow
|
||||||
|
sectionAvailablePrimarySize: root.sectionAvailablePrimarySize
|
||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === rowRepeater.count - 1
|
isLast: index === rowRepeater.count - 1
|
||||||
sectionSpacing: parent.rowSpacing
|
sectionSpacing: parent.rowSpacing
|
||||||
@@ -106,6 +108,7 @@ Item {
|
|||||||
barSpacing: root.barSpacing
|
barSpacing: root.barSpacing
|
||||||
barConfig: root.barConfig
|
barConfig: root.barConfig
|
||||||
blurBarWindow: root.blurBarWindow
|
blurBarWindow: root.blurBarWindow
|
||||||
|
sectionAvailablePrimarySize: root.sectionAvailablePrimarySize
|
||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === columnRepeater.count - 1
|
isLast: index === columnRepeater.count - 1
|
||||||
sectionSpacing: parent.columnSpacing
|
sectionSpacing: parent.columnSpacing
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Item {
|
|||||||
property real barSpacing: 4
|
property real barSpacing: 4
|
||||||
property var barConfig: null
|
property var barConfig: null
|
||||||
property var blurBarWindow: null
|
property var blurBarWindow: null
|
||||||
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool overrideAxisLayout: false
|
property bool overrideAxisLayout: false
|
||||||
property bool forceVerticalLayout: false
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ Item {
|
|||||||
barSpacing: root.barSpacing
|
barSpacing: root.barSpacing
|
||||||
barConfig: root.barConfig
|
barConfig: root.barConfig
|
||||||
blurBarWindow: root.blurBarWindow
|
blurBarWindow: root.blurBarWindow
|
||||||
|
sectionAvailablePrimarySize: root.sectionAvailablePrimarySize
|
||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === rowRepeater.count - 1
|
isLast: index === rowRepeater.count - 1
|
||||||
sectionSpacing: parent.rowSpacing
|
sectionSpacing: parent.rowSpacing
|
||||||
@@ -108,6 +110,7 @@ Item {
|
|||||||
barSpacing: root.barSpacing
|
barSpacing: root.barSpacing
|
||||||
barConfig: root.barConfig
|
barConfig: root.barConfig
|
||||||
blurBarWindow: root.blurBarWindow
|
blurBarWindow: root.blurBarWindow
|
||||||
|
sectionAvailablePrimarySize: root.sectionAvailablePrimarySize
|
||||||
isFirst: index === 0
|
isFirst: index === 0
|
||||||
isLast: index === columnRepeater.count - 1
|
isLast: index === columnRepeater.count - 1
|
||||||
sectionSpacing: parent.columnSpacing
|
sectionSpacing: parent.columnSpacing
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Loader {
|
|||||||
property real barSpacing: 4
|
property real barSpacing: 4
|
||||||
property var barConfig: null
|
property var barConfig: null
|
||||||
property var blurBarWindow: null
|
property var blurBarWindow: null
|
||||||
|
property real sectionAvailablePrimarySize: 0
|
||||||
property bool isFirst: false
|
property bool isFirst: false
|
||||||
property bool isLast: false
|
property bool isLast: false
|
||||||
property real sectionSpacing: 0
|
property real sectionSpacing: 0
|
||||||
@@ -141,6 +142,14 @@ Loader {
|
|||||||
restoreMode: Binding.RestoreNone
|
restoreMode: Binding.RestoreNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "sectionAvailablePrimarySize" in root.item
|
||||||
|
property: "sectionAvailablePrimarySize"
|
||||||
|
value: root.sectionAvailablePrimarySize
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: root.item
|
target: root.item
|
||||||
when: root.item && "isLeftBarEdge" in root.item
|
when: root.item && "isLeftBarEdge" in root.item
|
||||||
|
|||||||
@@ -32,9 +32,20 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property var notepadInstance: resolveNotepadInstance()
|
readonly property var notepadInstance: resolveNotepadInstance()
|
||||||
readonly property bool isActive: notepadInstance?.isVisible ?? false
|
readonly property bool popoutDefault: SettingsData.notepadDefaultMode === "popout"
|
||||||
|
readonly property bool isActive: popoutDefault ? (PopoutService.notepadPopout?.visible ?? false) : (notepadInstance?.isVisible ?? false)
|
||||||
property bool isAutoHideBar: false
|
property bool isAutoHideBar: false
|
||||||
|
|
||||||
|
function showActiveSurface() {
|
||||||
|
if (root.popoutDefault) {
|
||||||
|
PopoutService.openNotepadPopout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const instance = prepareNotepadInstance(root.notepadInstance);
|
||||||
|
if (instance && typeof instance.show === "function")
|
||||||
|
instance.show();
|
||||||
|
}
|
||||||
|
|
||||||
function prepareNotepadInstance(instance) {
|
function prepareNotepadInstance(instance) {
|
||||||
if (instance)
|
if (instance)
|
||||||
instance.triggerUsesOverlayLayer = root.barUsesOverlayLayer;
|
instance.triggerUsesOverlayLayer = root.barUsesOverlayLayer;
|
||||||
@@ -75,20 +86,14 @@ BasePill {
|
|||||||
function openTabByIndex(tabIndex) {
|
function openTabByIndex(tabIndex) {
|
||||||
if (tabIndex < 0)
|
if (tabIndex < 0)
|
||||||
return;
|
return;
|
||||||
const instance = prepareNotepadInstance(root.notepadInstance);
|
showActiveSurface();
|
||||||
if (instance && typeof instance.show === "function") {
|
|
||||||
instance.show();
|
|
||||||
}
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
NotepadStorageService.switchToTab(tabIndex);
|
NotepadStorageService.switchToTab(tabIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openNewNote() {
|
function openNewNote() {
|
||||||
const instance = prepareNotepadInstance(root.notepadInstance);
|
showActiveSurface();
|
||||||
if (instance && typeof instance.show === "function") {
|
|
||||||
instance.show();
|
|
||||||
}
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
NotepadStorageService.createNewTab();
|
NotepadStorageService.createNewTab();
|
||||||
});
|
});
|
||||||
@@ -147,6 +152,10 @@ BasePill {
|
|||||||
openContextMenu();
|
openContextMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (root.popoutDefault) {
|
||||||
|
PopoutService.toggleNotepadPopout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const inst = prepareNotepadInstance(root.notepadInstance);
|
const inst = prepareNotepadInstance(root.notepadInstance);
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst.toggle();
|
inst.toggle();
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ BasePill {
|
|||||||
property bool isAtBottom: false
|
property bool isAtBottom: false
|
||||||
property bool isAutoHideBar: false
|
property bool isAutoHideBar: false
|
||||||
property bool useOverflowPopup: !widgetData?.trayUseInlineExpansion
|
property bool useOverflowPopup: !widgetData?.trayUseInlineExpansion
|
||||||
|
property bool useSingleLineOverflowPopup: widgetData?.trayPopupSingleLine ?? SettingsData.trayPopupSingleLine
|
||||||
|
property bool useAutomaticOverflow: widgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow
|
||||||
|
property int configuredMaxVisibleItems: widgetData?.trayMaxVisibleItems ?? SettingsData.trayMaxVisibleItems
|
||||||
|
property real sectionAvailablePrimarySize: 0
|
||||||
readonly property var hiddenTrayIds: {
|
readonly property var hiddenTrayIds: {
|
||||||
const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || "";
|
const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || "";
|
||||||
return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : [];
|
return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : [];
|
||||||
@@ -146,12 +150,32 @@ BasePill {
|
|||||||
|
|
||||||
readonly property var allSortedTrayItems: sortByPreferredOrder(allTrayItems, _trayOrderTrigger)
|
readonly property var allSortedTrayItems: sortByPreferredOrder(allTrayItems, _trayOrderTrigger)
|
||||||
readonly property var allSortedTrayItemKeys: allSortedTrayItems.map(item => getTrayItemKey(item))
|
readonly property var allSortedTrayItemKeys: allSortedTrayItems.map(item => getTrayItemKey(item))
|
||||||
readonly property var mainBarItemsRaw: allSortedTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
readonly property var visibleSortedTrayItems: allSortedTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
||||||
|
readonly property int automaticVisibleItemLimit: {
|
||||||
|
if (!root.useAutomaticOverflow)
|
||||||
|
return root.visibleSortedTrayItems.length;
|
||||||
|
|
||||||
|
const explicitLimit = Number(root.configuredMaxVisibleItems || 0);
|
||||||
|
if (explicitLimit > 0)
|
||||||
|
return Math.max(1, Math.min(root.visibleSortedTrayItems.length, explicitLimit));
|
||||||
|
|
||||||
|
const scale = (typeof CompositorService !== "undefined" && CompositorService.getScreenScale) ? Math.max(1, CompositorService.getScreenScale(root.parentScreen)) : 1;
|
||||||
|
const sectionPrimary = root.sectionAvailablePrimarySize > 0 ? root.sectionAvailablePrimarySize : (root.isVerticalOrientation ? (root.parentScreen?.height || 0) : (root.parentScreen?.width || 0));
|
||||||
|
const logicalPrimary = sectionPrimary > 0 ? (sectionPrimary / scale) : 640;
|
||||||
|
const maxTrayShare = root.isVerticalOrientation ? 0.55 : 0.50;
|
||||||
|
const itemSize = Math.max(1, root.trayItemSize);
|
||||||
|
const slots = Math.floor((logicalPrimary * maxTrayShare) / itemSize);
|
||||||
|
return Math.max(2, Math.min(10, Math.min(root.visibleSortedTrayItems.length, slots)));
|
||||||
|
}
|
||||||
|
readonly property var mainBarItemsRaw: visibleSortedTrayItems.slice(0, automaticVisibleItemLimit)
|
||||||
readonly property var mainBarItems: mainBarItemsRaw.map((item, idx) => ({
|
readonly property var mainBarItems: mainBarItemsRaw.map((item, idx) => ({
|
||||||
key: getTrayItemKey(item),
|
key: getTrayItemKey(item),
|
||||||
item: item
|
item: item
|
||||||
}))
|
}))
|
||||||
readonly property var hiddenBarItems: allSortedTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
readonly property var autoOverflowBarItems: visibleSortedTrayItems.slice(automaticVisibleItemLimit)
|
||||||
|
readonly property var manualHiddenBarItems: allSortedTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
|
||||||
|
readonly property var hiddenBarItemKeys: manualHiddenBarItems.concat(autoOverflowBarItems).map(item => root.getTrayItemKey(item))
|
||||||
|
readonly property var hiddenBarItems: allSortedTrayItems.filter(item => hiddenBarItemKeys.indexOf(root.getTrayItemKey(item)) !== -1)
|
||||||
readonly property string trayIconTintMode: {
|
readonly property string trayIconTintMode: {
|
||||||
const configuredMode = SettingsData.systemTrayIconTintMode || "none";
|
const configuredMode = SettingsData.systemTrayIconTintMode || "none";
|
||||||
switch (configuredMode) {
|
switch (configuredMode) {
|
||||||
@@ -219,6 +243,10 @@ BasePill {
|
|||||||
|
|
||||||
const fromKey = mainBarItems[visibleFromIndex]?.key ?? null;
|
const fromKey = mainBarItems[visibleFromIndex]?.key ?? null;
|
||||||
const toKey = mainBarItems[visibleToIndex]?.key ?? null;
|
const toKey = mainBarItems[visibleToIndex]?.key ?? null;
|
||||||
|
moveTrayItemKeyInFullOrder(fromKey, toKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveTrayItemKeyInFullOrder(fromKey, toKey) {
|
||||||
if (!fromKey || !toKey)
|
if (!fromKey || !toKey)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -233,10 +261,103 @@ BasePill {
|
|||||||
SessionData.setTrayItemOrder(fullOrder);
|
SessionData.setTrayItemOrder(fullOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function promoteTrayItemToBar(item) {
|
||||||
|
const itemKey = getTrayItemKey(item);
|
||||||
|
if (!itemKey)
|
||||||
|
return;
|
||||||
|
if (SessionData.isHiddenTrayId(itemKey)) {
|
||||||
|
SessionData.showTrayId(itemKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullOrder = [...allSortedTrayItemKeys];
|
||||||
|
const fromIndex = fullOrder.indexOf(itemKey);
|
||||||
|
if (fromIndex < 0)
|
||||||
|
return;
|
||||||
|
const movedKey = fullOrder.splice(fromIndex, 1)[0];
|
||||||
|
const targetIndex = Math.max(0, Math.min(root.automaticVisibleItemLimit - 1, fullOrder.length));
|
||||||
|
fullOrder.splice(targetIndex, 0, movedKey);
|
||||||
|
SessionData.setTrayItemOrder(fullOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isManualHiddenTrayItem(item) {
|
||||||
|
return SessionData.isHiddenTrayId(getTrayItemKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAutoOverflowTrayItem(item) {
|
||||||
|
const key = getTrayItemKey(item);
|
||||||
|
return key && !isManualHiddenTrayItem(item) && root.autoOverflowBarItems.some(overflowItem => getTrayItemKey(overflowItem) === key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragShiftOffset(index, draggedIndex, dropTargetIndex, shiftAmount) {
|
||||||
|
if (draggedIndex < 0 || index === draggedIndex || dropTargetIndex < 0)
|
||||||
|
return 0;
|
||||||
|
if (draggedIndex < dropTargetIndex && index > draggedIndex && index <= dropTargetIndex)
|
||||||
|
return -shiftAmount;
|
||||||
|
if (draggedIndex > dropTargetIndex && index >= dropTargetIndex && index < draggedIndex)
|
||||||
|
return shiftAmount;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginMainDrag(visualIndex, reversed) {
|
||||||
|
root.draggedIndex = reversed ? (root.mainBarItems.length - 1 - visualIndex) : visualIndex;
|
||||||
|
root.dropTargetIndex = root.draggedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMainDrag(axisOffset, visualIndex, reversed) {
|
||||||
|
const itemSize = root.trayItemSize;
|
||||||
|
const slotOffset = Math.round(axisOffset / itemSize);
|
||||||
|
const visualTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, visualIndex + slotOffset));
|
||||||
|
const newTargetIndex = reversed ? (root.mainBarItems.length - 1 - visualTargetIndex) : visualTargetIndex;
|
||||||
|
if (newTargetIndex !== root.dropTargetIndex)
|
||||||
|
root.dropTargetIndex = newTargetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishMainDrag() {
|
||||||
|
const didReorder = root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
|
||||||
|
if (didReorder) {
|
||||||
|
root.suppressShiftAnimation = true;
|
||||||
|
root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex);
|
||||||
|
Qt.callLater(() => root.suppressShiftAnimation = false);
|
||||||
|
}
|
||||||
|
root.draggedIndex = -1;
|
||||||
|
root.dropTargetIndex = -1;
|
||||||
|
return didReorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginPopupDrag(index) {
|
||||||
|
root.popupDraggedIndex = index;
|
||||||
|
root.popupDropTargetIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePopupDrag(axisOffset, index) {
|
||||||
|
const itemSize = root.trayItemSize + 6;
|
||||||
|
const slotOffset = Math.round(axisOffset / itemSize);
|
||||||
|
const newTargetIndex = Math.max(0, Math.min(root.hiddenBarItems.length - 1, index + slotOffset));
|
||||||
|
if (newTargetIndex !== root.popupDropTargetIndex)
|
||||||
|
root.popupDropTargetIndex = newTargetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPopupDrag() {
|
||||||
|
const didReorder = root.popupDropTargetIndex >= 0 && root.popupDropTargetIndex !== root.popupDraggedIndex;
|
||||||
|
if (didReorder) {
|
||||||
|
const fromItem = root.hiddenBarItems[root.popupDraggedIndex];
|
||||||
|
const toItem = root.hiddenBarItems[root.popupDropTargetIndex];
|
||||||
|
root.suppressShiftAnimation = true;
|
||||||
|
root.moveTrayItemKeyInFullOrder(root.getTrayItemKey(fromItem), root.getTrayItemKey(toItem));
|
||||||
|
Qt.callLater(() => root.suppressShiftAnimation = false);
|
||||||
|
}
|
||||||
|
root.popupDraggedIndex = -1;
|
||||||
|
root.popupDropTargetIndex = -1;
|
||||||
|
return didReorder;
|
||||||
|
}
|
||||||
|
|
||||||
property int draggedIndex: -1
|
property int draggedIndex: -1
|
||||||
property int dropTargetIndex: -1
|
property int dropTargetIndex: -1
|
||||||
|
property int popupDraggedIndex: -1
|
||||||
|
property int popupDropTargetIndex: -1
|
||||||
property bool suppressShiftAnimation: false
|
property bool suppressShiftAnimation: false
|
||||||
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
|
readonly property bool hasHiddenItems: hiddenBarItems.length > 0
|
||||||
readonly property bool inlineExpanded: hasHiddenItems && !useOverflowPopup && menuOpen
|
readonly property bool inlineExpanded: hasHiddenItems && !useOverflowPopup && menuOpen
|
||||||
visible: allTrayItems.length > 0
|
visible: allTrayItems.length > 0
|
||||||
opacity: allTrayItems.length > 0 ? 1 : 0
|
opacity: allTrayItems.length > 0 ? 1 : 0
|
||||||
@@ -351,22 +472,7 @@ BasePill {
|
|||||||
height: root.barThickness
|
height: root.barThickness
|
||||||
z: dragHandler.dragging ? 100 : 0
|
z: dragHandler.dragging ? 100 : 0
|
||||||
|
|
||||||
property real shiftOffset: {
|
property real shiftOffset: root.dragShiftOffset(index, root.draggedIndex, root.dropTargetIndex, root.trayItemSize)
|
||||||
if (root.draggedIndex < 0)
|
|
||||||
return 0;
|
|
||||||
if (index === root.draggedIndex)
|
|
||||||
return 0;
|
|
||||||
const dragIdx = root.draggedIndex;
|
|
||||||
const dropIdx = root.dropTargetIndex;
|
|
||||||
const shiftAmount = root.trayItemSize;
|
|
||||||
if (dropIdx < 0)
|
|
||||||
return 0;
|
|
||||||
if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx)
|
|
||||||
return -shiftAmount;
|
|
||||||
if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx)
|
|
||||||
return shiftAmount;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Translate {
|
transform: Translate {
|
||||||
x: delegateRoot.shiftOffset
|
x: delegateRoot.shiftOffset
|
||||||
@@ -466,19 +572,12 @@ BasePill {
|
|||||||
onReleased: mouse => {
|
onReleased: mouse => {
|
||||||
longPressTimer.stop();
|
longPressTimer.stop();
|
||||||
const wasDragging = dragHandler.dragging;
|
const wasDragging = dragHandler.dragging;
|
||||||
const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
|
if (wasDragging)
|
||||||
|
root.finishMainDrag();
|
||||||
if (didReorder) {
|
|
||||||
root.suppressShiftAnimation = true;
|
|
||||||
root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex);
|
|
||||||
Qt.callLater(() => root.suppressShiftAnimation = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
dragHandler.longPressing = false;
|
dragHandler.longPressing = false;
|
||||||
dragHandler.dragging = false;
|
dragHandler.dragging = false;
|
||||||
dragHandler.dragAxisOffset = 0;
|
dragHandler.dragAxisOffset = 0;
|
||||||
root.draggedIndex = -1;
|
|
||||||
root.dropTargetIndex = -1;
|
|
||||||
|
|
||||||
if (wasDragging || mouse.button !== Qt.LeftButton)
|
if (wasDragging || mouse.button !== Qt.LeftButton)
|
||||||
return;
|
return;
|
||||||
@@ -501,8 +600,7 @@ BasePill {
|
|||||||
const distance = Math.abs(mouse.x - dragHandler.dragStartPos.x);
|
const distance = Math.abs(mouse.x - dragHandler.dragStartPos.x);
|
||||||
if (distance > 5) {
|
if (distance > 5) {
|
||||||
dragHandler.dragging = true;
|
dragHandler.dragging = true;
|
||||||
root.draggedIndex = root.reverseInlineHorizontal ? (root.mainBarItems.length - 1 - index) : index;
|
root.beginMainDrag(index, root.reverseInlineHorizontal);
|
||||||
root.dropTargetIndex = root.draggedIndex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!dragHandler.dragging)
|
if (!dragHandler.dragging)
|
||||||
@@ -510,13 +608,7 @@ BasePill {
|
|||||||
|
|
||||||
const axisOffset = mouse.x - dragHandler.dragStartPos.x;
|
const axisOffset = mouse.x - dragHandler.dragStartPos.x;
|
||||||
dragHandler.dragAxisOffset = axisOffset;
|
dragHandler.dragAxisOffset = axisOffset;
|
||||||
const itemSize = root.trayItemSize;
|
root.updateMainDrag(axisOffset, index, root.reverseInlineHorizontal);
|
||||||
const slotOffset = Math.round(axisOffset / itemSize);
|
|
||||||
const visualTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset));
|
|
||||||
const newTargetIndex = root.reverseInlineHorizontal ? (root.mainBarItems.length - 1 - visualTargetIndex) : visualTargetIndex;
|
|
||||||
if (newTargetIndex !== root.dropTargetIndex) {
|
|
||||||
root.dropTargetIndex = newTargetIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
@@ -706,22 +798,7 @@ BasePill {
|
|||||||
height: root.trayItemSize
|
height: root.trayItemSize
|
||||||
z: dragHandler.dragging ? 100 : 0
|
z: dragHandler.dragging ? 100 : 0
|
||||||
|
|
||||||
property real shiftOffset: {
|
property real shiftOffset: root.dragShiftOffset(index, root.draggedIndex, root.dropTargetIndex, root.trayItemSize)
|
||||||
if (root.draggedIndex < 0)
|
|
||||||
return 0;
|
|
||||||
if (index === root.draggedIndex)
|
|
||||||
return 0;
|
|
||||||
const dragIdx = root.draggedIndex;
|
|
||||||
const dropIdx = root.dropTargetIndex;
|
|
||||||
const shiftAmount = root.trayItemSize;
|
|
||||||
if (dropIdx < 0)
|
|
||||||
return 0;
|
|
||||||
if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx)
|
|
||||||
return -shiftAmount;
|
|
||||||
if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx)
|
|
||||||
return shiftAmount;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Translate {
|
transform: Translate {
|
||||||
y: shiftOffset
|
y: shiftOffset
|
||||||
@@ -821,19 +898,12 @@ BasePill {
|
|||||||
onReleased: mouse => {
|
onReleased: mouse => {
|
||||||
longPressTimer.stop();
|
longPressTimer.stop();
|
||||||
const wasDragging = dragHandler.dragging;
|
const wasDragging = dragHandler.dragging;
|
||||||
const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
|
if (wasDragging)
|
||||||
|
root.finishMainDrag();
|
||||||
if (didReorder) {
|
|
||||||
root.suppressShiftAnimation = true;
|
|
||||||
root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex);
|
|
||||||
Qt.callLater(() => root.suppressShiftAnimation = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
dragHandler.longPressing = false;
|
dragHandler.longPressing = false;
|
||||||
dragHandler.dragging = false;
|
dragHandler.dragging = false;
|
||||||
dragHandler.dragAxisOffset = 0;
|
dragHandler.dragAxisOffset = 0;
|
||||||
root.draggedIndex = -1;
|
|
||||||
root.dropTargetIndex = -1;
|
|
||||||
|
|
||||||
if (wasDragging || mouse.button !== Qt.LeftButton)
|
if (wasDragging || mouse.button !== Qt.LeftButton)
|
||||||
return;
|
return;
|
||||||
@@ -856,8 +926,7 @@ BasePill {
|
|||||||
const distance = Math.abs(mouse.y - dragHandler.dragStartPos.y);
|
const distance = Math.abs(mouse.y - dragHandler.dragStartPos.y);
|
||||||
if (distance > 5) {
|
if (distance > 5) {
|
||||||
dragHandler.dragging = true;
|
dragHandler.dragging = true;
|
||||||
root.draggedIndex = index;
|
root.beginMainDrag(index, false);
|
||||||
root.dropTargetIndex = root.draggedIndex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!dragHandler.dragging)
|
if (!dragHandler.dragging)
|
||||||
@@ -865,12 +934,7 @@ BasePill {
|
|||||||
|
|
||||||
const axisOffset = mouse.y - dragHandler.dragStartPos.y;
|
const axisOffset = mouse.y - dragHandler.dragStartPos.y;
|
||||||
dragHandler.dragAxisOffset = axisOffset;
|
dragHandler.dragAxisOffset = axisOffset;
|
||||||
const itemSize = root.trayItemSize;
|
root.updateMainDrag(axisOffset, index, false);
|
||||||
const slotOffset = Math.round(axisOffset / itemSize);
|
|
||||||
const newTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset));
|
|
||||||
if (newTargetIndex !== root.dropTargetIndex) {
|
|
||||||
root.dropTargetIndex = newTargetIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
@@ -1136,20 +1200,38 @@ BasePill {
|
|||||||
id: menuContainer
|
id: menuContainer
|
||||||
objectName: "overflowMenuContainer"
|
objectName: "overflowMenuContainer"
|
||||||
|
|
||||||
|
readonly property bool popupUsesVerticalLine: root.useSingleLineOverflowPopup && root.isVerticalOrientation
|
||||||
|
readonly property real popupPadding: Theme.spacingS + (popupUsesVerticalLine ? 3 : 0)
|
||||||
|
|
||||||
readonly property real rawWidth: {
|
readonly property real rawWidth: {
|
||||||
const itemCount = root.hiddenBarItems.length;
|
const itemCount = root.hiddenBarItems.length;
|
||||||
const cols = Math.min(5, itemCount);
|
if (itemCount === 0)
|
||||||
|
return 0;
|
||||||
|
if (popupUsesVerticalLine)
|
||||||
|
return root.trayItemSize + 4 + popupPadding * 2;
|
||||||
|
const cols = root.useSingleLineOverflowPopup ? itemCount : Math.min(5, itemCount);
|
||||||
const itemSize = root.trayItemSize + 4;
|
const itemSize = root.trayItemSize + 4;
|
||||||
const spacing = 2;
|
const spacing = 2;
|
||||||
return cols * itemSize + (cols - 1) * spacing + Theme.spacingS * 2;
|
const desiredWidth = cols * itemSize + (cols - 1) * spacing + popupPadding * 2;
|
||||||
|
if (!root.useSingleLineOverflowPopup)
|
||||||
|
return desiredWidth;
|
||||||
|
const maxWidth = Math.max(itemSize + popupPadding * 2, overflowMenu.maskWidth - 20);
|
||||||
|
return Math.min(desiredWidth, maxWidth);
|
||||||
}
|
}
|
||||||
readonly property real rawHeight: {
|
readonly property real rawHeight: {
|
||||||
const itemCount = root.hiddenBarItems.length;
|
const itemCount = root.hiddenBarItems.length;
|
||||||
const cols = Math.min(5, itemCount);
|
if (itemCount === 0)
|
||||||
const rows = Math.ceil(itemCount / cols);
|
return 0;
|
||||||
const itemSize = root.trayItemSize + 4;
|
const itemSize = root.trayItemSize + 4;
|
||||||
const spacing = 2;
|
const spacing = 2;
|
||||||
return rows * itemSize + (rows - 1) * spacing + Theme.spacingS * 2;
|
if (popupUsesVerticalLine) {
|
||||||
|
const desiredHeight = itemCount * itemSize + (itemCount - 1) * spacing + popupPadding * 2;
|
||||||
|
const maxHeight = Math.max(itemSize + popupPadding * 2, overflowMenu.maskHeight - 20);
|
||||||
|
return Math.min(desiredHeight, maxHeight);
|
||||||
|
}
|
||||||
|
const cols = root.useSingleLineOverflowPopup ? itemCount : Math.min(5, itemCount);
|
||||||
|
const rows = Math.ceil(itemCount / cols);
|
||||||
|
return rows * itemSize + (rows - 1) * spacing + popupPadding * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real alignedWidth: Theme.px(rawWidth, overflowMenu.dpr)
|
readonly property real alignedWidth: Theme.px(rawWidth, overflowMenu.dpr)
|
||||||
@@ -1230,10 +1312,21 @@ BasePill {
|
|||||||
z: 100
|
z: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - menuContainer.popupPadding * 2
|
||||||
|
height: parent.height - menuContainer.popupPadding * 2
|
||||||
|
contentWidth: menuGrid.implicitWidth
|
||||||
|
contentHeight: menuGrid.implicitHeight
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
|
interactive: root.useSingleLineOverflowPopup && (menuContainer.popupUsesVerticalLine ? contentHeight > height : contentWidth > width)
|
||||||
|
|
||||||
Grid {
|
Grid {
|
||||||
id: menuGrid
|
id: menuGrid
|
||||||
anchors.centerIn: parent
|
anchors.verticalCenter: menuContainer.popupUsesVerticalLine ? undefined : parent.verticalCenter
|
||||||
columns: Math.min(5, root.hiddenBarItems.length)
|
anchors.horizontalCenter: menuContainer.popupUsesVerticalLine ? parent.horizontalCenter : undefined
|
||||||
|
columns: menuContainer.popupUsesVerticalLine ? 1 : (root.useSingleLineOverflowPopup ? root.hiddenBarItems.length : Math.min(5, root.hiddenBarItems.length))
|
||||||
spacing: 2
|
spacing: 2
|
||||||
rowSpacing: 2
|
rowSpacing: 2
|
||||||
|
|
||||||
@@ -1241,13 +1334,56 @@ BasePill {
|
|||||||
model: root.hiddenBarItems
|
model: root.hiddenBarItems
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
|
id: overflowItemRoot
|
||||||
property var trayItem: modelData
|
property var trayItem: modelData
|
||||||
|
property string itemKey: root.getTrayItemKey(trayItem)
|
||||||
property string iconSource: root.trayIconSourceFor(trayItem)
|
property string iconSource: root.trayIconSourceFor(trayItem)
|
||||||
|
|
||||||
width: root.trayItemSize + 4
|
width: root.trayItemSize + 4
|
||||||
height: root.trayItemSize + 4
|
height: root.trayItemSize + 4
|
||||||
|
z: popupDragHandler.dragging ? 100 : 0
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: itemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0)
|
color: itemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(Theme.surfaceContainer, 0)
|
||||||
|
border.width: popupDragHandler.dragging ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
opacity: popupDragHandler.dragging ? 0.8 : 1.0
|
||||||
|
|
||||||
|
property real shiftOffset: root.dragShiftOffset(index, root.popupDraggedIndex, root.popupDropTargetIndex, root.trayItemSize + 6)
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
x: !menuContainer.popupUsesVerticalLine ? overflowItemRoot.shiftOffset + (popupDragHandler.dragging ? popupDragHandler.dragAxisOffset : 0) : 0
|
||||||
|
y: menuContainer.popupUsesVerticalLine ? overflowItemRoot.shiftOffset + (popupDragHandler.dragging ? popupDragHandler.dragAxisOffset : 0) : 0
|
||||||
|
Behavior on x {
|
||||||
|
enabled: !root.suppressShiftAnimation && !menuContainer.popupUsesVerticalLine
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on y {
|
||||||
|
enabled: !root.suppressShiftAnimation && menuContainer.popupUsesVerticalLine
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: popupDragHandler
|
||||||
|
anchors.fill: parent
|
||||||
|
property bool dragging: false
|
||||||
|
property point dragStartPos: Qt.point(0, 0)
|
||||||
|
property real dragAxisOffset: 0
|
||||||
|
property bool longPressing: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: popupLongPressTimer
|
||||||
|
interval: 400
|
||||||
|
repeat: false
|
||||||
|
onTriggered: popupDragHandler.longPressing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: menuIconImg
|
id: menuIconImg
|
||||||
@@ -1285,8 +1421,38 @@ BasePill {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: popupDragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||||
|
onPressed: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
popupDragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||||
|
popupLongPressTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: mouse => {
|
||||||
|
popupLongPressTimer.stop();
|
||||||
|
const wasDragging = popupDragHandler.dragging;
|
||||||
|
if (wasDragging)
|
||||||
|
root.finishPopupDrag();
|
||||||
|
|
||||||
|
popupDragHandler.longPressing = false;
|
||||||
|
popupDragHandler.dragging = false;
|
||||||
|
popupDragHandler.dragAxisOffset = 0;
|
||||||
|
}
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
const axisDelta = menuContainer.popupUsesVerticalLine ? (mouse.y - popupDragHandler.dragStartPos.y) : (mouse.x - popupDragHandler.dragStartPos.x);
|
||||||
|
if (popupDragHandler.longPressing && !popupDragHandler.dragging && Math.abs(axisDelta) > 5) {
|
||||||
|
popupDragHandler.dragging = true;
|
||||||
|
root.beginPopupDrag(index);
|
||||||
|
}
|
||||||
|
if (!popupDragHandler.dragging)
|
||||||
|
return;
|
||||||
|
|
||||||
|
popupDragHandler.dragAxisOffset = axisDelta;
|
||||||
|
root.updatePopupDrag(axisDelta, index);
|
||||||
|
}
|
||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
|
if (popupDragHandler.dragging)
|
||||||
|
return;
|
||||||
if (!trayItem)
|
if (!trayItem)
|
||||||
return;
|
return;
|
||||||
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
|
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
|
||||||
@@ -1307,6 +1473,7 @@ BasePill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: trayMenuComponent
|
id: trayMenuComponent
|
||||||
@@ -1695,7 +1862,12 @@ BasePill {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: menuRoot.trayItem?.id || "Unknown"
|
text: {
|
||||||
|
const itemId = menuRoot.trayItem?.id || "Unknown";
|
||||||
|
if (root.isAutoOverflowTrayItem(menuRoot.trayItem))
|
||||||
|
return itemId + " · " + I18n.tr("Keep in Bar");
|
||||||
|
return itemId;
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
@@ -1706,7 +1878,11 @@ BasePill {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
name: SessionData.isHiddenTrayId(root.getTrayItemKey(menuRoot.trayItem)) ? "visibility" : "visibility_off"
|
name: {
|
||||||
|
if (root.isAutoOverflowTrayItem(menuRoot.trayItem))
|
||||||
|
return "push_pin";
|
||||||
|
return root.isManualHiddenTrayItem(menuRoot.trayItem) ? "visibility" : "visibility_off";
|
||||||
|
}
|
||||||
size: 16
|
size: 16
|
||||||
color: Theme.widgetTextColor
|
color: Theme.widgetTextColor
|
||||||
}
|
}
|
||||||
@@ -1720,7 +1896,9 @@ BasePill {
|
|||||||
const itemKey = root.getTrayItemKey(menuRoot.trayItem);
|
const itemKey = root.getTrayItemKey(menuRoot.trayItem);
|
||||||
if (!itemKey)
|
if (!itemKey)
|
||||||
return;
|
return;
|
||||||
if (SessionData.isHiddenTrayId(itemKey)) {
|
if (root.isAutoOverflowTrayItem(menuRoot.trayItem)) {
|
||||||
|
root.promoteTrayItemToBar(menuRoot.trayItem);
|
||||||
|
} else if (root.isManualHiddenTrayItem(menuRoot.trayItem)) {
|
||||||
SessionData.showTrayId(itemKey);
|
SessionData.showTrayId(itemKey);
|
||||||
} else {
|
} else {
|
||||||
SessionData.hideTrayId(itemKey);
|
SessionData.hideTrayId(itemKey);
|
||||||
|
|||||||
@@ -108,9 +108,6 @@ DankPopout {
|
|||||||
MprisController.setActivePlayer(player);
|
MprisController.setActivePlayer(player);
|
||||||
root.__hideDropdowns();
|
root.__hideDropdowns();
|
||||||
}
|
}
|
||||||
onDeviceSelected: device => {
|
|
||||||
root.__hideDropdowns();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -383,7 +383,27 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onPressed: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onWheel: wheelEvent => {
|
||||||
|
if (SettingsData.audioDeviceScrollVolumeEnabled && wheelEvent.x >= deviceMouseArea.width / 2) {
|
||||||
|
AudioService.handleNodeVolumeWheel(modelData, wheelEvent);
|
||||||
|
} else {
|
||||||
|
wheelEvent.accepted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
if (modelData && modelData.audio) {
|
||||||
|
SessionData.suppressOSDTemporarily();
|
||||||
|
modelData.audio.muted = !modelData.audio.muted;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (modelData && modelData.name) {
|
if (modelData && modelData.name) {
|
||||||
AudioService.setDefaultSinkByName(modelData.name);
|
AudioService.setDefaultSinkByName(modelData.name);
|
||||||
root.deviceSelected(modelData);
|
root.deviceSelected(modelData);
|
||||||
|
|||||||
@@ -866,7 +866,27 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onPressed: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onWheel: wheelEvent => {
|
||||||
|
const delta = wheelEvent.angleDelta.y;
|
||||||
|
if (delta !== 0) {
|
||||||
|
AudioService.cycleAudioOutputDirection(delta < 0);
|
||||||
|
wheelEvent.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
if (AudioService.sink?.audio) {
|
||||||
|
SessionData.suppressOSDTemporarily();
|
||||||
|
AudioService.sink.audio.muted = !AudioService.sink.audio.muted;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (devicesExpanded) {
|
if (devicesExpanded) {
|
||||||
const sinks = AudioService.getAvailableSinks();
|
const sinks = AudioService.getAvailableSinks();
|
||||||
if (sinks && sinks.length > 1) {
|
if (sinks && sinks.length > 1) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -21,21 +22,71 @@ Item {
|
|||||||
property var currentTab: NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null
|
property var currentTab: NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null
|
||||||
property bool showSettingsMenu: false
|
property bool showSettingsMenu: false
|
||||||
property string pendingSaveContent: ""
|
property string pendingSaveContent: ""
|
||||||
|
readonly property bool conflictBannerVisible: currentTab !== null && NotepadStorageService.conflictTabId === currentTab.id
|
||||||
property var slideout: null
|
property var slideout: null
|
||||||
|
property bool inPopout: false
|
||||||
|
property bool surfaceVisible: slideout ? slideout.isVisible : true
|
||||||
|
|
||||||
signal hideRequested
|
signal hideRequested
|
||||||
|
signal popoutRequested
|
||||||
|
signal dockRequested
|
||||||
signal previewRequested(string content)
|
signal previewRequested(string content)
|
||||||
|
|
||||||
|
function externalSync() {
|
||||||
|
textEditor.syncFromDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushAutoSave() {
|
||||||
|
textEditor.autoSaveToSession();
|
||||||
|
}
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: NotepadStorageService
|
service: NotepadStorageService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In connected frame mode the slideout sits on the Overlay layer
|
||||||
|
onFileDialogOpenChanged: {
|
||||||
|
if (slideout)
|
||||||
|
slideout.suppressOverlayLayer = fileDialogOpen;
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: slideout
|
target: slideout
|
||||||
enabled: slideout !== null
|
enabled: slideout !== null
|
||||||
function onAboutToHide() {
|
function onAboutToHide() {
|
||||||
textEditor.autoSaveToSession();
|
textEditor.autoSaveToSession();
|
||||||
}
|
}
|
||||||
|
function onRevealed() {
|
||||||
|
textEditor.syncFromDisk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showConflictBanner(diskContent) {
|
||||||
|
if (!currentTab)
|
||||||
|
return;
|
||||||
|
NotepadStorageService.flagConflict(currentTab.id, diskContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveConflictKeepEdits() {
|
||||||
|
if (!root.conflictBannerVisible)
|
||||||
|
return;
|
||||||
|
NotepadStorageService.clearConflict();
|
||||||
|
if (currentTab && currentTab.filePath && !currentTab.isTemporary) {
|
||||||
|
root.saveToFile("file://" + currentTab.filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveConflictReload() {
|
||||||
|
if (!root.conflictBannerVisible)
|
||||||
|
return;
|
||||||
|
const diskContent = NotepadStorageService.conflictDiskContent;
|
||||||
|
NotepadStorageService.clearConflict();
|
||||||
|
textEditor.reloadFromDisk(diskContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismissConflictBanner() {
|
||||||
|
if (root.conflictBannerVisible)
|
||||||
|
NotepadStorageService.clearConflict();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasUnsavedChanges() {
|
function hasUnsavedChanges() {
|
||||||
@@ -51,10 +102,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function performCreateNewTab() {
|
function performCreateNewTab() {
|
||||||
|
textEditor.commitLiveBuffer();
|
||||||
NotepadStorageService.createNewTab();
|
NotepadStorageService.createNewTab();
|
||||||
|
textEditor.applyingShared = true;
|
||||||
textEditor.text = "";
|
textEditor.text = "";
|
||||||
textEditor.lastSavedContent = "";
|
textEditor.lastSavedContent = "";
|
||||||
|
textEditor.loadedTabId = -1;
|
||||||
textEditor.contentLoaded = true;
|
textEditor.contentLoaded = true;
|
||||||
|
textEditor.applyingShared = false;
|
||||||
textEditor.textArea.forceActiveFocus();
|
textEditor.textArea.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +141,6 @@ Item {
|
|||||||
|
|
||||||
NotepadStorageService.switchToTab(tabIndex);
|
NotepadStorageService.switchToTab(tabIndex);
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
textEditor.loadCurrentTabContent();
|
|
||||||
if (currentTab) {
|
if (currentTab) {
|
||||||
root.currentFileName = currentTab.fileName || "";
|
root.currentFileName = currentTab.fileName || "";
|
||||||
root.currentFileUrl = currentTab.fileUrl || "";
|
root.currentFileUrl = currentTab.fileUrl || "";
|
||||||
@@ -100,6 +154,7 @@ Item {
|
|||||||
var content = textEditor.text;
|
var content = textEditor.text;
|
||||||
var filePath = fileUrl.toString().replace(/^file:\/\//, '');
|
var filePath = fileUrl.toString().replace(/^file:\/\//, '');
|
||||||
|
|
||||||
|
textEditor.externalWatchPaused = true;
|
||||||
saveFileView.path = "";
|
saveFileView.path = "";
|
||||||
pendingSaveContent = content;
|
pendingSaveContent = content;
|
||||||
saveFileView.path = filePath;
|
saveFileView.path = filePath;
|
||||||
@@ -109,6 +164,53 @@ Item {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveExternalWithFreshnessCheck() {
|
||||||
|
if (!currentTab || currentTab.isTemporary || !currentTab.filePath)
|
||||||
|
return;
|
||||||
|
const filePath = currentTab.filePath;
|
||||||
|
loadFileView.path = "";
|
||||||
|
loadFileView.path = filePath;
|
||||||
|
|
||||||
|
if (!loadFileView.waitForJob()) {
|
||||||
|
saveToFile("file://" + filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (!currentTab || currentTab.isTemporary || currentTab.filePath !== filePath)
|
||||||
|
return;
|
||||||
|
const diskContent = loadFileView.text();
|
||||||
|
if (diskContent !== undefined && diskContent !== null && diskContent !== textEditor.text && diskContent !== textEditor.lastSavedContent) {
|
||||||
|
root.showConflictBanner(diskContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveToFile("file://" + filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoSaveExternal() {
|
||||||
|
if (!SettingsData.notepadAutoSave)
|
||||||
|
return;
|
||||||
|
if (!currentTab || currentTab.isTemporary || !currentTab.filePath)
|
||||||
|
return;
|
||||||
|
if (!textEditor.hasUnsavedChanges())
|
||||||
|
return;
|
||||||
|
const filePath = currentTab.filePath;
|
||||||
|
loadFileView.path = "";
|
||||||
|
loadFileView.path = filePath;
|
||||||
|
if (!loadFileView.waitForJob())
|
||||||
|
return;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (!currentTab || currentTab.isTemporary || currentTab.filePath !== filePath)
|
||||||
|
return;
|
||||||
|
const diskContent = loadFileView.text();
|
||||||
|
if (diskContent === undefined || diskContent === null)
|
||||||
|
return;
|
||||||
|
if (diskContent !== textEditor.lastSavedContent)
|
||||||
|
return;
|
||||||
|
saveToFile("file://" + filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadFromFile(fileUrl) {
|
function loadFromFile(fileUrl) {
|
||||||
if (hasUnsavedTemporaryContent()) {
|
if (hasUnsavedTemporaryContent()) {
|
||||||
root.pendingFileUrl = fileUrl;
|
root.pendingFileUrl = fileUrl;
|
||||||
@@ -146,14 +248,155 @@ Item {
|
|||||||
|
|
||||||
root.currentFileName = fileName;
|
root.currentFileName = fileName;
|
||||||
root.currentFileUrl = fileUrl;
|
root.currentFileUrl = fileUrl;
|
||||||
textEditor.saveCurrentTabContent();
|
textEditor.loadedTabId = currentTab.id;
|
||||||
|
NotepadStorageService.clearSessionBuffer(currentTab.id);
|
||||||
|
if (root.conflictBannerVisible)
|
||||||
|
NotepadStorageService.clearConflict();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Item {
|
||||||
|
id: conflictBanner
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: root.conflictBannerVisible ? bannerRect.implicitHeight : 0
|
||||||
|
visible: height > 0
|
||||||
|
clip: true
|
||||||
|
z: 5
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: bannerRect
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
implicitHeight: bannerLayout.implicitHeight + Theme.spacingM * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.warning, 0.12)
|
||||||
|
border.color: Theme.withAlpha(Theme.warning, 0.5)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: bannerLayout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
name: "sync_problem"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.warning
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
text: I18n.tr("File changed on disk")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSizeSmall
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
buttonSize: 28
|
||||||
|
onClicked: root.dismissConflictBanner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 32
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: bannerActions
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
readonly property real available: parent.width
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: Math.min(keepText.implicitWidth + Theme.spacingM * 2, Math.max(104, (bannerActions.available - bannerActions.spacing) / 2))
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Theme.outlineMedium
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
cornerRadius: parent.radius
|
||||||
|
stateColor: Theme.surfaceText
|
||||||
|
onClicked: root.resolveConflictKeepEdits()
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: keepText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Theme.spacingM
|
||||||
|
text: I18n.tr("Keep My Edits")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: Math.min(reloadText.implicitWidth + Theme.spacingM * 2, Math.max(116, (bannerActions.available - bannerActions.spacing) / 2))
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.primary
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
cornerRadius: parent.radius
|
||||||
|
stateColor: Theme.background
|
||||||
|
onClicked: root.resolveConflictReload()
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: reloadText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Theme.spacingM
|
||||||
|
text: I18n.tr("Reload From Disk")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.background
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.top: conflictBanner.bottom
|
||||||
|
anchors.topMargin: root.conflictBannerVisible ? Theme.spacingM : 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
NotepadTabs {
|
NotepadTabs {
|
||||||
@@ -178,11 +421,12 @@ Item {
|
|||||||
id: textEditor
|
id: textEditor
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - tabBar.height - Theme.spacingM * 2
|
height: parent.height - tabBar.height - Theme.spacingM * 2
|
||||||
|
inPopout: root.inPopout
|
||||||
|
surfaceVisible: root.surfaceVisible
|
||||||
|
|
||||||
onSaveRequested: {
|
onSaveRequested: {
|
||||||
if (currentTab && !currentTab.isTemporary && currentTab.filePath) {
|
if (currentTab && !currentTab.isTemporary && currentTab.filePath) {
|
||||||
var fileUrl = "file://" + currentTab.filePath;
|
root.saveExternalWithFreshnessCheck();
|
||||||
saveToFile(fileUrl);
|
|
||||||
} else {
|
} else {
|
||||||
root.fileDialogOpen = true;
|
root.fileDialogOpen = true;
|
||||||
saveBrowserLoader.active = true;
|
saveBrowserLoader.active = true;
|
||||||
@@ -214,12 +458,28 @@ Item {
|
|||||||
|
|
||||||
onEscapePressed: {
|
onEscapePressed: {
|
||||||
textEditor.autoSaveToSession();
|
textEditor.autoSaveToSession();
|
||||||
|
if (showSettingsMenu) {
|
||||||
|
showSettingsMenu = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.inPopout) {
|
||||||
root.hideRequested();
|
root.hideRequested();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onSettingsRequested: {
|
onSettingsRequested: {
|
||||||
showSettingsMenu = !showSettingsMenu;
|
showSettingsMenu = !showSettingsMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPopoutRequested: root.popoutRequested()
|
||||||
|
|
||||||
|
onDockRequested: root.dockRequested()
|
||||||
|
|
||||||
|
onConflictDetected: diskContent => {
|
||||||
|
root.showConflictBanner(diskContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAutoSaveRequested: root.autoSaveExternal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,17 +502,24 @@ Item {
|
|||||||
printErrors: true
|
printErrors: true
|
||||||
|
|
||||||
onSaved: {
|
onSaved: {
|
||||||
if (currentTab && saveFileView.path && pendingSaveContent) {
|
if (currentTab && saveFileView.path) {
|
||||||
NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, {
|
NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, {
|
||||||
hasUnsavedChanges: false,
|
hasUnsavedChanges: false,
|
||||||
lastSavedContent: pendingSaveContent
|
lastSavedContent: pendingSaveContent
|
||||||
});
|
});
|
||||||
root.lastSavedFileContent = pendingSaveContent;
|
root.lastSavedFileContent = pendingSaveContent;
|
||||||
pendingSaveContent = "";
|
textEditor.lastSavedContent = pendingSaveContent;
|
||||||
|
textEditor.ignoreNextExternalChange = true;
|
||||||
|
textEditor.commitLiveBuffer();
|
||||||
|
if (root.conflictBannerVisible)
|
||||||
|
NotepadStorageService.clearConflict();
|
||||||
}
|
}
|
||||||
|
textEditor.externalWatchPaused = false;
|
||||||
|
pendingSaveContent = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
onSaveFailed: error => {
|
onSaveFailed: error => {
|
||||||
|
textEditor.externalWatchPaused = false;
|
||||||
pendingSaveContent = "";
|
pendingSaveContent = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,6 +565,7 @@ Item {
|
|||||||
|
|
||||||
root.currentFileName = fileName;
|
root.currentFileName = fileName;
|
||||||
root.currentFileUrl = fileUrl;
|
root.currentFileUrl = fileUrl;
|
||||||
|
textEditor.externalWatchPaused = true;
|
||||||
|
|
||||||
if (currentTab) {
|
if (currentTab) {
|
||||||
NotepadStorageService.saveTabAs(NotepadStorageService.currentTabIndex, cleanPath);
|
NotepadStorageService.saveTabAs(NotepadStorageService.currentTabIndex, cleanPath);
|
||||||
@@ -343,7 +611,7 @@ Item {
|
|||||||
browserTitle: I18n.tr("Open Notepad File")
|
browserTitle: I18n.tr("Open Notepad File")
|
||||||
browserIcon: "folder_open"
|
browserIcon: "folder_open"
|
||||||
browserType: "notepad_load"
|
browserType: "notepad_load"
|
||||||
fileExtensions: ["*.txt", "*.md", "*.*"]
|
fileExtensions: ["*"]
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
|
|
||||||
onFileSelected: path => {
|
onFileSelected: path => {
|
||||||
@@ -376,6 +644,7 @@ Item {
|
|||||||
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 180
|
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 180
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
|
useOverlayLayer: true
|
||||||
|
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
close();
|
close();
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Notepad
|
||||||
|
|
||||||
|
FloatingWindow {
|
||||||
|
id: win
|
||||||
|
|
||||||
|
property alias shouldBeVisible: win.visible
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
visible = !visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
title: I18n.tr("Notepad")
|
||||||
|
minimumSize: Qt.size(360, 320)
|
||||||
|
implicitWidth: 640
|
||||||
|
implicitHeight: 760
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
Qt.callLater(notepad.externalSync);
|
||||||
|
} else {
|
||||||
|
notepad.flushAutoSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compositor close (e.g. niri close-window)
|
||||||
|
onClosed: win.visible = false
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: titleBar
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 44
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: windowControls.tryStartMove()
|
||||||
|
onDoubleClicked: windowControls.tryToggleMaximize()
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "edit_note"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Notepad")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
visible: windowControls.canMaximize
|
||||||
|
circular: false
|
||||||
|
iconName: win.maximized ? "fullscreen_exit" : "fullscreen"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: windowControls.tryToggleMaximize()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
circular: false
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: win.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Notepad {
|
||||||
|
id: notepad
|
||||||
|
anchors.top: titleBar.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.bottomMargin: Theme.spacingM
|
||||||
|
inPopout: true
|
||||||
|
surfaceVisible: win.visible
|
||||||
|
onHideRequested: win.hide()
|
||||||
|
onDockRequested: {
|
||||||
|
win.hide();
|
||||||
|
PopoutService.openNotepadSlideout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingWindowControls {
|
||||||
|
id: windowControls
|
||||||
|
targetWindow: win
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ Item {
|
|||||||
property var cachedFontFamilies: []
|
property var cachedFontFamilies: []
|
||||||
property var cachedMonoFamilies: []
|
property var cachedMonoFamilies: []
|
||||||
property bool fontsEnumerated: false
|
property bool fontsEnumerated: false
|
||||||
|
property bool shortcutsExpanded: false
|
||||||
|
|
||||||
signal settingsRequested
|
signal settingsRequested
|
||||||
signal findRequested
|
signal findRequested
|
||||||
@@ -62,11 +63,23 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: root.isVisible
|
visible: root.isVisible
|
||||||
onClicked: root.settingsRequested()
|
|
||||||
z: 50
|
z: 50
|
||||||
|
color: Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, 0.85)
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
// Hold scroll so the editor beneath doesn't move while settings are open.
|
||||||
|
onWheel: event => {
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.settingsRequested()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -74,8 +87,8 @@ Item {
|
|||||||
visible: root.isVisible
|
visible: root.isVisible
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 360
|
width: Math.min(360, root.width - Theme.spacingL * 2)
|
||||||
height: settingsColumn.implicitHeight + Theme.spacingXL * 2
|
height: Math.min(settingsColumn.implicitHeight + Theme.spacingXL * 2, root.height - Theme.spacingL * 2)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, Theme.notepadTransparency)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, Theme.notepadTransparency)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
@@ -93,12 +106,18 @@ Item {
|
|||||||
z: parent.z - 1
|
z: parent.z - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
id: settingsFlickable
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentWidth: width
|
||||||
|
contentHeight: settingsColumn.implicitHeight + Theme.spacingXL * 2
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: settingsColumn
|
id: settingsColumn
|
||||||
width: parent.width - Theme.spacingXL * 2
|
x: Theme.spacingXL
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
y: Theme.spacingXL
|
||||||
anchors.top: parent.top
|
width: settingsFlickable.width - Theme.spacingXL * 2
|
||||||
anchors.topMargin: Theme.spacingXL
|
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -110,7 +129,7 @@ Item {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: -Theme.spacingXS
|
anchors.leftMargin: -Theme.spacingXS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: I18n.tr("Notepad Font Settings")
|
text: I18n.tr("Notepad Settings")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -147,6 +166,18 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -Theme.spacingM
|
||||||
|
width: parent.width + Theme.spacingM
|
||||||
|
text: I18n.tr("Auto-save to disk")
|
||||||
|
description: I18n.tr("Automatically save changes to opened files as you type")
|
||||||
|
checked: SettingsData.notepadAutoSave
|
||||||
|
onToggled: checked => {
|
||||||
|
SettingsData.notepadAutoSave = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
@@ -321,7 +352,7 @@ Item {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: -Theme.spacingM
|
anchors.leftMargin: -Theme.spacingM
|
||||||
width: parent.width + Theme.spacingM
|
width: parent.width + Theme.spacingM
|
||||||
text: I18n.tr("Custom Transparency")
|
text: I18n.tr("Surface Opacity")
|
||||||
description: I18n.tr("Override global transparency for Notepad")
|
description: I18n.tr("Override global transparency for Notepad")
|
||||||
checked: SettingsData.notepadTransparencyOverride >= 0
|
checked: SettingsData.notepadTransparencyOverride >= 0
|
||||||
onToggled: checked => {
|
onToggled: checked => {
|
||||||
@@ -354,6 +385,102 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: gapColumn.height + Theme.spacingS
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: gapColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Default Mode")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
model: [I18n.tr("Slideout"), I18n.tr("Popout")]
|
||||||
|
size: "small"
|
||||||
|
currentIndex: SettingsData.notepadDefaultMode === "popout" ? 1 : 0
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
SettingsData.notepadDefaultMode = index === 1 ? "popout" : "slideout";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: SettingsData.notepadDefaultMode !== "popout"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Open From")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
model: [I18n.tr("Right"), I18n.tr("Left")]
|
||||||
|
size: "small"
|
||||||
|
currentIndex: SettingsData.notepadSlideoutSide === "left" ? 1 : 0
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
SettingsData.notepadSlideoutSide = index === 1 ? "left" : "right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -Theme.spacingM
|
||||||
|
width: parent.width + Theme.spacingM
|
||||||
|
text: I18n.tr("Auto Compositor Gaps")
|
||||||
|
description: I18n.tr("Inset the Notepad from screen edges using the compositor's configured gaps")
|
||||||
|
checked: SettingsData.notepadUseCompositorGap
|
||||||
|
onToggled: checked => {
|
||||||
|
SettingsData.notepadUseCompositorGap = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: !SettingsData.notepadUseCompositorGap
|
||||||
|
text: I18n.tr("Manual Gaps")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSlider {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingXS
|
||||||
|
width: parent.width - Theme.spacingXS * 2
|
||||||
|
height: 24
|
||||||
|
visible: !SettingsData.notepadUseCompositorGap
|
||||||
|
value: SettingsData.notepadEdgeGap
|
||||||
|
minimum: 0
|
||||||
|
maximum: 64
|
||||||
|
unit: "px"
|
||||||
|
showValue: true
|
||||||
|
wheelEnabled: false
|
||||||
|
onSliderValueChanged: newValue => {
|
||||||
|
SettingsData.notepadEdgeGap = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: SettingsData.notepadUseMonospace ? I18n.tr("Using global monospace font from Settings → Personalization") : I18n.tr("Global fonts can be configured in Settings → Personalization")
|
text: SettingsData.notepadUseMonospace ? I18n.tr("Using global monospace font from Settings → Personalization") : I18n.tr("Global fonts can be configured in Settings → Personalization")
|
||||||
@@ -362,6 +489,76 @@ Item {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: shortcutsHeader.height + (root.shortcutsExpanded ? shortcutsColumn.implicitHeight + Theme.spacingM : 0)
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: root.shortcutsExpanded ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : "transparent"
|
||||||
|
border.color: root.shortcutsExpanded ? Theme.primary : Theme.outlineMedium
|
||||||
|
border.width: root.shortcutsExpanded ? 2 : 1
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
stateColor: Theme.primary
|
||||||
|
cornerRadius: parent.radius
|
||||||
|
onClicked: root.shortcutsExpanded = !root.shortcutsExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: shortcutsHeader
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
height: 36
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: root.shortcutsExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Keyboard Shortcuts")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: shortcutsColumn
|
||||||
|
visible: root.shortcutsExpanded
|
||||||
|
width: parent.width - Theme.spacingL * 2
|
||||||
|
anchors.top: shortcutsHeader.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Ctrl+S: Save • Ctrl+O: Open • Ctrl+N: New • Ctrl+F: Find")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Ctrl+A: Select All • Ctrl+P: Preview • Enter/Shift+Enter: Find Next/Previous • Esc: Close")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,23 @@ Column {
|
|||||||
property string pluginHighlightedHtml: ""
|
property string pluginHighlightedHtml: ""
|
||||||
property string lastPluginContent: ""
|
property string lastPluginContent: ""
|
||||||
property int loadRequestId: 0
|
property int loadRequestId: 0
|
||||||
|
property bool ignoreNextExternalChange: false
|
||||||
|
property bool watcherReloadPending: false
|
||||||
|
property bool externalWatchPaused: false
|
||||||
|
property bool inPopout: false
|
||||||
|
property bool surfaceVisible: true
|
||||||
|
// Tab ids are Date.now() timestamps (~1.78e12) which overflow a 32-bit `int`,
|
||||||
|
// corrupting the value (e.g. -946062153) and breaking buffer keying. `var`
|
||||||
|
// holds the full JS-safe integer.
|
||||||
|
property var loadedTabId: -1
|
||||||
|
property bool applyingShared: false
|
||||||
|
property bool showPathInfo: false
|
||||||
|
|
||||||
|
function currentFilePath() {
|
||||||
|
if (!currentTab)
|
||||||
|
return "";
|
||||||
|
return currentTab.isTemporary ? (NotepadStorageService.baseDir + "/" + currentTab.filePath) : currentTab.filePath;
|
||||||
|
}
|
||||||
|
|
||||||
signal saveRequested
|
signal saveRequested
|
||||||
signal openRequested
|
signal openRequested
|
||||||
@@ -40,6 +57,10 @@ Column {
|
|||||||
signal escapePressed
|
signal escapePressed
|
||||||
signal contentChanged
|
signal contentChanged
|
||||||
signal settingsRequested
|
signal settingsRequested
|
||||||
|
signal popoutRequested
|
||||||
|
signal dockRequested
|
||||||
|
signal conflictDetected(string diskContent)
|
||||||
|
signal autoSaveRequested
|
||||||
|
|
||||||
function hasUnsavedChanges() {
|
function hasUnsavedChanges() {
|
||||||
if (!currentTab || !contentLoaded) {
|
if (!currentTab || !contentLoaded) {
|
||||||
@@ -52,6 +73,12 @@ Column {
|
|||||||
return textArea.text !== lastSavedContent;
|
return textArea.text !== lastSavedContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function commitLiveBuffer() {
|
||||||
|
if (loadedTabId < 0 || !contentLoaded)
|
||||||
|
return;
|
||||||
|
NotepadStorageService.setSessionBuffer(loadedTabId, textArea.text, lastSavedContent);
|
||||||
|
}
|
||||||
|
|
||||||
function loadCurrentTabContent() {
|
function loadCurrentTabContent() {
|
||||||
if (!currentTab)
|
if (!currentTab)
|
||||||
return;
|
return;
|
||||||
@@ -62,8 +89,25 @@ Column {
|
|||||||
const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null;
|
const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null;
|
||||||
if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId)
|
if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const buffer = NotepadStorageService.getSessionBuffer(requestedTabId);
|
||||||
|
if (buffer !== undefined) {
|
||||||
|
applyingShared = true;
|
||||||
|
lastSavedContent = buffer.baseline;
|
||||||
|
textArea.text = buffer.content;
|
||||||
|
applyingShared = false;
|
||||||
|
loadedTabId = requestedTabId;
|
||||||
|
contentLoaded = true;
|
||||||
|
syncContentToPlugin();
|
||||||
|
applyDiskContent(content);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyingShared = true;
|
||||||
lastSavedContent = content;
|
lastSavedContent = content;
|
||||||
textArea.text = content;
|
textArea.text = content;
|
||||||
|
applyingShared = false;
|
||||||
|
loadedTabId = requestedTabId;
|
||||||
contentLoaded = true;
|
contentLoaded = true;
|
||||||
syncContentToPlugin();
|
syncContentToPlugin();
|
||||||
});
|
});
|
||||||
@@ -72,14 +116,56 @@ Column {
|
|||||||
function saveCurrentTabContent() {
|
function saveCurrentTabContent() {
|
||||||
if (!currentTab || !contentLoaded)
|
if (!currentTab || !contentLoaded)
|
||||||
return;
|
return;
|
||||||
|
if (!currentTab.isTemporary)
|
||||||
|
return;
|
||||||
NotepadStorageService.saveTabContent(NotepadStorageService.currentTabIndex, textArea.text);
|
NotepadStorageService.saveTabContent(NotepadStorageService.currentTabIndex, textArea.text);
|
||||||
lastSavedContent = textArea.text;
|
lastSavedContent = textArea.text;
|
||||||
|
NotepadStorageService.clearSessionBuffer(loadedTabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoSaveToSession() {
|
function autoSaveToSession() {
|
||||||
|
commitLiveBuffer();
|
||||||
if (!currentTab || !contentLoaded)
|
if (!currentTab || !contentLoaded)
|
||||||
return;
|
return;
|
||||||
|
if (currentTab.isTemporary) {
|
||||||
saveCurrentTabContent();
|
saveCurrentTabContent();
|
||||||
|
} else if (SettingsData.notepadAutoSave) {
|
||||||
|
root.autoSaveRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncFromDisk() {
|
||||||
|
if (!currentTab)
|
||||||
|
return;
|
||||||
|
loadCurrentTabContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDiskContent(diskContent) {
|
||||||
|
if (diskContent === undefined || diskContent === null)
|
||||||
|
return;
|
||||||
|
if (diskContent === textArea.text) {
|
||||||
|
lastSavedContent = diskContent;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (diskContent === lastSavedContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (textArea.text === lastSavedContent) {
|
||||||
|
reloadFromDisk(diskContent);
|
||||||
|
} else if (surfaceVisible) {
|
||||||
|
conflictDetected(diskContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadFromDisk(diskContent) {
|
||||||
|
applyingShared = true;
|
||||||
|
contentLoaded = false;
|
||||||
|
textArea.text = diskContent;
|
||||||
|
lastSavedContent = diskContent;
|
||||||
|
contentLoaded = true;
|
||||||
|
applyingShared = false;
|
||||||
|
NotepadStorageService.clearSessionBuffer(loadedTabId);
|
||||||
|
syncContentToPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTextDocumentLineHeight() {
|
function setTextDocumentLineHeight() {
|
||||||
@@ -202,7 +288,8 @@ Column {
|
|||||||
if (!currentTab)
|
if (!currentTab)
|
||||||
return;
|
return;
|
||||||
const filePath = currentTab?.filePath || "";
|
const filePath = currentTab?.filePath || "";
|
||||||
const ext = filePath.split('.').pop().toLowerCase();
|
const baseName = filePath.split('/').pop();
|
||||||
|
const ext = baseName.includes('.') ? baseName.split('.').pop().toLowerCase() : "";
|
||||||
const content = textArea.text;
|
const content = textArea.text;
|
||||||
|
|
||||||
if (content === lastPluginContent && SettingsData.getBuiltInPluginSetting("dankNotepadModule", "previewActive", false) === inlinePreviewVisible) {
|
if (content === lastPluginContent && SettingsData.getBuiltInPluginSetting("dankNotepadModule", "previewActive", false) === inlinePreviewVisible) {
|
||||||
@@ -550,6 +637,7 @@ Column {
|
|||||||
Connections {
|
Connections {
|
||||||
target: NotepadStorageService
|
target: NotepadStorageService
|
||||||
function onCurrentTabIndexChanged() {
|
function onCurrentTabIndexChanged() {
|
||||||
|
root.commitLiveBuffer();
|
||||||
loadCurrentTabContent();
|
loadCurrentTabContent();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
textArea.forceActiveFocus();
|
textArea.forceActiveFocus();
|
||||||
@@ -570,7 +658,9 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (contentLoaded && text !== lastSavedContent) {
|
// Debounced flush to the shared buffer (+ optional disk
|
||||||
|
// autosave) for every loaded tab, not just scratch notes.
|
||||||
|
if (contentLoaded && !applyingShared) {
|
||||||
autoSaveTimer.restart();
|
autoSaveTimer.restart();
|
||||||
}
|
}
|
||||||
root.contentChanged();
|
root.contentChanged();
|
||||||
@@ -744,6 +834,7 @@ Column {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: buttonBarItem
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
@@ -820,9 +911,29 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
Row {
|
||||||
|
id: rightButtonRow
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
visible: !root.inPopout
|
||||||
|
iconName: "open_in_new"
|
||||||
|
iconSize: Theme.iconSize - 2
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: root.popoutRequested()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
visible: root.inPopout
|
||||||
|
iconName: "dock_to_right"
|
||||||
|
iconSize: Theme.iconSize - 2
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: root.dockRequested()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
iconName: "more_horiz"
|
iconName: "more_horiz"
|
||||||
iconSize: Theme.iconSize - 2
|
iconSize: Theme.iconSize - 2
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
@@ -830,7 +941,68 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: pathInfoPopup
|
||||||
|
visible: root.showPathInfo
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.top
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
|
width: Math.min(root.width, 360)
|
||||||
|
height: pathInfoRow.implicitHeight + Theme.spacingS * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
border.color: Theme.outlineMedium
|
||||||
|
border.width: 1
|
||||||
|
z: 10
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: pathInfoRow
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: currentTab && currentTab.isTemporary ? "draft" : "description"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: pathInfoRow.width - (Theme.iconSize - 4) - copyPathButton.width - Theme.spacingS * 2
|
||||||
|
text: root.currentFilePath()
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: copyPathButton
|
||||||
|
iconName: "content_copy"
|
||||||
|
iconSize: Theme.iconSize - 6
|
||||||
|
iconColor: Theme.surfaceTextMedium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
const proc = clipboardCopyProcComp.createObject(root, {
|
||||||
|
content: root.currentFilePath(),
|
||||||
|
running: true
|
||||||
|
});
|
||||||
|
proc.exited.connect(() => {
|
||||||
|
ToastService.showInfo(I18n.tr("Path copied to clipboard"));
|
||||||
|
proc.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: statusRow
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
@@ -853,35 +1025,46 @@ Column {
|
|||||||
opacity: 1.0
|
opacity: 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
visible: textArea.text.length > 0
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
readonly property bool savingToDisk: autoSaveTimer.running && currentTab && (currentTab.isTemporary || SettingsData.notepadAutoSave)
|
||||||
text: {
|
text: {
|
||||||
if (autoSaveTimer.running) {
|
if (savingToDisk) {
|
||||||
return I18n.tr("Auto-saving...");
|
return I18n.tr("Saving...");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUnsavedChanges()) {
|
|
||||||
if (currentTab && currentTab.isTemporary) {
|
if (currentTab && currentTab.isTemporary) {
|
||||||
return I18n.tr("Unsaved note...");
|
return I18n.tr("Auto saved");
|
||||||
} else {
|
|
||||||
return I18n.tr("Unsaved changes");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return I18n.tr("Saved");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hasUnsavedChanges() ? I18n.tr("Unsaved changes") : I18n.tr("Saved");
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: {
|
color: {
|
||||||
if (autoSaveTimer.running) {
|
if (savingToDisk) {
|
||||||
return Theme.primary;
|
return Theme.primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUnsavedChanges()) {
|
if (currentTab && currentTab.isTemporary) {
|
||||||
return Theme.warning;
|
|
||||||
} else {
|
|
||||||
return Theme.success;
|
return Theme.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hasUnsavedChanges() ? Theme.warning : Theme.success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "info"
|
||||||
|
iconSize: Theme.iconSizeSmall
|
||||||
|
iconColor: root.showPathInfo ? Theme.primary : Theme.surfaceTextMedium
|
||||||
|
buttonSize: 20
|
||||||
|
onClicked: root.showPathInfo = !root.showPathInfo
|
||||||
}
|
}
|
||||||
opacity: textArea.text.length > 0 ? 1.0 : 0.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -902,6 +1085,38 @@ Column {
|
|||||||
onTriggered: syncContentToPlugin()
|
onTriggered: syncContentToPlugin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: externalWatch
|
||||||
|
path: (!root.externalWatchPaused && currentTab && !currentTab.isTemporary && currentTab.filePath) ? currentTab.filePath : ""
|
||||||
|
blockLoading: true
|
||||||
|
preload: true
|
||||||
|
watchChanges: true
|
||||||
|
|
||||||
|
onFileChanged: {
|
||||||
|
root.watcherReloadPending = true;
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (root.ignoreNextExternalChange) {
|
||||||
|
root.ignoreNextExternalChange = false;
|
||||||
|
root.lastSavedContent = externalWatch.text();
|
||||||
|
root.watcherReloadPending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.watcherReloadPending)
|
||||||
|
return;
|
||||||
|
root.watcherReloadPending = false;
|
||||||
|
if (!root.contentLoaded || !root.currentTab || root.currentTab.isTemporary)
|
||||||
|
return;
|
||||||
|
if (!root.surfaceVisible)
|
||||||
|
return;
|
||||||
|
root.applyDiskContent(externalWatch.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: error => {}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onBuiltInPluginSettingsChanged() {
|
function onBuiltInPluginSettingsChanged() {
|
||||||
@@ -910,4 +1125,24 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: NotepadStorageService
|
||||||
|
function onSessionBufferRevisionChanged() {
|
||||||
|
if (applyingShared || !contentLoaded || loadedTabId < 0)
|
||||||
|
return;
|
||||||
|
if (textArea.activeFocus)
|
||||||
|
return;
|
||||||
|
var buffer = NotepadStorageService.getSessionBuffer(loadedTabId);
|
||||||
|
if (buffer === undefined || buffer.content === textArea.text)
|
||||||
|
return;
|
||||||
|
if (textArea.text === lastSavedContent) {
|
||||||
|
applyingShared = true;
|
||||||
|
lastSavedContent = buffer.baseline;
|
||||||
|
textArea.text = buffer.content;
|
||||||
|
applyingShared = false;
|
||||||
|
syncContentToPlugin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,13 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
text: I18n.tr("Device list scroll volume")
|
||||||
|
description: I18n.tr("Allow adjusting device volume by scrolling on the right half of items in the device list")
|
||||||
|
checked: SettingsData.audioDeviceScrollVolumeEnabled
|
||||||
|
onToggled: checked => SettingsData.set("audioDeviceScrollVolumeEnabled", checked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ Item {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "keyboardLayoutNameShowIcon", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "showIdleInhibitorIcon", "showDoNotDisturbIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "hideWhenIdle"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "keyboardLayoutNameShowIcon", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "showIdleInhibitorIcon", "showDoNotDisturbIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "trayPopupSingleLine", "trayAutoOverflow", "trayMaxVisibleItems", "hideWhenIdle"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -803,6 +803,12 @@ Item {
|
|||||||
item.barShowOverflowBadge = widget.barShowOverflowBadge;
|
item.barShowOverflowBadge = widget.barShowOverflowBadge;
|
||||||
if (widget.trayUseInlineExpansion !== undefined)
|
if (widget.trayUseInlineExpansion !== undefined)
|
||||||
item.trayUseInlineExpansion = widget.trayUseInlineExpansion;
|
item.trayUseInlineExpansion = widget.trayUseInlineExpansion;
|
||||||
|
if (widget.trayPopupSingleLine !== undefined)
|
||||||
|
item.trayPopupSingleLine = widget.trayPopupSingleLine;
|
||||||
|
if (widget.trayAutoOverflow !== undefined)
|
||||||
|
item.trayAutoOverflow = widget.trayAutoOverflow;
|
||||||
|
if (widget.trayMaxVisibleItems !== undefined)
|
||||||
|
item.trayMaxVisibleItems = widget.trayMaxVisibleItems;
|
||||||
if (widget.hideWhenIdle !== undefined)
|
if (widget.hideWhenIdle !== undefined)
|
||||||
item.hideWhenIdle = widget.hideWhenIdle;
|
item.hideWhenIdle = widget.hideWhenIdle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Column {
|
|||||||
"id": widget.id,
|
"id": widget.id,
|
||||||
"enabled": widget.enabled
|
"enabled": widget.enabled
|
||||||
};
|
};
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "keyboardLayoutNameShowIcon", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "showIdleInhibitorIcon", "showDoNotDisturbIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion"];
|
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "keyboardLayoutNameShowIcon", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "showIdleInhibitorIcon", "showDoNotDisturbIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "trayPopupSingleLine", "trayAutoOverflow", "trayMaxVisibleItems"];
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
if (widget[keys[i]] !== undefined)
|
if (widget[keys[i]] !== undefined)
|
||||||
result[keys[i]] = widget[keys[i]];
|
result[keys[i]] = widget[keys[i]];
|
||||||
@@ -1126,6 +1126,188 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: trayPopupLineArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
opacity: (trayContextMenu.currentWidgetData?.trayUseInlineExpansion ?? false) ? 0.55 : 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "view_week"
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Single-Line Popup")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: trayPopupLineToggle
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 40
|
||||||
|
height: 20
|
||||||
|
checked: trayContextMenu.currentWidgetData?.trayPopupSingleLine ?? SettingsData.trayPopupSingleLine
|
||||||
|
enabled: !(trayContextMenu.currentWidgetData?.trayUseInlineExpansion ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayPopupLineArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: (trayContextMenu.currentWidgetData?.trayUseInlineExpansion ?? false) ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (trayContextMenu.currentWidgetData?.trayUseInlineExpansion ?? false)
|
||||||
|
return;
|
||||||
|
const newValue = !(trayContextMenu.currentWidgetData?.trayPopupSingleLine ?? SettingsData.trayPopupSingleLine);
|
||||||
|
root.overflowSettingChanged(trayContextMenu.sectionId, trayContextMenu.widgetIndex, "trayPopupSingleLine", newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: trayAutoOverflowArea.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: "responsive_layout"
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Auto Overflow")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: trayAutoOverflowToggle
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 40
|
||||||
|
height: 20
|
||||||
|
checked: trayContextMenu.currentWidgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayAutoOverflowArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
const newValue = !(trayContextMenu.currentWidgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow);
|
||||||
|
root.overflowSettingChanged(trayContextMenu.sectionId, trayContextMenu.widgetIndex, "trayAutoOverflow", newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: trayMaxVisibleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
opacity: (trayContextMenu.currentWidgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow) ? 1 : 0.55
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "low_priority"
|
||||||
|
size: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Max Visible")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const value = trayContextMenu.currentWidgetData?.trayMaxVisibleItems ?? SettingsData.trayMaxVisibleItems;
|
||||||
|
return value > 0 ? String(value) : I18n.tr("Auto");
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
buttonSize: 28
|
||||||
|
iconName: "remove"
|
||||||
|
iconSize: 16
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
enabled: trayContextMenu.currentWidgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow
|
||||||
|
onClicked: {
|
||||||
|
const current = trayContextMenu.currentWidgetData?.trayMaxVisibleItems ?? SettingsData.trayMaxVisibleItems;
|
||||||
|
root.overflowSettingChanged(trayContextMenu.sectionId, trayContextMenu.widgetIndex, "trayMaxVisibleItems", Math.max(0, current - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
buttonSize: 28
|
||||||
|
iconName: "add"
|
||||||
|
iconSize: 16
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
enabled: trayContextMenu.currentWidgetData?.trayAutoOverflow ?? SettingsData.trayAutoOverflow
|
||||||
|
onClicked: {
|
||||||
|
const current = trayContextMenu.currentWidgetData?.trayMaxVisibleItems ?? SettingsData.trayMaxVisibleItems;
|
||||||
|
root.overflowSettingChanged(trayContextMenu.sectionId, trayContextMenu.widgetIndex, "trayMaxVisibleItems", Math.min(20, current + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayMaxVisibleArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ Singleton {
|
|||||||
return SessionData.deviceMaxVolumes[name] ?? 100;
|
return SessionData.deviceMaxVolumes[name] ?? 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property int wheelVolumeStep: SettingsData.audioWheelScrollAmount
|
||||||
|
|
||||||
signal micMuteChanged
|
signal micMuteChanged
|
||||||
signal audioOutputCycled(string deviceName, string deviceIcon)
|
signal audioOutputCycled(string deviceName, string deviceIcon)
|
||||||
signal deviceAliasChanged(string nodeName, string newAlias)
|
signal deviceAliasChanged(string nodeName, string newAlias)
|
||||||
@@ -156,14 +158,19 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cycleAudioOutput() {
|
function cycleAudioOutputDirection(forward) {
|
||||||
const sinks = getAvailableSinks();
|
const sinks = getAvailableSinks();
|
||||||
if (sinks.length < 2)
|
if (sinks.length < 2)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const currentName = root.sink?.name ?? "";
|
const currentName = root.sink?.name ?? "";
|
||||||
const currentIndex = sinks.findIndex(s => s.name === currentName);
|
const currentIndex = sinks.findIndex(s => s.name === currentName);
|
||||||
const nextIndex = (currentIndex + 1) % sinks.length;
|
let nextIndex;
|
||||||
|
if (forward) {
|
||||||
|
nextIndex = (currentIndex + 1) % sinks.length;
|
||||||
|
} else {
|
||||||
|
nextIndex = (currentIndex - 1 + sinks.length) % sinks.length;
|
||||||
|
}
|
||||||
const nextSink = sinks[nextIndex];
|
const nextSink = sinks[nextIndex];
|
||||||
setDefaultSinkByName(nextSink.name);
|
setDefaultSinkByName(nextSink.name);
|
||||||
const name = displayName(nextSink);
|
const name = displayName(nextSink);
|
||||||
@@ -171,6 +178,10 @@ Singleton {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cycleAudioOutput() {
|
||||||
|
return cycleAudioOutputDirection(true);
|
||||||
|
}
|
||||||
|
|
||||||
function getDeviceAlias(nodeName) {
|
function getDeviceAlias(nodeName) {
|
||||||
if (!nodeName)
|
if (!nodeName)
|
||||||
return null;
|
return null;
|
||||||
@@ -833,6 +844,28 @@ EOFCONFIG
|
|||||||
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
|
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleNodeVolumeWheel(node, wheelEvent) {
|
||||||
|
if (!node?.audio)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SessionData.suppressOSDTemporarily();
|
||||||
|
const delta = wheelEvent.angleDelta.y;
|
||||||
|
if (delta === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const current = Math.round(node.audio.volume * 100);
|
||||||
|
const maxVol = getMaxVolumePercent(node);
|
||||||
|
const newVolume = delta > 0 ? Math.min(maxVol, current + root.wheelVolumeStep) : Math.max(0, current - root.wheelVolumeStep);
|
||||||
|
|
||||||
|
node.audio.muted = false;
|
||||||
|
node.audio.volume = newVolume / 100;
|
||||||
|
|
||||||
|
if (node === sink) {
|
||||||
|
playVolumeChangeSoundIfEnabled();
|
||||||
|
}
|
||||||
|
wheelEvent.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
function setMicVolume(percentage) {
|
function setMicVolume(percentage) {
|
||||||
if (!root.source?.audio) {
|
if (!root.source?.audio) {
|
||||||
return "No audio source available";
|
return "No audio source available";
|
||||||
|
|||||||
@@ -23,6 +23,49 @@ Singleton {
|
|||||||
property var tabsBeingCreated: ({})
|
property var tabsBeingCreated: ({})
|
||||||
property bool metadataLoaded: false
|
property bool metadataLoaded: false
|
||||||
|
|
||||||
|
// Shared live edit state across slideout and popout surfaces.
|
||||||
|
property var sessionBuffers: ({})
|
||||||
|
property int sessionBufferRevision: 0
|
||||||
|
|
||||||
|
function setSessionBuffer(tabId, content, baseline) {
|
||||||
|
if (tabId === undefined || tabId === null || tabId < 0)
|
||||||
|
return
|
||||||
|
var next = Object.assign({}, sessionBuffers)
|
||||||
|
if (content !== baseline) {
|
||||||
|
next[tabId] = { content: content, baseline: baseline }
|
||||||
|
} else {
|
||||||
|
delete next[tabId]
|
||||||
|
}
|
||||||
|
sessionBuffers = next
|
||||||
|
sessionBufferRevision++
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSessionBuffer(tabId) {
|
||||||
|
return sessionBuffers[tabId]
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSessionBuffer(tabId) {
|
||||||
|
if (sessionBuffers[tabId] === undefined)
|
||||||
|
return
|
||||||
|
var next = Object.assign({}, sessionBuffers)
|
||||||
|
delete next[tabId]
|
||||||
|
sessionBuffers = next
|
||||||
|
sessionBufferRevision++
|
||||||
|
}
|
||||||
|
|
||||||
|
property var conflictTabId: -1
|
||||||
|
property string conflictDiskContent: ""
|
||||||
|
|
||||||
|
function flagConflict(tabId, diskContent) {
|
||||||
|
conflictDiskContent = diskContent
|
||||||
|
conflictTabId = tabId
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearConflict() {
|
||||||
|
conflictTabId = -1
|
||||||
|
conflictDiskContent = ""
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
ensureDirectories()
|
ensureDirectories()
|
||||||
}
|
}
|
||||||
@@ -209,6 +252,10 @@ Singleton {
|
|||||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||||
|
|
||||||
var newTabs = tabs.slice()
|
var newTabs = tabs.slice()
|
||||||
|
var closedTabId = newTabs[tabIndex] ? newTabs[tabIndex].id : -1
|
||||||
|
clearSessionBuffer(closedTabId)
|
||||||
|
if (conflictTabId === closedTabId)
|
||||||
|
clearConflict()
|
||||||
|
|
||||||
if (newTabs.length <= 1) {
|
if (newTabs.length <= 1) {
|
||||||
var id = Date.now()
|
var id = Date.now()
|
||||||
|
|||||||
@@ -789,21 +789,97 @@ Singleton {
|
|||||||
networkInfoModal?.close();
|
networkInfoModal?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openNotepad() {
|
function closeNotepadSlideouts() {
|
||||||
|
for (var i = 0; i < notepadSlideouts.length; i++) {
|
||||||
|
if (notepadSlideouts[i] && notepadSlideouts[i].isVisible)
|
||||||
|
notepadSlideouts[i].hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNotepadSlideout() {
|
||||||
|
notepadPopout?.hide();
|
||||||
if (notepadSlideouts.length > 0) {
|
if (notepadSlideouts.length > 0) {
|
||||||
notepadSlideouts[0]?.show();
|
notepadSlideouts[0]?.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep the notepad in a single presentation for default modes
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onNotepadDefaultModeChanged() {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
var hadSlideout = false;
|
||||||
|
for (var i = 0; i < root.notepadSlideouts.length; i++) {
|
||||||
|
if (root.notepadSlideouts[i] && root.notepadSlideouts[i].isVisible) {
|
||||||
|
hadSlideout = true;
|
||||||
|
root.notepadSlideouts[i].hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hadSlideout)
|
||||||
|
root.openNotepadPopout();
|
||||||
|
} else if (root.notepadPopout && root.notepadPopout.visible) {
|
||||||
|
root.notepadPopout.hide();
|
||||||
|
root.openNotepadSlideout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNotepad() {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
openNotepadPopout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openNotepadSlideout();
|
||||||
|
}
|
||||||
|
|
||||||
function closeNotepad() {
|
function closeNotepad() {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
notepadPopout?.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (notepadSlideouts.length > 0) {
|
if (notepadSlideouts.length > 0) {
|
||||||
notepadSlideouts[0]?.hide();
|
notepadSlideouts[0]?.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleNotepad() {
|
function toggleNotepad() {
|
||||||
|
if (SettingsData.notepadDefaultMode === "popout") {
|
||||||
|
toggleNotepadPopout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (notepadSlideouts.length > 0) {
|
if (notepadSlideouts.length > 0) {
|
||||||
notepadSlideouts[0]?.toggle();
|
notepadSlideouts[0]?.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var notepadPopout: null
|
||||||
|
property var notepadPopoutLoader: null
|
||||||
|
property bool _notepadPopoutWantsOpen: false
|
||||||
|
|
||||||
|
function openNotepadPopout() {
|
||||||
|
closeNotepadSlideouts();
|
||||||
|
if (notepadPopout) {
|
||||||
|
notepadPopout.show();
|
||||||
|
} else if (notepadPopoutLoader) {
|
||||||
|
_notepadPopoutWantsOpen = true;
|
||||||
|
notepadPopoutLoader.active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onNotepadPopoutLoaded() {
|
||||||
|
if (_notepadPopoutWantsOpen && notepadPopout) {
|
||||||
|
_notepadPopoutWantsOpen = false;
|
||||||
|
notepadPopout.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleNotepadPopout() {
|
||||||
|
if (notepadPopout) {
|
||||||
|
if (!notepadPopout.visible)
|
||||||
|
closeNotepadSlideouts();
|
||||||
|
notepadPopout.toggle();
|
||||||
|
} else {
|
||||||
|
openNotepadPopout();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ Row {
|
|||||||
property var initialSelection: []
|
property var initialSelection: []
|
||||||
property var currentSelection: initialSelection
|
property var currentSelection: initialSelection
|
||||||
property bool checkEnabled: true
|
property bool checkEnabled: true
|
||||||
property int buttonHeight: 40
|
property string size: "medium"
|
||||||
property int minButtonWidth: 64
|
property int buttonHeight: size === "small" ? 32 : 40
|
||||||
property int buttonPadding: Theme.spacingL
|
property int minButtonWidth: size === "small" ? 56 : 64
|
||||||
property int checkIconSize: Theme.iconSizeSmall
|
property int buttonPadding: size === "small" ? Theme.spacingM : Theme.spacingL
|
||||||
property int textSize: Theme.fontSizeMedium
|
property int checkIconSize: size === "small" ? Theme.iconSizeSmall - 2 : Theme.iconSizeSmall
|
||||||
|
property int textSize: size === "small" ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||||
property bool userInteracted: false
|
property bool userInteracted: false
|
||||||
|
|
||||||
signal selectionChanged(int index, bool selected)
|
signal selectionChanged(int index, bool selected)
|
||||||
|
|||||||
@@ -16,21 +16,28 @@ PanelWindow {
|
|||||||
property var targetScreen: null
|
property var targetScreen: null
|
||||||
property var modelData: null
|
property var modelData: null
|
||||||
property bool triggerUsesOverlayLayer: false
|
property bool triggerUsesOverlayLayer: false
|
||||||
|
// Drop off the Overlay layer (back to Top) while an overlay modal
|
||||||
|
property bool suppressOverlayLayer: false
|
||||||
property real slideoutWidth: 480
|
property real slideoutWidth: 480
|
||||||
property bool expandable: false
|
property bool expandable: false
|
||||||
property bool expandedWidth: false
|
property bool expandedWidth: false
|
||||||
property real expandedWidthValue: 960
|
property real expandedWidthValue: 960
|
||||||
|
property real edgeGap: 0
|
||||||
|
property string slideEdge: "right"
|
||||||
|
readonly property bool slideFromLeft: slideEdge === "left"
|
||||||
property Component content: null
|
property Component content: null
|
||||||
property string title: ""
|
property string title: ""
|
||||||
property alias container: contentContainer
|
property alias container: contentContainer
|
||||||
property real customTransparency: -1
|
property real customTransparency: -1
|
||||||
property bool mappedVisible: false
|
property bool mappedVisible: false
|
||||||
signal aboutToHide
|
signal aboutToHide
|
||||||
|
signal revealed
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
mappedVisible = true;
|
mappedVisible = true;
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
isVisible = true;
|
isVisible = true;
|
||||||
|
revealed();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +59,9 @@ PanelWindow {
|
|||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.bottom: true
|
anchors.bottom: true
|
||||||
anchors.right: true
|
anchors.right: !root.slideFromLeft
|
||||||
|
anchors.left: root.slideFromLeft
|
||||||
|
|
||||||
// Expandable: fixed max surface width; strip width is slideContainer only (keeps blur/mask aligned).
|
|
||||||
implicitWidth: expandable ? expandedWidthValue : slideoutWidth
|
implicitWidth: expandable ? expandedWidthValue : slideoutWidth
|
||||||
implicitHeight: modelData ? modelData.height : 800
|
implicitHeight: modelData ? modelData.height : 800
|
||||||
|
|
||||||
@@ -62,21 +69,22 @@ PanelWindow {
|
|||||||
|
|
||||||
readonly property bool slideoutBlurActive: root.visible && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
|
readonly property bool slideoutBlurActive: root.visible && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
|
||||||
|
|
||||||
WlrLayershell.layer: (triggerUsesOverlayLayer || CompositorService.framePeerSurfacesUseOverlayForScreen(modelData)) ? WlrLayershell.Overlay : WlrLayershell.Top
|
WlrLayershell.layer: (!suppressOverlayLayer && (triggerUsesOverlayLayer || CompositorService.framePeerSurfacesUseOverlayForScreen(modelData))) ? WlrLayershell.Overlay : WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: 0
|
WlrLayershell.exclusiveZone: 0
|
||||||
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||||
|
|
||||||
readonly property real dpr: CompositorService.getScreenScale(root.screen)
|
readonly property real dpr: CompositorService.getScreenScale(root.screen)
|
||||||
readonly property real alignedWidth: Theme.px(expandable && expandedWidth ? expandedWidthValue : slideoutWidth, dpr)
|
readonly property real alignedWidth: Theme.px(expandable && expandedWidth ? expandedWidthValue : slideoutWidth, dpr)
|
||||||
readonly property real alignedHeight: Theme.px(modelData ? modelData.height : 800, dpr)
|
readonly property real alignedHeight: Theme.px(modelData ? modelData.height : 800, dpr)
|
||||||
|
readonly property real alignedEdgeGap: Theme.px(edgeGap, dpr)
|
||||||
readonly property real slideoutSlideSnapX: Theme.snap(slideContainer.slideOffset, dpr)
|
readonly property real slideoutSlideSnapX: Theme.snap(slideContainer.slideOffset, dpr)
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: Rectangle {
|
item: Rectangle {
|
||||||
x: root.width - slideContainer.width
|
x: root.slideFromLeft ? root.alignedEdgeGap : (root.width - slideContainer.width - root.alignedEdgeGap)
|
||||||
y: 0
|
y: root.alignedEdgeGap
|
||||||
width: slideContainer.width
|
width: slideContainer.width
|
||||||
height: root.height
|
height: root.height - root.alignedEdgeGap * 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,16 +92,21 @@ PanelWindow {
|
|||||||
id: slideContainer
|
id: slideContainer
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: root.slideFromLeft ? undefined : parent.right
|
||||||
|
anchors.left: root.slideFromLeft ? parent.left : undefined
|
||||||
|
anchors.topMargin: root.alignedEdgeGap
|
||||||
|
anchors.bottomMargin: root.alignedEdgeGap
|
||||||
|
anchors.rightMargin: root.alignedEdgeGap
|
||||||
|
anchors.leftMargin: root.alignedEdgeGap
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.alignedHeight
|
height: root.alignedHeight - root.alignedEdgeGap * 2
|
||||||
|
|
||||||
property real slideOffset: root.alignedWidth
|
property real slideOffset: root.slideFromLeft ? -root.alignedWidth : root.alignedWidth
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root
|
target: root
|
||||||
function onIsVisibleChanged() {
|
function onIsVisibleChanged() {
|
||||||
slideContainer.slideOffset = root.isVisible ? 0 : slideContainer.width;
|
slideContainer.slideOffset = root.isVisible ? 0 : (root.slideFromLeft ? -slideContainer.width : slideContainer.width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +124,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expandable only; mask/blur bind to slideContainer geometry so they track this animation.
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
enabled: root.expandable
|
enabled: root.expandable
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -217,7 +229,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blur region from slideContainer (not layered contentRect); position uses x + slideoutSlideSnapX, not mapToItem(root).
|
|
||||||
WindowBlur {
|
WindowBlur {
|
||||||
targetWindow: root
|
targetWindow: root
|
||||||
blurX: root.slideoutBlurActive ? slideContainer.x + root.slideoutSlideSnapX : 0
|
blurX: root.slideoutBlurActive ? slideContainer.x + root.slideoutSlideSnapX : 0
|
||||||
|
|||||||
Reference in New Issue
Block a user