format, move text color to styled text area

This commit is contained in:
end-4
2025-11-14 10:57:26 +01:00
parent a5b941360c
commit f8ffe5e63f
2 changed files with 118 additions and 132 deletions
@@ -10,6 +10,7 @@ TextArea {
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
selectionColor: Appearance.colors.colSecondaryContainer
placeholderTextColor: Appearance.m3colors.m3outline
color: Appearance.colors.colOnLayer0
font {
family: Appearance.font.family.main
pixelSize: Appearance?.font.pixelSize.small ?? 15
@@ -11,149 +11,146 @@ import qs.modules.ii.overlay
OverlayBackground {
id: root
readonly property real panelPadding: 20
property string stickypadContents: ""
property bool pendingReload: false
property var copylistEntries: []
property string lastParsedCopylistText: ""
property var parsedCopylistLines: []
property bool isClickthrough: false
Component.onCompleted: {
stickypadFile.reload()
updateCopylistEntries()
stickypadFile.reload();
updateCopylistEntries();
}
function saveStickypad() {
if (!stickypadInput)
return
stickypadContents = stickypadInput.text
stickypadFile.setText(stickypadContents)
return;
stickypadContents = stickypadInput.text;
stickypadFile.setText(stickypadContents);
}
function focusStickypadAtEnd() {
if (!stickypadInput)
return
stickypadInput.forceActiveFocus()
const endPos = stickypadInput.text.length
applySelection(endPos, endPos)
return;
stickypadInput.forceActiveFocus();
const endPos = stickypadInput.text.length;
applySelection(endPos, endPos);
}
function applySelection(cursorPos, anchorPos) {
if (!stickypadInput)
return
const textLength = stickypadInput.text.length
const cursor = Math.max(0, Math.min(cursorPos, textLength))
const anchor = Math.max(0, Math.min(anchorPos, textLength))
stickypadInput.select(anchor, cursor)
return;
const textLength = stickypadInput.text.length;
const cursor = Math.max(0, Math.min(cursorPos, textLength));
const anchor = Math.max(0, Math.min(anchorPos, textLength));
stickypadInput.select(anchor, cursor);
if (cursor === anchor)
stickypadInput.deselect()
stickypadInput.deselect();
}
function scheduleCopylistUpdate(immediate = false) {
if (!stickypadInput)
return
return;
if (immediate) {
copyListDebounce.stop()
updateCopylistEntries()
copyListDebounce.stop();
updateCopylistEntries();
} else {
copyListDebounce.restart()
copyListDebounce.restart();
}
}
function updateCopylistEntries() {
if (!stickypadInput)
return
const textValue = stickypadInput.text
return;
const textValue = stickypadInput.text;
if (!textValue || textValue.length === 0) {
lastParsedCopylistText = ""
parsedCopylistLines = []
copylistEntries = []
return
lastParsedCopylistText = "";
parsedCopylistLines = [];
copylistEntries = [];
return;
}
if (textValue !== lastParsedCopylistText) {
const lineRegex = /(.*?)(\r?\n|$)/g
let match = null
const parsed = []
const lineRegex = /(.*?)(\r?\n|$)/g;
let match = null;
const parsed = [];
while ((match = lineRegex.exec(textValue)) !== null) {
const lineText = match[1]
const newlineText = match[2]
const lineStart = match.index
const lineEnd = lineStart + lineText.length
const bulletMatch = lineText.match(/^\s*-\s+(.*\S)\s*$/)
const lineText = match[1];
const newlineText = match[2];
const lineStart = match.index;
const lineEnd = lineStart + lineText.length;
const bulletMatch = lineText.match(/^\s*-\s+(.*\S)\s*$/);
if (bulletMatch) {
parsed.push({
content: bulletMatch[1].trim(),
start: lineStart,
end: lineEnd
})
});
}
if (newlineText === "")
break
break;
}
lastParsedCopylistText = textValue
parsedCopylistLines = parsed
lastParsedCopylistText = textValue;
parsedCopylistLines = parsed;
if (parsed.length === 0) {
copylistEntries = []
return
copylistEntries = [];
return;
}
}
updateCopylistPositions()
updateCopylistPositions();
}
function updateCopylistPositions() {
if (!stickypadInput || parsedCopylistLines.length === 0)
return
const rawSelectionStart = stickypadInput.selectionStart
const rawSelectionEnd = stickypadInput.selectionEnd
const selectionStart = rawSelectionStart === -1 ? stickypadInput.cursorPosition : rawSelectionStart
const selectionEnd = rawSelectionEnd === -1 ? stickypadInput.cursorPosition : rawSelectionEnd
const rangeStart = Math.min(selectionStart, selectionEnd)
const rangeEnd = Math.max(selectionStart, selectionEnd)
return;
const rawSelectionStart = stickypadInput.selectionStart;
const rawSelectionEnd = stickypadInput.selectionEnd;
const selectionStart = rawSelectionStart === -1 ? stickypadInput.cursorPosition : rawSelectionStart;
const selectionEnd = rawSelectionEnd === -1 ? stickypadInput.cursorPosition : rawSelectionEnd;
const rangeStart = Math.min(selectionStart, selectionEnd);
const rangeEnd = Math.max(selectionStart, selectionEnd);
const entries = parsedCopylistLines.map(line => {
const caretIntersects = rangeEnd > line.start && rangeStart <= line.end
const caretIntersects = rangeEnd > line.start && rangeStart <= line.end;
if (caretIntersects)
return null
const startRect = stickypadInput.positionToRectangle(line.start)
let endRect = stickypadInput.positionToRectangle(line.end)
return null;
const startRect = stickypadInput.positionToRectangle(line.start);
let endRect = stickypadInput.positionToRectangle(line.end);
if (!isFinite(startRect.y))
return null
return null;
if (!isFinite(endRect.y))
endRect = startRect
const lineBottom = endRect.y + endRect.height
const rectHeight = Math.max(lineBottom - startRect.y, stickypadInput.font.pixelSize + 8)
endRect = startRect;
const lineBottom = endRect.y + endRect.height;
const rectHeight = Math.max(lineBottom - startRect.y, stickypadInput.font.pixelSize + 8);
return {
content: line.content,
y: startRect.y,
height: rectHeight
}
}).filter(entry => entry !== null)
copylistEntries = entries
};
}).filter(entry => entry !== null);
copylistEntries = entries;
}
ColumnLayout {
id: stickypadLayout
anchors {
fill: parent
margins: panelPadding
margins: 16
}
spacing: 14
spacing: 10
ScrollView {
id: editorScrollView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
onWidthChanged: root.scheduleCopylistUpdate(true)
StyledTextArea {
id: stickypadInput
anchors {
@@ -167,19 +164,16 @@ OverlayBackground {
textFormat: TextEdit.PlainText
background: null
rightPadding: 44
// Adapt text color to theme (light/dark mode) - START
color: Appearance.colors.colOnLayer0
// Adapt text color to theme (light/dark mode) - END
// Disable text area when clickthrough enabled - START
enabled: GlobalStates.overlayOpen || !root.isClickthrough
activeFocusOnTab: GlobalStates.overlayOpen || !root.isClickthrough
// Disable text area when clickthrough enabled - END
onTextChanged: {
if (stickypadInput.activeFocus) {
saveDebounce.restart()
saveDebounce.restart();
}
root.scheduleCopylistUpdate(true)
root.scheduleCopylistUpdate(true);
}
onCursorPositionChanged: root.scheduleCopylistUpdate()
onSelectionStartChanged: root.scheduleCopylistUpdate()
@@ -187,25 +181,25 @@ OverlayBackground {
onHeightChanged: root.scheduleCopylistUpdate(true)
onContentHeightChanged: root.scheduleCopylistUpdate(true)
}
Item {
anchors.fill: stickypadInput
visible: copylistEntries.length > 0
clip: true
Repeater {
model: copylistEntries
delegate: Item {
readonly property real lineHeight: Math.max(modelData.height, Appearance.font.pixelSize.normal + 6)
readonly property real iconSizeLocal: Appearance.font.pixelSize.normal
readonly property real hitPadding: 6
width: iconSizeLocal + hitPadding * 2
height: lineHeight
y: modelData.y
x: Math.max(stickypadInput.width - width - 8, 0)
z: 5
Rectangle {
id: feedbackFlash
anchors.centerIn: iconItem
@@ -214,9 +208,9 @@ OverlayBackground {
radius: width / 2
color: Appearance.colors.colLayer2
opacity: 0
z: -1
z: 999
}
MaterialSymbol {
id: iconItem
anchors.centerIn: parent
@@ -237,7 +231,7 @@ OverlayBackground {
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
@@ -248,12 +242,12 @@ OverlayBackground {
onReleased: iconItem.scale = 1
onCanceled: iconItem.scale = 1
onClicked: {
feedbackFlash.opacity = 0.6
feedbackFade.restart()
Quickshell.clipboardText = modelData.content
feedbackFlash.opacity = 0.6;
feedbackFade.restart();
Quickshell.clipboardText = modelData.content;
}
}
NumberAnimation {
id: feedbackFade
target: feedbackFlash
@@ -266,72 +260,63 @@ OverlayBackground {
}
}
}
Rectangle {
StyledText {
id: statusLabel
Layout.fillWidth: true
Layout.preferredHeight: 28
Layout.minimumHeight: 28
color: "transparent"
StyledText {
id: statusLabel
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
rightMargin: 8
}
text: saveDebounce.running ? "Saving..." : "Saved"
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.small
font.weight: Font.Medium
}
// Layout.preferredHeight: 28
horizontalAlignment: Text.AlignRight
text: saveDebounce.running ? Translation.tr("Saving...") : Translation.tr("Saved")
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.small
font.weight: Font.Medium
}
}
Timer {
id: saveDebounce
interval: 500
repeat: false
onTriggered: saveStickypad()
}
Timer {
id: copyListDebounce
interval: 100
repeat: false
onTriggered: updateCopylistPositions()
}
FileView {
id: stickypadFile
path: Qt.resolvedUrl(Directories.stickypadPath)
onLoaded: {
stickypadContents = stickypadFile.text()
stickypadContents = stickypadFile.text();
if (stickypadInput && stickypadInput.text !== stickypadContents) {
const previousCursor = stickypadInput.cursorPosition
const previousAnchor = stickypadInput.selectionStart
stickypadInput.text = stickypadContents
applySelection(previousCursor, previousAnchor)
const previousCursor = stickypadInput.cursorPosition;
const previousAnchor = stickypadInput.selectionStart;
stickypadInput.text = stickypadContents;
applySelection(previousCursor, previousAnchor);
}
if (pendingReload) {
pendingReload = false
Qt.callLater(focusStickypadAtEnd)
pendingReload = false;
Qt.callLater(focusStickypadAtEnd);
}
Qt.callLater(root.updateCopylistEntries)
Qt.callLater(root.updateCopylistEntries);
}
onLoadFailed: (error) => {
onLoadFailed: error => {
if (error === FileViewError.FileNotFound) {
stickypadContents = ""
stickypadFile.setText(stickypadContents)
stickypadContents = "";
stickypadFile.setText(stickypadContents);
if (stickypadInput)
stickypadInput.text = stickypadContents
stickypadInput.text = stickypadContents;
if (pendingReload) {
pendingReload = false
Qt.callLater(focusStickypadAtEnd)
pendingReload = false;
Qt.callLater(focusStickypadAtEnd);
}
Qt.callLater(root.updateCopylistEntries)
Qt.callLater(root.updateCopylistEntries);
} else {
console.log("[Stickypad] Error loading file: " + error)
console.log("[Stickypad] Error loading file: " + error);
}
}
}