forked from Shinonome/dots-hyprland
sidebar: translator: language selector
This commit is contained in:
@@ -63,6 +63,14 @@ Singleton {
|
||||
]
|
||||
}
|
||||
|
||||
property QtObject language: QtObject {
|
||||
property QtObject translator: QtObject {
|
||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
||||
property string sourceLanguage: "auto"
|
||||
property string targetLanguage: "English" // Run `trans -list-all` for available languages
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject networking: QtObject {
|
||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import QtQuick.Layouts
|
||||
*/
|
||||
Rectangle {
|
||||
id: root
|
||||
default property alias content: rowLayout.data
|
||||
default property alias data: rowLayout.data
|
||||
property real spacing: 5
|
||||
property real padding: 0
|
||||
property int clickIndex: rowLayout.clickIndex
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real dialogPadding: 15
|
||||
property real dialogMargin: 30
|
||||
property string titleText: "Selection Dialog"
|
||||
property alias items: choiceModel.values
|
||||
property int selectedId: -1 // -1 means no selection
|
||||
|
||||
signal canceled();
|
||||
signal selected(var result);
|
||||
|
||||
Rectangle { // Scrim
|
||||
id: scrimOverlay
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colScrim
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
propagateComposedEvents: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // The dialog
|
||||
id: dialog
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.fill: parent
|
||||
anchors.margins: dialogMargin
|
||||
implicitHeight: dialogColumnLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: dialogColumnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 16
|
||||
|
||||
StyledText {
|
||||
id: dialogTitle
|
||||
Layout.topMargin: dialogPadding
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
text: root.titleText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: choiceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
|
||||
model: ScriptModel {
|
||||
id: choiceModel
|
||||
}
|
||||
|
||||
delegate: StyledRadioButton {
|
||||
id: radioButton
|
||||
required property var modelData
|
||||
required property int index
|
||||
anchors {
|
||||
left: parent?.left
|
||||
right: parent?.right
|
||||
leftMargin: root.dialogPadding
|
||||
rightMargin: root.dialogPadding
|
||||
}
|
||||
|
||||
description: modelData.toString()
|
||||
checked: index === root.selectedId
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.selectedId = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: dialogButtonsRowLayout
|
||||
Layout.bottomMargin: dialogPadding
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
DialogButton {
|
||||
buttonText: qsTr("Cancel")
|
||||
onClicked: root.canceled()
|
||||
}
|
||||
DialogButton {
|
||||
buttonText: qsTr("OK")
|
||||
onClicked: root.selected(
|
||||
root.selectedId === -1 ? null :
|
||||
root.items[root.selectedId]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "./translator/"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -15,11 +16,26 @@ import Quickshell.Hyprland
|
||||
*/
|
||||
Item {
|
||||
id: root
|
||||
property var inputField: inputTextArea
|
||||
property var outputField: outputTextArea
|
||||
|
||||
// Widgets
|
||||
property var inputField: inputCanvas.inputTextArea
|
||||
// Widget variables
|
||||
property bool translationFor: false // Indicates if the translation is for an autocorrected text
|
||||
property string translatedText: ""
|
||||
property list<string> languages: []
|
||||
// Options
|
||||
property string targetLanguage: ConfigOptions.language.translator.targetLanguage
|
||||
property string sourceLanguage: ConfigOptions.language.translator.sourceLanguage
|
||||
property string hostLanguage: targetLanguage
|
||||
|
||||
property bool showLanguageSelector: false
|
||||
property bool languageSelectorTarget: false // true for target language, false for source language
|
||||
property string languageSelectorLanguage: ""
|
||||
|
||||
function showLanguageSelectorDialog(isTargetLang: bool) {
|
||||
root.showLanguageSelector = true
|
||||
root.languageSelectorTarget = isTargetLang;
|
||||
root.languageSelectorLanguage = isTargetLang ? root.targetLanguage : root.sourceLanguage;
|
||||
}
|
||||
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
@@ -32,19 +48,23 @@ Item {
|
||||
interval: ConfigOptions.sidebar.translator.delay
|
||||
repeat: false
|
||||
onTriggered: () => {
|
||||
if (inputTextArea.text.trim().length > 0) {
|
||||
if (root.inputField.text.trim().length > 0) {
|
||||
console.log("Translating with command:", translateProc.command);
|
||||
translateProc.running = false;
|
||||
translateProc.buffer = ""; // Clear the buffer
|
||||
translateProc.running = true; // Restart the process
|
||||
} else {
|
||||
outputTextArea.text = "";
|
||||
root.translatedText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: translateProc
|
||||
command: ["bash", "-c", `trans -no-theme -no-ansi '${StringUtils.shellSingleQuoteEscape(inputTextArea.text.trim())}'`]
|
||||
command: ["bash", "-c", `trans -no-theme`
|
||||
+ ` -source '${StringUtils.shellSingleQuoteEscape(root.sourceLanguage)}'`
|
||||
+ ` -target '${StringUtils.shellSingleQuoteEscape(root.targetLanguage)}'`
|
||||
+ ` -no-ansi '${StringUtils.shellSingleQuoteEscape(root.inputField.text.trim())}'`]
|
||||
property string buffer: ""
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
@@ -54,12 +74,29 @@ Item {
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
// 1. Split into sections by double newlines
|
||||
const sections = translateProc.buffer.trim().split(/\n\s*\n/);
|
||||
// console.log("BUFFER:", translateProc.buffer);
|
||||
// console.log("SECTIONS:", sections);
|
||||
console.log("BUFFER:", translateProc.buffer);
|
||||
console.log("SECTIONS:", sections);
|
||||
|
||||
// 2. Extract relevant data
|
||||
root.translatedText = sections.length > 1 ? sections[1].trim() : "";
|
||||
root.outputField.text = root.translatedText;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getLanguagesProc
|
||||
command: ["trans", "-list-languages"]
|
||||
property list<string> bufferList: ["auto"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
getLanguagesProc.bufferList.push(data.trim());
|
||||
}
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.languages = getLanguagesProc.bufferList
|
||||
.filter(lang => lang.trim().length > 0) // Filter out empty lines
|
||||
.sort((a, b) => a.localeCompare(b)); // Sort alphabetically
|
||||
getLanguagesProc.bufferList = []; // Clear the buffer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,159 +108,133 @@ Item {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle { // INPUT
|
||||
LanguageSelectorButton { // Source language button
|
||||
id: sourceLanguageButton
|
||||
displayText: root.sourceLanguage
|
||||
onClicked: {
|
||||
root.showLanguageSelectorDialog(false);
|
||||
}
|
||||
}
|
||||
|
||||
TextCanvas { // Content input
|
||||
id: inputCanvas
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, inputColumn.implicitHeight)
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.normal
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: inputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StyledTextArea { // Input area
|
||||
id: inputTextArea
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Enter text to translate...")
|
||||
wrapMode: TextEdit.Wrap
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
padding: 15
|
||||
background: null
|
||||
onTextChanged: {
|
||||
if (inputTextArea.text.trim().length > 0) {
|
||||
translateTimer.restart();
|
||||
} else {
|
||||
outputTextArea.text = "";
|
||||
}
|
||||
}
|
||||
isInput: true
|
||||
placeholderText: qsTr("Enter text to translate...")
|
||||
onInputTextChanged: {
|
||||
translateTimer.restart();
|
||||
}
|
||||
GroupButton {
|
||||
id: pasteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_paste"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
Layout.leftMargin: 10
|
||||
text: qsTr("%1 characters").arg(inputTextArea.text.length)
|
||||
color: Appearance.colors.colOnLayer1
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
GroupButton {
|
||||
id: pasteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_paste"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = Quickshell.clipboardText
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: deleteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: inputTextArea.text.length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "close"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = Quickshell.clipboardText
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: deleteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: inputCanvas.inputTextArea.text.length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "close"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // OUTPUT
|
||||
LanguageSelectorButton { // Target language button
|
||||
id: targetLanguageButton
|
||||
displayText: root.targetLanguage
|
||||
onClicked: {
|
||||
root.showLanguageSelectorDialog(true);
|
||||
}
|
||||
}
|
||||
|
||||
TextCanvas { // Content translation
|
||||
id: outputCanvas
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, outputColumn.implicitHeight)
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
ColumnLayout { // Output column
|
||||
id: outputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText { // Output area
|
||||
id: outputTextArea
|
||||
Layout.fillWidth: true
|
||||
property bool hasTranslation: (root.translatedText.trim().length > 0)
|
||||
wrapMode: TextEdit.Wrap
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: hasTranslation ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
padding: 15
|
||||
text: hasTranslation ? root.translatedText : ""
|
||||
isInput: false
|
||||
placeholderText: qsTr("Translation goes here...")
|
||||
property bool hasTranslation: (root.translatedText.trim().length > 0)
|
||||
text: hasTranslation ? root.translatedText : ""
|
||||
GroupButton {
|
||||
id: copyButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: outputCanvas.displayedText.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_copy"
|
||||
color: copyButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
Item { Layout.fillHeight: true }
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
GroupButton {
|
||||
id: copyButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: root.outputField.text.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_copy"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
Quickshell.clipboardText = root.outputField.text
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: searchButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: root.outputField.text.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "travel_explore"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
let url = ConfigOptions.search.engineBaseUrl + root.outputField.text;
|
||||
for (let site of ConfigOptions.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
Quickshell.clipboardText = outputCanvas.displayedText
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: searchButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: outputCanvas.displayedText.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "travel_explore"
|
||||
color: searchButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
let url = ConfigOptions.search.engineBaseUrl + outputCanvas.displayedText;
|
||||
for (let site of ConfigOptions.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.showLanguageSelector
|
||||
visible: root.showLanguageSelector
|
||||
z: 9999
|
||||
sourceComponent: SelectionDialog {
|
||||
id: languageSelectorDialog
|
||||
titleText: qsTr("Select Language")
|
||||
items: root.languages
|
||||
onCanceled: () => {
|
||||
root.showLanguageSelector = false;
|
||||
}
|
||||
onSelected: (result) => {
|
||||
root.showLanguageSelector = false;
|
||||
if (!result || result.length === 0) return; // No selection made
|
||||
|
||||
if (root.languageSelectorTarget) {
|
||||
root.targetLanguage = result;
|
||||
ConfigOptions.language.translator.targetLanguage = result; // Save to config
|
||||
} else {
|
||||
root.sourceLanguage = result;
|
||||
ConfigOptions.language.translator.sourceLanguage = result; // Save to config
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
RippleButton {
|
||||
id: root
|
||||
property string displayText: ""
|
||||
colBackground: Appearance.colors.colLayer2
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: languageRow.implicitWidth
|
||||
implicitHeight: languageText.implicitHeight
|
||||
RowLayout {
|
||||
id: languageRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
StyledText {
|
||||
id: languageText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 5
|
||||
text: root.displayText
|
||||
color: Appearance.colors.colOnLayer2
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
iconSize: Appearance.font.pixelSize.hugeass
|
||||
text: "arrow_drop_down"
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property bool isInput: true // true for input, false for output
|
||||
property string placeholderText
|
||||
property string text: ""
|
||||
property var inputTextArea: isInput ? inputLoader.item : undefined
|
||||
readonly property string displayedText: isInput ? inputLoader.item.text :
|
||||
root.text.length > 0 ? outputLoader.item.text : ""
|
||||
default property alias actionButtons: actions.data
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, inputColumn.implicitHeight)
|
||||
color: isInput ? Appearance.colors.colLayer1 : Appearance.m3colors.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
border.color: isInput ? Appearance.m3colors.m3outlineVariant : "transparent"
|
||||
border.width: isInput ? 1 : 0
|
||||
|
||||
signal inputTextChanged(); // Signal emitted when text changes
|
||||
|
||||
ColumnLayout {
|
||||
id: inputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Loader {
|
||||
id: inputLoader
|
||||
active: root.isInput
|
||||
visible: root.isInput
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: StyledTextArea { // Input area
|
||||
id: inputTextArea
|
||||
placeholderText: root.placeholderText
|
||||
wrapMode: TextEdit.Wrap
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
padding: 15
|
||||
background: null
|
||||
onTextChanged: root.inputTextChanged()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: outputLoader
|
||||
active: !root.isInput
|
||||
visible: !root.isInput
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: StyledText { // Output area
|
||||
id: outputTextArea
|
||||
padding: 15
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: root.text.length > 0 ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
text: root.text.length > 0 ? root.text : root.placeholderText
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
|
||||
Loader {
|
||||
active: root.isInput
|
||||
visible: root.isInput
|
||||
Layout.leftMargin: 10
|
||||
sourceComponent: Text {
|
||||
text: qsTr("%1 characters").arg(inputLoader.item.text.length)
|
||||
color: Appearance.colors.colOnLayer1
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
id: actions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
@@ -16,7 +17,7 @@ Item {
|
||||
property int dialogMargins: 16
|
||||
property PwNode selectedDevice
|
||||
|
||||
function showDeviceSelectorDialog(input) {
|
||||
function showDeviceSelectorDialog(input: bool) {
|
||||
root.selectedDevice = null
|
||||
root.showDeviceSelector = true
|
||||
root.deviceSelectorInput = input
|
||||
@@ -207,9 +208,11 @@ Item {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes.values.filter(node => {
|
||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
||||
})
|
||||
model: ScriptModel {
|
||||
values: Pipewire.nodes.values.filter(node => {
|
||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
||||
})
|
||||
}
|
||||
|
||||
// This could and should be refractored, but all data becomes null when passed wtf
|
||||
delegate: StyledRadioButton {
|
||||
|
||||
@@ -29,7 +29,7 @@ ShellRoot {
|
||||
property bool enableBar: true
|
||||
property bool enableBackgroundWidgets: true
|
||||
property bool enableCheatsheet: true
|
||||
property bool enableDock: true
|
||||
property bool enableDock: false
|
||||
property bool enableMediaControls: true
|
||||
property bool enableNotificationPopup: true
|
||||
property bool enableOnScreenDisplayBrightness: true
|
||||
|
||||
Reference in New Issue
Block a user