mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-01 19:18:28 -04:00
notepad: add inline tab renaming and bump tab size
This commit is contained in:
@@ -120,12 +120,18 @@ Singleton {
|
||||
|
||||
function setMonitorScrollPosition(screenName, scrollX, scrollY) {
|
||||
var newPositions = Object.assign({}, monitorScrollPositions);
|
||||
newPositions[screenName] = { scrollX: scrollX, scrollY: scrollY };
|
||||
newPositions[screenName] = {
|
||||
scrollX: scrollX,
|
||||
scrollY: scrollY
|
||||
};
|
||||
monitorScrollPositions = newPositions;
|
||||
}
|
||||
|
||||
function getMonitorScrollPosition(screenName) {
|
||||
return monitorScrollPositions[screenName] || { scrollX: 50, scrollY: 50 };
|
||||
return monitorScrollPositions[screenName] || {
|
||||
scrollX: 50,
|
||||
scrollY: 50
|
||||
};
|
||||
}
|
||||
|
||||
function clearMonitorScrollPosition(screenName) {
|
||||
@@ -209,6 +215,8 @@ Singleton {
|
||||
property string locale: ""
|
||||
property string timeLocale: ""
|
||||
|
||||
property string notepadLastMode: ""
|
||||
|
||||
property string launcherLastMode: "all"
|
||||
property string launcherLastFileSearchType: "all"
|
||||
property string launcherLastQuery: ""
|
||||
@@ -1216,6 +1224,13 @@ Singleton {
|
||||
I18n.useLocale(locale, locale.startsWith("en") ? "" : I18n.folder + "/" + locale + ".json");
|
||||
}
|
||||
|
||||
function setNotepadLastMode(mode) {
|
||||
if (notepadLastMode === mode)
|
||||
return;
|
||||
notepadLastMode = mode;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function setLauncherLastMode(mode) {
|
||||
launcherLastMode = mode;
|
||||
saveSettings();
|
||||
|
||||
@@ -88,6 +88,8 @@ var SPEC = {
|
||||
locale: { def: "", onChange: "updateLocale" },
|
||||
timeLocale: { def: "" },
|
||||
|
||||
notepadLastMode: { def: "" },
|
||||
|
||||
launcherLastMode: { def: "all" },
|
||||
launcherLastFileSearchType: { def: "all" },
|
||||
launcherLastQuery: { def: "" },
|
||||
|
||||
@@ -373,7 +373,7 @@ Item {
|
||||
}
|
||||
|
||||
function open(): string {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (PopoutService.notepadResolvedMode === "popout") {
|
||||
PopoutService.openNotepadPopout();
|
||||
return "NOTEPAD_OPEN_SUCCESS";
|
||||
}
|
||||
@@ -388,7 +388,7 @@ Item {
|
||||
function openFile(path: string): string {
|
||||
if (!path)
|
||||
return open();
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (PopoutService.notepadResolvedMode === "popout") {
|
||||
PopoutService.openNotepadPopoutWithFile(path);
|
||||
return "NOTEPAD_OPEN_FILE_SUCCESS";
|
||||
}
|
||||
@@ -402,7 +402,7 @@ Item {
|
||||
}
|
||||
|
||||
function close(): string {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (PopoutService.notepadResolvedMode === "popout") {
|
||||
PopoutService.notepadPopout?.hide();
|
||||
return "NOTEPAD_CLOSE_SUCCESS";
|
||||
}
|
||||
@@ -415,7 +415,7 @@ Item {
|
||||
}
|
||||
|
||||
function toggle(): string {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (PopoutService.notepadResolvedMode === "popout") {
|
||||
PopoutService.toggleNotepadPopout();
|
||||
return "NOTEPAD_TOGGLE_SUCCESS";
|
||||
}
|
||||
|
||||
@@ -13,12 +13,19 @@ Column {
|
||||
property int draggedIndex: -1
|
||||
property int dropTargetIndex: -1
|
||||
property bool suppressShiftAnimation: false
|
||||
readonly property real tabItemSize: 128 + Theme.spacingXS
|
||||
property int editingIndex: -1
|
||||
readonly property real tabItemSize: tabRow.dynamicTabWidth + Theme.spacingXS
|
||||
|
||||
signal tabSwitched(int tabIndex)
|
||||
signal tabClosed(int tabIndex)
|
||||
signal newTabRequested
|
||||
|
||||
function commitRename(index, newTitle) {
|
||||
if (index >= 0)
|
||||
NotepadStorageService.renameTab(index, newTitle);
|
||||
editingIndex = -1;
|
||||
}
|
||||
|
||||
function hasUnsavedChangesForTab(tab) {
|
||||
if (!tab)
|
||||
return false;
|
||||
@@ -32,11 +39,19 @@ Column {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Row {
|
||||
id: tabRow
|
||||
width: parent.width
|
||||
height: 36
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
readonly property real dynamicTabWidth: {
|
||||
var count = Math.max(1, NotepadStorageService.tabs.length);
|
||||
var raw = (tabScroll.width - (count - 1) * Theme.spacingXS) / count;
|
||||
return Math.max(128, Math.min(300, raw));
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: tabScroll
|
||||
width: parent.width - newTabButton.width - Theme.spacingXS
|
||||
height: parent.height
|
||||
clip: true
|
||||
@@ -57,7 +72,8 @@ Column {
|
||||
|
||||
readonly property bool isActive: NotepadStorageService.currentTabIndex === index
|
||||
readonly property bool isHovered: tabMouseArea.containsMouse && !closeMouseArea.containsMouse
|
||||
readonly property real tabWidth: 128
|
||||
readonly property bool editing: root.editingIndex === index
|
||||
readonly property real tabWidth: tabRow.dynamicTabWidth
|
||||
property bool longPressing: false
|
||||
property bool dragging: false
|
||||
property point dragStartPos: Qt.point(0, 0)
|
||||
@@ -138,6 +154,7 @@ Column {
|
||||
|
||||
StyledText {
|
||||
id: tabText
|
||||
visible: !delegateItem.editing
|
||||
width: parent.width - (tabCloseButton.visible ? tabCloseButton.width + Theme.spacingXS : 0)
|
||||
text: {
|
||||
var prefix = "";
|
||||
@@ -155,13 +172,54 @@ Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: renameField
|
||||
visible: delegateItem.editing
|
||||
enabled: delegateItem.editing
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
selectionColor: Theme.primary
|
||||
selectedTextColor: Theme.background
|
||||
selectByMouse: true
|
||||
clip: true
|
||||
|
||||
onEditingFinished: root.commitRename(index, text)
|
||||
Keys.onEscapePressed: event => {
|
||||
text = modelData.title || "Untitled";
|
||||
root.editingIndex = -1;
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
// A tab switch re-focuses the editor via Qt.callLater; the
|
||||
// timer fires afterwards so the field keeps focus + selection.
|
||||
Timer {
|
||||
id: renameFocusTimer
|
||||
interval: 20
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
renameField.forceActiveFocus();
|
||||
renameField.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible)
|
||||
return;
|
||||
text = modelData.title || "Untitled";
|
||||
renameFocusTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
visible: NotepadStorageService.tabs.length > 1 && !delegateItem.editing
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
@@ -195,11 +253,24 @@ Column {
|
||||
MouseArea {
|
||||
id: tabMouseArea
|
||||
anchors.fill: parent
|
||||
enabled: !delegateItem.editing
|
||||
hoverEnabled: true
|
||||
preventStealing: dragging || longPressing
|
||||
cursorShape: dragging || longPressing ? Qt.ClosedHandCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
||||
onDoubleClicked: {
|
||||
root.tabSwitched(index);
|
||||
root.editingIndex = index;
|
||||
}
|
||||
|
||||
onExited: tabTooltip.hide()
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse && tabText.truncated)
|
||||
tabTooltip.show(modelData.title || "Untitled", delegateItem, 0, 0, "bottom");
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.LeftButton && NotepadStorageService.tabs.length > 1) {
|
||||
delegateItem.dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
@@ -279,4 +350,8 @@ Column {
|
||||
onClicked: root.newTabRequested()
|
||||
}
|
||||
}
|
||||
|
||||
DankTooltipV2 {
|
||||
id: tabTooltip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,45 +29,48 @@ Singleton {
|
||||
|
||||
function setSessionBuffer(tabId, content, baseline) {
|
||||
if (tabId === undefined || tabId === null || tabId < 0)
|
||||
return
|
||||
var next = Object.assign({}, sessionBuffers)
|
||||
return;
|
||||
var next = Object.assign({}, sessionBuffers);
|
||||
if (content !== baseline) {
|
||||
next[tabId] = { content: content, baseline: baseline }
|
||||
next[tabId] = {
|
||||
content: content,
|
||||
baseline: baseline
|
||||
};
|
||||
} else {
|
||||
delete next[tabId]
|
||||
delete next[tabId];
|
||||
}
|
||||
sessionBuffers = next
|
||||
sessionBufferRevision++
|
||||
sessionBuffers = next;
|
||||
sessionBufferRevision++;
|
||||
}
|
||||
|
||||
function getSessionBuffer(tabId) {
|
||||
return sessionBuffers[tabId]
|
||||
return sessionBuffers[tabId];
|
||||
}
|
||||
|
||||
function clearSessionBuffer(tabId) {
|
||||
if (sessionBuffers[tabId] === undefined)
|
||||
return
|
||||
var next = Object.assign({}, sessionBuffers)
|
||||
delete next[tabId]
|
||||
sessionBuffers = next
|
||||
sessionBufferRevision++
|
||||
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
|
||||
conflictDiskContent = diskContent;
|
||||
conflictTabId = tabId;
|
||||
}
|
||||
|
||||
function clearConflict() {
|
||||
conflictTabId = -1
|
||||
conflictDiskContent = ""
|
||||
conflictTabId = -1;
|
||||
conflictDiskContent = "";
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ensureDirectories()
|
||||
ensureDirectories();
|
||||
}
|
||||
|
||||
FileView {
|
||||
@@ -78,64 +81,66 @@ Singleton {
|
||||
|
||||
onLoaded: {
|
||||
try {
|
||||
var data = JSON.parse(text())
|
||||
root.tabs = data.tabs || []
|
||||
root.currentTabIndex = data.currentTabIndex || 0
|
||||
root.metadataLoaded = true
|
||||
root.validateTabs()
|
||||
} catch(e) {
|
||||
log.warn("Failed to parse notepad metadata:", e)
|
||||
root.createDefaultTab()
|
||||
var data = JSON.parse(text());
|
||||
root.tabs = data.tabs || [];
|
||||
root.currentTabIndex = data.currentTabIndex || 0;
|
||||
root.metadataLoaded = true;
|
||||
root.validateTabs();
|
||||
} catch (e) {
|
||||
log.warn("Failed to parse notepad metadata:", e);
|
||||
root.createDefaultTab();
|
||||
}
|
||||
}
|
||||
|
||||
onLoadFailed: {
|
||||
root.createDefaultTab()
|
||||
root.createDefaultTab();
|
||||
}
|
||||
}
|
||||
|
||||
onRefCountChanged: {
|
||||
if (refCount === 1 && !metadataLoaded) {
|
||||
metadataFile.path = ""
|
||||
metadataFile.path = root.metadataPath
|
||||
metadataFile.path = "";
|
||||
metadataFile.path = root.metadataPath;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureDirectories() {
|
||||
mkdirProcess.running = true
|
||||
Proc.runCommand("", ["mkdir", "-p", root.baseDir, root.filesDir], null);
|
||||
}
|
||||
|
||||
function loadMetadata() {
|
||||
metadataFile.path = ""
|
||||
metadataFile.path = root.metadataPath
|
||||
metadataFile.path = "";
|
||||
metadataFile.path = root.metadataPath;
|
||||
}
|
||||
|
||||
function createDefaultTab() {
|
||||
var id = Date.now()
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt"
|
||||
var fullPath = baseDir + "/" + filePath
|
||||
var id = Date.now();
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt";
|
||||
var fullPath = baseDir + "/" + filePath;
|
||||
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
newTabsBeingCreated[id] = true
|
||||
tabsBeingCreated = newTabsBeingCreated
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
newTabsBeingCreated[id] = true;
|
||||
tabsBeingCreated = newTabsBeingCreated;
|
||||
|
||||
root.createEmptyFile(fullPath, function() {
|
||||
root.tabs = [{
|
||||
id: id,
|
||||
title: I18n.tr("Untitled"),
|
||||
filePath: filePath,
|
||||
isTemporary: true,
|
||||
lastModified: new Date().toISOString(),
|
||||
cursorPosition: 0,
|
||||
scrollPosition: 0
|
||||
}]
|
||||
root.currentTabIndex = 0
|
||||
root.createEmptyFile(fullPath, function () {
|
||||
root.tabs = [
|
||||
{
|
||||
id: id,
|
||||
title: I18n.tr("Untitled"),
|
||||
filePath: filePath,
|
||||
isTemporary: true,
|
||||
lastModified: new Date().toISOString(),
|
||||
cursorPosition: 0,
|
||||
scrollPosition: 0
|
||||
}
|
||||
];
|
||||
root.currentTabIndex = 0;
|
||||
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
delete updatedTabsBeingCreated[id]
|
||||
tabsBeingCreated = updatedTabsBeingCreated
|
||||
root.saveMetadata()
|
||||
})
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
delete updatedTabsBeingCreated[id];
|
||||
tabsBeingCreated = updatedTabsBeingCreated;
|
||||
root.saveMetadata();
|
||||
});
|
||||
}
|
||||
|
||||
function saveMetadata() {
|
||||
@@ -143,82 +148,73 @@ Singleton {
|
||||
version: 1,
|
||||
currentTabIndex: currentTabIndex,
|
||||
tabs: tabs
|
||||
}
|
||||
metadataFile.setText(JSON.stringify(metadata, null, 2))
|
||||
};
|
||||
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 tabs[i];
|
||||
}
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadTabContent(tabIndex, callback) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) {
|
||||
callback("")
|
||||
return
|
||||
callback("");
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = tabs[tabIndex]
|
||||
var requestTabId = tab.id
|
||||
var fullPath = tab.isTemporary
|
||||
? baseDir + "/" + tab.filePath
|
||||
: tab.filePath
|
||||
var tab = tabs[tabIndex];
|
||||
var requestTabId = tab.id;
|
||||
var fullPath = tab.isTemporary ? baseDir + "/" + tab.filePath : tab.filePath;
|
||||
|
||||
if (tabsBeingCreated[tab.id]) {
|
||||
Qt.callLater(() => {
|
||||
loadTabContent(tabIndex, callback)
|
||||
})
|
||||
return
|
||||
loadTabContent(tabIndex, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var fileChecker = fileExistsComponent.createObject(root, {
|
||||
path: fullPath,
|
||||
callback: (exists) => {
|
||||
var currentTab = root.getTabById(requestTabId)
|
||||
var currentPath = currentTab
|
||||
? (currentTab.isTemporary ? baseDir + "/" + currentTab.filePath : currentTab.filePath)
|
||||
: ""
|
||||
Proc.runCommand("", ["test", "-f", fullPath], (output, exitCode) => {
|
||||
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,
|
||||
callback: callback
|
||||
})
|
||||
} else {
|
||||
log.warn("Tab file does not exist:", fullPath)
|
||||
callback("")
|
||||
}
|
||||
if (!currentTab || currentPath !== fullPath) {
|
||||
callback("");
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
if (exitCode === 0) {
|
||||
tabFileLoaderComponent.createObject(root, {
|
||||
path: fullPath,
|
||||
callback: callback
|
||||
});
|
||||
} else {
|
||||
log.warn("Tab file does not exist:", fullPath);
|
||||
callback("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveTabContent(tabIndex, content) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||
|
||||
var tab = tabs[tabIndex]
|
||||
var fullPath = tab.isTemporary
|
||||
? baseDir + "/" + tab.filePath
|
||||
: tab.filePath
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
var tab = tabs[tabIndex];
|
||||
var fullPath = tab.isTemporary ? baseDir + "/" + tab.filePath : tab.filePath;
|
||||
|
||||
var saver = tabFileSaverComponent.createObject(root, {
|
||||
path: fullPath,
|
||||
content: content,
|
||||
tabIndex: tabIndex
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function createNewTab() {
|
||||
var id = Date.now()
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt"
|
||||
var fullPath = baseDir + "/" + filePath
|
||||
var id = Date.now();
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt";
|
||||
var fullPath = baseDir + "/" + filePath;
|
||||
|
||||
var newTab = {
|
||||
id: id,
|
||||
@@ -228,29 +224,29 @@ Singleton {
|
||||
lastModified: new Date().toISOString(),
|
||||
cursorPosition: 0,
|
||||
scrollPosition: 0
|
||||
}
|
||||
};
|
||||
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
newTabsBeingCreated[id] = true
|
||||
tabsBeingCreated = newTabsBeingCreated
|
||||
createEmptyFile(fullPath, function() {
|
||||
var newTabs = tabs.slice()
|
||||
newTabs.push(newTab)
|
||||
tabs = newTabs
|
||||
currentTabIndex = tabs.length - 1
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
newTabsBeingCreated[id] = true;
|
||||
tabsBeingCreated = newTabsBeingCreated;
|
||||
createEmptyFile(fullPath, function () {
|
||||
var newTabs = tabs.slice();
|
||||
newTabs.push(newTab);
|
||||
tabs = newTabs;
|
||||
currentTabIndex = tabs.length - 1;
|
||||
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
delete updatedTabsBeingCreated[id]
|
||||
tabsBeingCreated = updatedTabsBeingCreated
|
||||
saveMetadata()
|
||||
})
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
delete updatedTabsBeingCreated[id];
|
||||
tabsBeingCreated = updatedTabsBeingCreated;
|
||||
saveMetadata();
|
||||
});
|
||||
|
||||
return newTab
|
||||
return newTab;
|
||||
}
|
||||
|
||||
function createTabForFile(path) {
|
||||
var id = Date.now()
|
||||
var fileName = path.split('/').pop()
|
||||
var id = Date.now();
|
||||
var fileName = path.split('/').pop();
|
||||
|
||||
var newTab = {
|
||||
id: id,
|
||||
@@ -260,34 +256,34 @@ Singleton {
|
||||
lastModified: new Date().toISOString(),
|
||||
cursorPosition: 0,
|
||||
scrollPosition: 0
|
||||
}
|
||||
};
|
||||
|
||||
var newTabs = tabs.slice()
|
||||
newTabs.push(newTab)
|
||||
tabs = newTabs
|
||||
currentTabIndex = tabs.length - 1
|
||||
saveMetadata()
|
||||
var newTabs = tabs.slice();
|
||||
newTabs.push(newTab);
|
||||
tabs = newTabs;
|
||||
currentTabIndex = tabs.length - 1;
|
||||
saveMetadata();
|
||||
|
||||
return newTab
|
||||
return newTab;
|
||||
}
|
||||
|
||||
function closeTab(tabIndex) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||
|
||||
var newTabs = tabs.slice()
|
||||
var closedTabId = newTabs[tabIndex] ? newTabs[tabIndex].id : -1
|
||||
clearSessionBuffer(closedTabId)
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
var newTabs = tabs.slice();
|
||||
var closedTabId = newTabs[tabIndex] ? newTabs[tabIndex].id : -1;
|
||||
clearSessionBuffer(closedTabId);
|
||||
if (conflictTabId === closedTabId)
|
||||
clearConflict()
|
||||
clearConflict();
|
||||
|
||||
if (newTabs.length <= 1) {
|
||||
var id = Date.now()
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt"
|
||||
var id = Date.now();
|
||||
var filePath = "notepad-files/untitled-" + id + ".txt";
|
||||
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
newTabsBeingCreated[id] = true
|
||||
tabsBeingCreated = newTabsBeingCreated
|
||||
createEmptyFile(baseDir + "/" + filePath, function() {
|
||||
var newTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
newTabsBeingCreated[id] = true;
|
||||
tabsBeingCreated = newTabsBeingCreated;
|
||||
createEmptyFile(baseDir + "/" + filePath, function () {
|
||||
newTabs[0] = {
|
||||
id: id,
|
||||
title: I18n.tr("Untitled"),
|
||||
@@ -296,110 +292,129 @@ Singleton {
|
||||
lastModified: new Date().toISOString(),
|
||||
cursorPosition: 0,
|
||||
scrollPosition: 0
|
||||
}
|
||||
currentTabIndex = 0
|
||||
tabs = newTabs
|
||||
};
|
||||
currentTabIndex = 0;
|
||||
tabs = newTabs;
|
||||
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated)
|
||||
delete updatedTabsBeingCreated[id]
|
||||
tabsBeingCreated = updatedTabsBeingCreated
|
||||
saveMetadata()
|
||||
})
|
||||
return
|
||||
var updatedTabsBeingCreated = Object.assign({}, tabsBeingCreated);
|
||||
delete updatedTabsBeingCreated[id];
|
||||
tabsBeingCreated = updatedTabsBeingCreated;
|
||||
saveMetadata();
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
var tabToDelete = newTabs[tabIndex]
|
||||
var tabToDelete = newTabs[tabIndex];
|
||||
if (tabToDelete && tabToDelete.isTemporary) {
|
||||
deleteFile(baseDir + "/" + tabToDelete.filePath)
|
||||
deleteFile(baseDir + "/" + tabToDelete.filePath);
|
||||
}
|
||||
|
||||
newTabs.splice(tabIndex, 1)
|
||||
newTabs.splice(tabIndex, 1);
|
||||
if (currentTabIndex >= newTabs.length) {
|
||||
currentTabIndex = newTabs.length - 1
|
||||
currentTabIndex = newTabs.length - 1;
|
||||
} else if (currentTabIndex > tabIndex) {
|
||||
currentTabIndex -= 1
|
||||
currentTabIndex -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
tabs = newTabs
|
||||
saveMetadata()
|
||||
|
||||
tabs = newTabs;
|
||||
saveMetadata();
|
||||
}
|
||||
|
||||
function switchToTab(tabIndex) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||
|
||||
currentTabIndex = tabIndex
|
||||
saveMetadata()
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
currentTabIndex = tabIndex;
|
||||
saveMetadata();
|
||||
}
|
||||
|
||||
function reorderTab(fromIndex, toIndex) {
|
||||
if (fromIndex < 0 || fromIndex >= tabs.length || toIndex < 0 || toIndex >= tabs.length)
|
||||
return
|
||||
return;
|
||||
if (fromIndex === toIndex)
|
||||
return
|
||||
|
||||
var newTabs = tabs.slice()
|
||||
var moved = newTabs.splice(fromIndex, 1)[0]
|
||||
newTabs.splice(toIndex, 0, moved)
|
||||
tabs = newTabs
|
||||
return;
|
||||
var newTabs = tabs.slice();
|
||||
var moved = newTabs.splice(fromIndex, 1)[0];
|
||||
newTabs.splice(toIndex, 0, moved);
|
||||
tabs = newTabs;
|
||||
|
||||
if (currentTabIndex === fromIndex) {
|
||||
currentTabIndex = toIndex
|
||||
currentTabIndex = toIndex;
|
||||
} else if (fromIndex < currentTabIndex && toIndex >= currentTabIndex) {
|
||||
currentTabIndex--
|
||||
currentTabIndex--;
|
||||
} else if (fromIndex > currentTabIndex && toIndex <= currentTabIndex) {
|
||||
currentTabIndex++
|
||||
currentTabIndex++;
|
||||
}
|
||||
|
||||
saveMetadata()
|
||||
saveMetadata();
|
||||
}
|
||||
|
||||
function saveTabAs(tabIndex, userPath) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||
|
||||
var tab = tabs[tabIndex]
|
||||
var fileName = userPath.split('/').pop()
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
var tab = tabs[tabIndex];
|
||||
var fileName = userPath.split('/').pop();
|
||||
|
||||
if (tab.isTemporary) {
|
||||
var tempPath = baseDir + "/" + tab.filePath
|
||||
copyFile(tempPath, userPath)
|
||||
deleteFile(tempPath)
|
||||
var tempPath = baseDir + "/" + tab.filePath;
|
||||
copyFile(tempPath, userPath);
|
||||
deleteFile(tempPath);
|
||||
}
|
||||
|
||||
var newTabs = tabs.slice()
|
||||
var newTabs = tabs.slice();
|
||||
newTabs[tabIndex] = Object.assign({}, tab, {
|
||||
title: fileName,
|
||||
filePath: userPath,
|
||||
isTemporary: false,
|
||||
lastModified: new Date().toISOString()
|
||||
})
|
||||
tabs = newTabs
|
||||
saveMetadata()
|
||||
});
|
||||
tabs = newTabs;
|
||||
saveMetadata();
|
||||
}
|
||||
|
||||
function renameTab(tabIndex, newTitle) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
var trimmed = (newTitle || "").trim();
|
||||
var tab = tabs[tabIndex];
|
||||
if (trimmed.length === 0 || trimmed === tab.title)
|
||||
return;
|
||||
if (tab.isTemporary) {
|
||||
updateTabMetadata(tabIndex, {
|
||||
title: trimmed
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var dir = tab.filePath.substring(0, tab.filePath.lastIndexOf('/') + 1);
|
||||
var newPath = dir + trimmed;
|
||||
moveFile(tab.filePath, newPath);
|
||||
updateTabMetadata(tabIndex, {
|
||||
title: trimmed,
|
||||
filePath: newPath
|
||||
});
|
||||
}
|
||||
|
||||
function updateTabMetadata(tabIndex, properties) {
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length) return
|
||||
|
||||
var newTabs = tabs.slice()
|
||||
var updatedTab = Object.assign({}, newTabs[tabIndex], properties)
|
||||
updatedTab.lastModified = new Date().toISOString()
|
||||
newTabs[tabIndex] = updatedTab
|
||||
tabs = newTabs
|
||||
saveMetadata()
|
||||
|
||||
if (tabIndex < 0 || tabIndex >= tabs.length)
|
||||
return;
|
||||
var newTabs = tabs.slice();
|
||||
var updatedTab = Object.assign({}, newTabs[tabIndex], properties);
|
||||
updatedTab.lastModified = new Date().toISOString();
|
||||
newTabs[tabIndex] = updatedTab;
|
||||
tabs = newTabs;
|
||||
saveMetadata();
|
||||
}
|
||||
|
||||
function validateTabs() {
|
||||
var validTabs = []
|
||||
var validTabs = [];
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
var tab = tabs[i]
|
||||
validTabs.push(tab)
|
||||
var tab = tabs[i];
|
||||
validTabs.push(tab);
|
||||
}
|
||||
tabs = validTabs
|
||||
tabs = validTabs;
|
||||
|
||||
if (tabs.length === 0) {
|
||||
root.createDefaultTab()
|
||||
root.createDefaultTab();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,29 +426,13 @@ Singleton {
|
||||
preload: true
|
||||
|
||||
onLoaded: {
|
||||
callback(text())
|
||||
destroy()
|
||||
callback(text());
|
||||
destroy();
|
||||
}
|
||||
|
||||
onLoadFailed: {
|
||||
callback("")
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileExistsComponent
|
||||
Process {
|
||||
property string path
|
||||
property var callback
|
||||
command: ["test", "-f", path]
|
||||
|
||||
Component.onCompleted: running = true
|
||||
|
||||
onExited: (exitCode) => {
|
||||
callback(exitCode === 0)
|
||||
destroy()
|
||||
callback("");
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,94 +451,46 @@ Singleton {
|
||||
|
||||
onSaved: {
|
||||
if (tabIndex >= 0) {
|
||||
root.updateTabMetadata(tabIndex, {})
|
||||
root.updateTabMetadata(tabIndex, {});
|
||||
}
|
||||
if (creationCallback) {
|
||||
creationCallback()
|
||||
creationCallback();
|
||||
}
|
||||
destroy()
|
||||
destroy();
|
||||
}
|
||||
|
||||
onSaveFailed: {
|
||||
log.error("Failed to save tab content")
|
||||
log.error("Failed to save tab content");
|
||||
if (creationCallback) {
|
||||
creationCallback()
|
||||
creationCallback();
|
||||
}
|
||||
destroy()
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyFile(path, callback) {
|
||||
var cleanPath = decodeURI(path.toString())
|
||||
var cleanPath = decodeURI(path.toString());
|
||||
|
||||
if (!cleanPath.startsWith("/")) {
|
||||
cleanPath = baseDir + "/" + cleanPath
|
||||
cleanPath = baseDir + "/" + cleanPath;
|
||||
}
|
||||
|
||||
var creator = fileCreatorComponent.createObject(root, {
|
||||
filePath: cleanPath,
|
||||
creationCallback: callback
|
||||
})
|
||||
Proc.runCommand("", ["touch", cleanPath], (output, exitCode) => {
|
||||
if (callback)
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function copyFile(source, destination) {
|
||||
copyProcess.source = source
|
||||
copyProcess.destination = destination
|
||||
copyProcess.running = true
|
||||
Proc.runCommand("", ["cp", source, destination], null);
|
||||
}
|
||||
|
||||
function deleteFile(path) {
|
||||
deleteProcess.filePath = path
|
||||
deleteProcess.running = true
|
||||
Proc.runCommand("", ["rm", "-f", path], null);
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileCreatorComponent
|
||||
QtObject {
|
||||
property string filePath
|
||||
property var creationCallback
|
||||
|
||||
Component.onCompleted: {
|
||||
var touchProcess = touchProcessComponent.createObject(this, {
|
||||
filePath: filePath,
|
||||
callback: creationCallback
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: touchProcessComponent
|
||||
Process {
|
||||
property string filePath
|
||||
property var callback
|
||||
command: ["touch", filePath]
|
||||
|
||||
Component.onCompleted: running = true
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (callback) callback()
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: copyProcess
|
||||
property string source
|
||||
property string destination
|
||||
command: ["cp", source, destination]
|
||||
}
|
||||
|
||||
Process {
|
||||
id: deleteProcess
|
||||
property string filePath
|
||||
command: ["rm", "-f", filePath]
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mkdirProcess
|
||||
command: ["mkdir", "-p", root.baseDir, root.filesDir]
|
||||
function moveFile(source, destination) {
|
||||
Proc.runCommand("", ["mv", source, destination], null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,7 +864,12 @@ Singleton {
|
||||
return notepadSlideouts[0];
|
||||
}
|
||||
|
||||
// Remembered presentation wins over the configured default until the user
|
||||
// changes the default in settings (handled below).
|
||||
readonly property string notepadResolvedMode: SessionData.notepadLastMode || SettingsData.notepadDefaultMode
|
||||
|
||||
function openNotepadSlideout() {
|
||||
SessionData.setNotepadLastMode("slideout");
|
||||
notepadPopout?.hide();
|
||||
if (notepadSlideouts.length > 0) {
|
||||
notepadSlideoutForFocusedScreen()?.show();
|
||||
@@ -875,6 +880,7 @@ Singleton {
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onNotepadDefaultModeChanged() {
|
||||
SessionData.setNotepadLastMode(SettingsData.notepadDefaultMode);
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
var hadSlideout = false;
|
||||
for (var i = 0; i < root.notepadSlideouts.length; i++) {
|
||||
@@ -893,7 +899,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function openNotepad() {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (notepadResolvedMode === "popout") {
|
||||
openNotepadPopout();
|
||||
return;
|
||||
}
|
||||
@@ -901,7 +907,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function closeNotepad() {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (notepadResolvedMode === "popout") {
|
||||
notepadPopout?.hide();
|
||||
return;
|
||||
}
|
||||
@@ -911,7 +917,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function toggleNotepad() {
|
||||
if (SettingsData.notepadDefaultMode === "popout") {
|
||||
if (notepadResolvedMode === "popout") {
|
||||
toggleNotepadPopout();
|
||||
return;
|
||||
}
|
||||
@@ -926,6 +932,7 @@ Singleton {
|
||||
property string _notepadPendingOpenFilePath: ""
|
||||
|
||||
function openNotepadPopout() {
|
||||
SessionData.setNotepadLastMode("popout");
|
||||
closeNotepadSlideouts();
|
||||
if (notepadPopout) {
|
||||
notepadPopout.show();
|
||||
|
||||
Reference in New Issue
Block a user