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