1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-02 02:22:06 -04:00

Compare commits

...

7 Commits

Author SHA1 Message Date
purian23
8e047f45f5 (notepad): Update tab loading with request ID validation 2026-03-18 00:10:35 -04:00
purian23
fbe8cbb23f DMS Shell: Remove dup import 2026-03-18 00:09:48 -04:00
purian23
28315a165f feat(Notepad): Implement tab reordering via drag-and-drop functionality 2026-03-18 00:09:19 -04:00
purian23
1b32829dac refactor(Notepad): Streamline hide behavior & auto-save function 2026-03-17 23:23:17 -04:00
purian23
1fce29324f dankinstall(debian): Minor update to ARM64 support 2026-03-17 21:23:01 -04:00
purian23
1fab90178a (greeter): Revise dir perms and add validations 2026-03-17 20:38:37 -04:00
bbedward
eb04ab7dca launcher v2: simplify screen change bindings 2026-03-17 20:31:55 -04:00
14 changed files with 310 additions and 115 deletions

View File

@@ -1490,6 +1490,19 @@ func checkGreeterStatus() error {
}
if stat, err := os.Stat(cacheDir); err == nil && stat.IsDir() {
fmt.Printf(" ✓ %s exists\n", cacheDir)
requiredSubdirs := []string{".local/state", ".local/share", ".cache"}
missingSubdirs := false
for _, sub := range requiredSubdirs {
subPath := filepath.Join(cacheDir, sub)
if _, err := os.Stat(subPath); os.IsNotExist(err) {
fmt.Printf(" ⚠ Missing required subdir: %s\n", subPath)
missingSubdirs = true
}
}
if missingSubdirs {
fmt.Println(" Run 'dms greeter sync' to initialize the cache directory structure.")
allGood = false
}
} else {
fmt.Printf(" ✗ %s not found\n", cacheDir)
fmt.Printf(" %s\n", packageInstallHint())

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os/exec"
"runtime"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
@@ -113,6 +112,17 @@ func containsString(values []string, target string) bool {
return false
}
func debianRepoArchitecture(arch string) string {
switch arch {
case "amd64", "x86_64":
return "amd64"
case "arm64", "aarch64":
return "arm64"
default:
return arch
}
}
func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
return d.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant))
}
@@ -460,7 +470,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
}
// Add repository
repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, debianRepoArchitecture(osInfo.Architecture), baseURL)
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,

View File

@@ -9,7 +9,7 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
Package: dms
Architecture: amd64
Architecture: amd64 arm64
Depends: ${misc:Depends},
quickshell | quickshell-git,
accountsservice,

View File

@@ -1,2 +1,3 @@
dms-distropkg-amd64.gz
dms-distropkg-arm64.gz
dms-source.tar.gz

View File

@@ -1,4 +1,5 @@
# Include files that are normally excluded by .gitignore
# These are needed for the build process on Launchpad
tar-ignore = !dms-distropkg-amd64.gz
tar-ignore = !dms-distropkg-arm64.gz
tar-ignore = !dms-source.tar.gz

View File

