Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
// Focused result sheet for local OCR and code recognition. It is intentionally
|
||||
// a transient overlay: the user selects pixels, acts on the result, and leaves
|
||||
// without creating another application window or workspace tile.
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.widgets
|
||||
|
||||
PanelWindow {
|
||||
id: win
|
||||
|
||||
visible: ScreenIntelligence.visible
|
||||
color: "transparent"
|
||||
exclusiveZone: 0
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
WlrLayershell.namespace: "qs-screen-intelligence"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
keyboardScope.forceActiveFocus();
|
||||
else
|
||||
copied = false;
|
||||
}
|
||||
|
||||
property bool copied: false
|
||||
|
||||
Timer {
|
||||
id: copiedTimer
|
||||
interval: 1500
|
||||
onTriggered: win.copied = false
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: keyboardScope
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
ScreenIntelligence.close();
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.alpha(Theme.bgDark, 0.46)
|
||||
border.width: 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: ScreenIntelligence.close()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: sheet
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width - 80, 840)
|
||||
height: Math.min(parent.height - 120, ScreenIntelligence.phase === "ready" ? 680 : 390)
|
||||
radius: 24
|
||||
color: Theme.alpha(Theme.bgPopover, 0.965)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.1)
|
||||
clip: true
|
||||
|
||||
PrismEdge {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
inset: sheet.radius
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: mouse => mouse.accepted = true
|
||||
}
|
||||
|
||||
Column {
|
||||
id: heading
|
||||
anchors.left: parent.left
|
||||
anchors.right: closeButton.left
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 28
|
||||
anchors.topMargin: 25
|
||||
anchors.rightMargin: 18
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: "Screen Intelligence"
|
||||
color: Theme.fg
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeTitle
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Text {
|
||||
text: {
|
||||
if (ScreenIntelligence.phase === "capturing")
|
||||
return "Capturing the selected pixels";
|
||||
if (ScreenIntelligence.phase === "reading")
|
||||
return "Reading locally on this computer";
|
||||
if (ScreenIntelligence.phase === "error")
|
||||
return "Recognition needs attention";
|
||||
return "Local text and code recognition";
|
||||
}
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: closeButton
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
anchors.rightMargin: 20
|
||||
width: 34
|
||||
height: 34
|
||||
radius: 11
|
||||
color: closeMouse.containsMouse ? Theme.alpha(Theme.fg, Theme.hoverAlpha) : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "×"
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: ScreenIntelligence.close()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: body
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: heading.bottom
|
||||
anchors.bottom: actions.top
|
||||
anchors.leftMargin: 28
|
||||
anchors.rightMargin: 28
|
||||
anchors.topMargin: 24
|
||||
anchors.bottomMargin: 18
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width, 520)
|
||||
spacing: 13
|
||||
visible: ScreenIntelligence.busy
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: ""
|
||||
color: Theme.accent
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 40
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
text: ScreenIntelligence.phase === "capturing" ? "Capturing…" : "Reading text and codes…"
|
||||
color: Theme.fg
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Pixels stay on this computer. No network request is being made."
|
||||
color: Theme.fgDim
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width, 560)
|
||||
spacing: 14
|
||||
visible: ScreenIntelligence.phase === "error"
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: ""
|
||||
color: Theme.warn
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 38
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
text: ScreenIntelligence.error
|
||||
color: Theme.fg
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: commandText.implicitWidth + 28
|
||||
height: 38
|
||||
radius: 10
|
||||
visible: !ScreenIntelligence.ocrReady
|
||||
color: Theme.alpha(Theme.fg, 0.07)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.08)
|
||||
|
||||
Text {
|
||||
id: commandText
|
||||
anchors.centerIn: parent
|
||||
text: "sudo dnf install -y tesseract zbar"
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
visible: ScreenIntelligence.phase === "ready"
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: ScreenIntelligence.codeValue === "" ? 0 : 62
|
||||
visible: ScreenIntelligence.codeValue !== ""
|
||||
radius: 13
|
||||
color: Theme.alpha(Theme.accent, 0.09)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.accent, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: ""
|
||||
color: Theme.accent
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 22
|
||||
}
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 52
|
||||
anchors.right: codeAction.left
|
||||
anchors.rightMargin: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
Text { text: ScreenIntelligence.codeType || "Detected code"; color: Theme.fg; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSizeSmall; font.weight: Font.DemiBold }
|
||||
Text { width: parent.width; text: ScreenIntelligence.codeValue; color: Theme.fgDim; elide: Text.ElideMiddle; font.family: Theme.fontFamily; font.pixelSize: Theme.fontSize }
|
||||
}
|
||||
ResultAction {
|
||||
id: codeAction
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
label: ScreenIntelligence.primaryUrl !== "" ? "Open" : "Copy"
|
||||
onClicked: {
|
||||
if (ScreenIntelligence.primaryUrl !== "")
|
||||
ScreenIntelligence.openDetected();
|
||||
else
|
||||
ScreenIntelligence.copyCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height - (ScreenIntelligence.codeValue === "" ? 0 : 74)
|
||||
radius: 14
|
||||
color: Theme.alpha(Theme.bgDark, 0.72)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.075)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 60
|
||||
visible: ScreenIntelligence.text === "" && ScreenIntelligence.codeValue === ""
|
||||
text: "No readable text or code was found. Try a tighter selection with larger, higher-contrast text."
|
||||
color: Theme.fgDim
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 17
|
||||
visible: ScreenIntelligence.text !== ""
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: resultText.implicitHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
TextEdit {
|
||||
id: resultText
|
||||
width: parent.width
|
||||
text: ScreenIntelligence.text
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
wrapMode: TextEdit.Wrap
|
||||
color: Theme.fg
|
||||
selectionColor: Theme.accent
|
||||
selectedTextColor: Theme.bgDark
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: actions
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.rightMargin: 28
|
||||
anchors.bottomMargin: 24
|
||||
spacing: 8
|
||||
visible: ScreenIntelligence.phase === "ready" && (ScreenIntelligence.text !== "" || ScreenIntelligence.codeValue !== "")
|
||||
|
||||
ResultAction {
|
||||
label: "Open link"
|
||||
visible: ScreenIntelligence.primaryUrl !== "" && ScreenIntelligence.codeValue === ""
|
||||
onClicked: ScreenIntelligence.openDetected()
|
||||
}
|
||||
ResultAction {
|
||||
label: "Translate"
|
||||
enabled: ScreenIntelligence.text !== ""
|
||||
onClicked: ScreenIntelligence.translate()
|
||||
}
|
||||
ResultAction {
|
||||
label: "Search"
|
||||
enabled: ScreenIntelligence.text !== ""
|
||||
onClicked: ScreenIntelligence.search()
|
||||
}
|
||||
ResultAction {
|
||||
label: win.copied ? "Copied" : "Copy text"
|
||||
primary: true
|
||||
enabled: ScreenIntelligence.text !== ""
|
||||
onClicked: {
|
||||
ScreenIntelligence.copyText();
|
||||
win.copied = true;
|
||||
copiedTimer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ResultAction: Rectangle {
|
||||
id: action
|
||||
property string label: ""
|
||||
property bool primary: false
|
||||
property bool enabled: true
|
||||
signal clicked
|
||||
|
||||
implicitWidth: actionLabel.implicitWidth + 26
|
||||
implicitHeight: 34
|
||||
radius: 10
|
||||
opacity: enabled ? 1 : 0.4
|
||||
color: primary
|
||||
? (actionMouse.containsMouse ? Theme.mix(Theme.accent, Theme.fg, 0.1) : Theme.accent)
|
||||
: (actionMouse.containsMouse ? Theme.alpha(Theme.fg, 0.13) : Theme.alpha(Theme.fg, 0.075))
|
||||
border.width: primary ? 0 : 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.08)
|
||||
|
||||
Text {
|
||||
id: actionLabel
|
||||
anchors.centerIn: parent
|
||||
text: action.label
|
||||
color: action.primary ? Theme.bgDark : Theme.fg
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: actionMouse
|
||||
anchors.fill: parent
|
||||
enabled: action.enabled
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: action.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user