@@ -7,7 +7,6 @@ import qs.Modals.Clipboard
import qs.Modals.Greeter
import qs.Modals.Settings
import qs.Modals.DankLauncherV2
import qs.Modals
import qs.Modules
import qs.Modules.AppDrawer
import qs.Modules.DankDash
@@ -820,9 +819,8 @@ Item {
content: Component {
Notepad {
onHideRequested: {
notepadSlideout.hide();
}
slideout: notepadSlideout
onHideRequested: notepadSlideout.hide()
}
}

View File

@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
@@ -17,7 +16,6 @@ Item {
property var spotlightContent: launcherContentLoader.item
property bool openedFromOverview: false
property bool isClosing: false
property bool _windowEnabled: true
property bool _pendingInitialize: false
property string _pendingQuery: ""
property string _pendingMode: ""
@@ -267,38 +265,26 @@ Item {
if (Quickshell.screens.length === 0)
return;
const screen = launcherWindow.screen;
const screenName = screen?.name;
let needsReset = !screen || !screenName;
if (!needsReset) {
needsReset = true;
const screenName = launcherWindow.screen?.name;
if (screenName) {
for (let i = 0; i < Quickshell.screens.length; i++) {
if (Quickshell.screens[i].name === screenName) {
needsReset = false;
break;
}
if (Quickshell.screens[i].name === screenName)
return;
}
}
if (!needsReset)
return;
if (spotlightOpen)
hide();
const newScreen = CompositorService.getFocusedScreen() ?? Quickshell.screens[0];
if (!newScreen)
return;
root._windowEnabled = false;
launcherWindow.screen = newScreen;
Qt.callLater(() => {
root._windowEnabled = true;
});
if (newScreen)
launcherWindow.screen = newScreen;
}
}
PanelWindow {
id: launcherWindow
visible: root._windowEnabled && (spotlightOpen || isClosing)
visible: spotlightOpen || isClosing
color: "transparent"
exclusionMode: ExclusionMode.Ignore

View File

@@ -22,7 +22,6 @@ Singleton {
property bool nightModeEnabled: false
Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", greetCfgDir]);
loadMemory();
loadSessionConfig();
}

View File

@@ -87,8 +87,7 @@ exec_compositor() {
exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info) 2>&1
fi
local log_file="$CACHE_DIR/$log_tag.log"
exec "$@" >> "$log_file" 2>&1
exec "$@"
}
while [[ $# -gt 0 ]]; do
@@ -180,19 +179,10 @@ export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export EGL_PLATFORM=gbm
export DMS_RUN_GREETER=1
ensure_cache_tree() {
local base="$1"
mkdir -p "$base/.local/state" "$base/.local/share" "$base/.cache"
}
if ! ensure_cache_tree "$CACHE_DIR" 2>/dev/null; then
FALLBACK_CACHE_DIR="/tmp/dms-greeter-${UID:-$(id -u)}"
echo "Warning: cache directory '$CACHE_DIR' is not writable; falling back to '$FALLBACK_CACHE_DIR'" >&2
CACHE_DIR="$FALLBACK_CACHE_DIR"
if ! ensure_cache_tree "$CACHE_DIR"; then
echo "Error: failed to initialize fallback cache directory '$CACHE_DIR'" >&2
exit 1
fi
if [[ ! -d "$CACHE_DIR" ]]; then
echo "Error: cache directory '$CACHE_DIR' does not exist." >&2
echo " Run 'dms greeter sync' to initialize it, or pass --cache-dir to an existing directory." >&2
exit 1
fi
export DMS_GREET_CFG_DIR="$CACHE_DIR"

View File

@@ -21,6 +21,7 @@ Item {
property var currentTab: NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null
property bool showSettingsMenu: false
property string pendingSaveContent: ""
property var slideout: null
signal hideRequested
signal previewRequested(string content)
@@ -29,6 +30,14 @@ Item {
service: NotepadStorageService
}
Connections {
target: slideout
enabled: slideout !== null
function onAboutToHide() {
textEditor.autoSaveToSession()
}
}
function hasUnsavedChanges() {
return textEditor.hasUnsavedChanges();
}
@@ -204,7 +213,8 @@ Item {
}
onEscapePressed: {
root.hideRequested();
textEditor.autoSaveToSession()
root.hideRequested()
}
onSettingsRequested: {

View File

@@ -10,6 +10,10 @@ Column {
property var currentTab: NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null
property bool contentLoaded: false
property int draggedIndex: -1
property int dropTargetIndex: -1
property bool suppressShiftAnimation: false
readonly property real tabItemSize: 128 + Theme.spacingXS
signal tabSwitched(int tabIndex)
signal tabClosed(int tabIndex)
@@ -46,92 +50,221 @@ Column {
Repeater {
model: NotepadStorageService.tabs
delegate: Rectangle {
delegate: Item {
id: delegateItem
required property int index
required property var modelData
readonly property bool isActive: NotepadStorageService.currentTabIndex === index
readonly property bool isHovered: tabMouseArea.containsMouse && !closeMouseArea.containsMouse
readonly property real tabWidth: 128
property bool longPressing: false
property bool dragging: false
property point dragStartPos: Qt.point(0, 0)
property int targetIndex: -1
property int originalIndex: -1
property real dragAxisOffset: 0
Timer {
id: longPressTimer
interval: 200
repeat: false
onTriggered: {
if (NotepadStorageService.tabs.length > 1) {
delegateItem.longPressing = true
}
}
}
readonly property real shiftOffset: {
if (root.draggedIndex < 0)
return 0
if (index === root.draggedIndex)
return 0
var dragIdx = root.draggedIndex
var dropIdx = root.dropTargetIndex
var myIdx = index
var shiftAmount = root.tabItemSize
if (dropIdx < 0)
return 0
if (dragIdx < dropIdx && myIdx > dragIdx && myIdx <= dropIdx)
return -shiftAmount
if (dragIdx > dropIdx && myIdx >= dropIdx && myIdx < dragIdx)
return shiftAmount
return 0
}
width: tabWidth
height: 32
radius: Theme.cornerRadius
color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryPressed, 0)
border.width: isActive ? 0 : 1
border.color: Theme.outlineMedium
clip: true
z: dragging ? 100 : 0
transform: Translate {
x: shiftOffset
Behavior on x {
enabled: !root.suppressShiftAnimation
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
}
Item {
id: tabVisual
anchors.fill: parent
z: 1
layer.enabled: dragging
layer.smooth: true
transform: Translate {
x: dragging ? dragAxisOffset : 0
}
Rectangle {
id: tabRect
anchors.fill: parent
radius: Theme.cornerRadius
color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryPressed, 0)
border.width: isActive || dragging ? 0 : 1
border.color: dragging ? Theme.primary : Theme.outlineMedium
clip: true
Row {
id: tabContent
anchors.fill: parent
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingXS
StyledText {
id: tabText
width: parent.width - (tabCloseButton.visible ? tabCloseButton.width + Theme.spacingXS : 0)
text: {
var prefix = ""
if (hasUnsavedChangesForTab(modelData)) {
prefix = "● "
}
return prefix + (modelData.title || "Untitled")
}
font.pixelSize: Theme.fontSizeSmall
color: isActive ? Theme.primary : Theme.surfaceText
font.weight: isActive ? Font.Medium : Font.Normal
elide: Text.ElideMiddle
maximumLineCount: 1
wrapMode: Text.NoWrap
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: tabCloseButton
width: 20
height: 20
radius: Theme.cornerRadius
color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
visible: NotepadStorageService.tabs.length > 1
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "close"
size: 14
color: Theme.surfaceTextMedium
anchors.centerIn: parent
}
MouseArea {
id: closeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
z: 100
onClicked: root.tabClosed(index)
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
MouseArea {
id: tabMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
preventStealing: dragging || longPressing
cursorShape: dragging || longPressing ? Qt.ClosedHandCursor : Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onClicked: root.tabSwitched(index)
}
Row {
id: tabContent
anchors.fill: parent
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingXS
StyledText {
id: tabText
width: parent.width - (tabCloseButton.visible ? tabCloseButton.width + Theme.spacingXS : 0)
text: {
var prefix = "";
if (hasUnsavedChangesForTab(modelData)) {
prefix = "● ";
}
return prefix + (modelData.title || "Untitled");
}
font.pixelSize: Theme.fontSizeSmall
color: isActive ? Theme.primary : Theme.surfaceText
font.weight: isActive ? Font.Medium : Font.Normal
elide: Text.ElideMiddle
maximumLineCount: 1
wrapMode: Text.NoWrap
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: tabCloseButton
width: 20
height: 20
radius: Theme.cornerRadius
color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
visible: NotepadStorageService.tabs.length > 1
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "close"
size: 14
color: Theme.surfaceTextMedium
anchors.centerIn: parent
}
MouseArea {
id: closeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
z: 100
onClicked: {
root.tabClosed(index);
}
onPressed: mouse => {
if (mouse.button === Qt.LeftButton && NotepadStorageService.tabs.length > 1) {
delegateItem.dragStartPos = Qt.point(mouse.x, mouse.y)
longPressTimer.start()
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
onReleased: mouse => {
longPressTimer.stop()
var wasDragging = delegateItem.dragging
var didReorder = wasDragging && delegateItem.targetIndex >= 0 && delegateItem.targetIndex !== delegateItem.originalIndex
if (didReorder) {
root.suppressShiftAnimation = true
NotepadStorageService.reorderTab(delegateItem.originalIndex, delegateItem.targetIndex)
}
delegateItem.longPressing = false
delegateItem.dragging = false
delegateItem.dragAxisOffset = 0
delegateItem.targetIndex = -1
delegateItem.originalIndex = -1
root.draggedIndex = -1
root.dropTargetIndex = -1
if (didReorder) {
Qt.callLater(() => {
root.suppressShiftAnimation = false
})
}
if (wasDragging || mouse.button !== Qt.LeftButton)
return
root.tabSwitched(index)
}
onPositionChanged: mouse => {
if (delegateItem.longPressing && !delegateItem.dragging) {
var distance = Math.sqrt(Math.pow(mouse.x - delegateItem.dragStartPos.x, 2) + Math.pow(mouse.y - delegateItem.dragStartPos.y, 2))
if (distance > 5) {
delegateItem.dragging = true
delegateItem.targetIndex = index
delegateItem.originalIndex = index
root.draggedIndex = index
root.dropTargetIndex = index
}
}
if (!delegateItem.dragging)
return
var axisOffset = mouse.x - delegateItem.dragStartPos.x
delegateItem.dragAxisOffset = axisOffset
var itemSize = root.tabItemSize
var rawSlot = axisOffset / itemSize
var slotOffset = rawSlot >= 0
? Math.floor(rawSlot + 0.4)
: Math.ceil(rawSlot - 0.4)
var tabCount = NotepadStorageService.tabs.length
var newTargetIndex = Math.max(0, Math.min(tabCount - 1, delegateItem.originalIndex + slotOffset))
if (newTargetIndex !== delegateItem.targetIndex) {
delegateItem.targetIndex = newTargetIndex
root.dropTargetIndex = newTargetIndex
}
}
}
}

View File

@@ -31,6 +31,7 @@ Column {
property string previewMode: "split" // split | full
property string pluginHighlightedHtml: ""
property string lastPluginContent: ""
property int loadRequestId: 0
signal saveRequested()
signal openRequested()
@@ -54,10 +55,18 @@ Column {
function loadCurrentTabContent() {
if (!currentTab) return
const requestedTabId = currentTab.id
const requestId = ++loadRequestId
contentLoaded = false
NotepadStorageService.loadTabContent(
NotepadStorageService.currentTabIndex,
(content) => {
const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex
? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex]
: null
if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId)
return
lastSavedContent = content
textArea.text = content
contentLoaded = true
@@ -555,7 +564,9 @@ Column {
})
}
function onTabsChanged() {
if (NotepadStorageService.tabs.length > 0 && !contentLoaded) {/* Lines 444-445 omitted */}
if (NotepadStorageService.tabs.length > 0 && !contentLoaded) {
loadCurrentTabContent()
}
}
}

View File

@@ -102,6 +102,14 @@ Singleton {
metadataFile.setText(JSON.stringify(metadata, null, 2))
}
function getTabById(tabId) {
for (var i = 0; i < tabs.length; i++) {
if (tabs[i].id === tabId)
return tabs[i]
}
return null
}
function loadTabContent(tabIndex, callback) {
if (tabIndex < 0 || tabIndex >= tabs.length) {
callback("")
@@ -109,6 +117,7 @@ Singleton {
}
var tab = tabs[tabIndex]
var requestTabId = tab.id
var fullPath = tab.isTemporary
? baseDir + "/" + tab.filePath
: tab.filePath
@@ -123,6 +132,16 @@ Singleton {
var fileChecker = fileExistsComponent.createObject(root, {
path: fullPath,
callback: (exists) => {
var currentTab = root.getTabById(requestTabId)
var currentPath = currentTab
? (currentTab.isTemporary ? baseDir + "/" + currentTab.filePath : currentTab.filePath)
: ""
if (!currentTab || currentPath !== fullPath) {
callback("")
return
}
if (exists) {
var loader = tabFileLoaderComponent.createObject(root, {
path: fullPath,
@@ -241,6 +260,28 @@ Singleton {
saveMetadata()
}
function reorderTab(fromIndex, toIndex) {
if (fromIndex < 0 || fromIndex >= tabs.length || toIndex < 0 || toIndex >= tabs.length)
return
if (fromIndex === toIndex)
return
var newTabs = tabs.slice()
var moved = newTabs.splice(fromIndex, 1)[0]
newTabs.splice(toIndex, 0, moved)
tabs = newTabs
if (currentTabIndex === fromIndex) {
currentTabIndex = toIndex
} else if (fromIndex < currentTabIndex && toIndex >= currentTabIndex) {
currentTabIndex--
} else if (fromIndex > currentTabIndex && toIndex <= currentTabIndex) {
currentTabIndex++
}
saveMetadata()
}
function saveTabAs(tabIndex, userPath) {
if (tabIndex < 0 || tabIndex >= tabs.length) return

View File

@@ -25,6 +25,7 @@ PanelWindow {
property string title: ""
property alias container: contentContainer
property real customTransparency: -1
signal aboutToHide
function show() {
visible = true
@@ -32,6 +33,7 @@ PanelWindow {
}
function hide() {
aboutToHide()
isVisible = false
}