/* SPDX-FileCopyrightText: 2014-2017 Weng Xuetian SPDX-License-Identifier: GPL-2.0-or-later */ import QtQuick import QtQuick.Layouts import org.kde.plasma.core as PlasmaCore import org.kde.plasma.components as PlasmaComponents3 import plasma.applet.org.kde.plasma.kimpanel as Kimpanel import org.kde.kirigami as Kirigami import org.kde.plasma.plasmoid PlasmaCore.Dialog { id: inputpanel type: PlasmaCore.Dialog.PopupMenu flags: { var flag = Qt.WindowTransparentForInput | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus; if (Qt.platform.pluginName.includes("wayland")) { flag |= Qt.ToolTip; } else { flag |= Qt.Popup; } return flag; } location: PlasmaCore.Types.Floating visible: helper.auxVisible || helper.preeditVisible || helper.lookupTableVisible required property Kimpanel.Kimpanel helper readonly property bool verticalLayout: (helper.lookupTableLayout === 1) || (helper.lookupTableLayout === 0 && Plasmoid.configuration.vertical_lookup_table); property int highlightCandidate: helper.lookupTableCursor property int hoveredCandidate: -1 property font preferredFont: Plasmoid.configuration.use_default_font ? Kirigami.Theme.defaultFont : Plasmoid.configuration.font readonly property alias textOffset: fontMetrics.ascent readonly property alias labelHeight: fontMetrics.height property rect position: helper.spotRect onPositionChanged : updatePosition(); onWidthChanged : updatePosition(); onHeightChanged : updatePosition(); mainItem: Item { Layout.minimumWidth: childrenRect.width Layout.minimumHeight: childrenRect.height Layout.maximumWidth: childrenRect.width Layout.maximumHeight: childrenRect.height FontMetrics { id: fontMetrics font: inputpanel.preferredFont } Column { spacing: Kirigami.Units.smallSpacing Row { id: textLabel width: auxLabel.width + preedit.width height: inputpanel.labelHeight visible: inputpanel.helper.auxVisible || inputpanel.helper.preeditVisible baselineOffset: inputpanel.textOffset PlasmaComponents3.Label { id: auxLabel anchors.baseline: parent.baseline font: inputpanel.preferredFont text: inputpanel.helper.auxText textFormat: Text.PlainText visible: inputpanel.helper.auxVisible } Item { id: preedit width: preeditLabel1.width + preeditLabel2.width + 2 height: parent.height clip: true visible: inputpanel.helper.preeditVisible baselineOffset: inputpanel.textOffset PlasmaComponents3.Label { id: preeditLabel1 anchors.baseline: parent.baseline anchors.left: parent.left font: inputpanel.preferredFont textFormat: Text.PlainText } Rectangle { color: Kirigami.Theme.textColor height: parent.height width: 1 opacity: 0.8 z: 1 anchors.verticalCenter: parent.verticalCenter anchors.left: preeditLabel1.right } PlasmaComponents3.Label { id: preeditLabel2 anchors.baseline: parent.baseline anchors.left: preeditLabel1.right font: inputpanel.preferredFont textFormat: Text.PlainText } } } GridLayout { flow: inputpanel.verticalLayout ? GridLayout.TopToBottom : GridLayout.LeftToRight columns: inputpanel.verticalLayout ? 1 : tableList.count + 1 rows: inputpanel.verticalLayout ? tableList.count + 1 : 1 Repeater { model: ListModel { id: tableList dynamicRoles: true } delegate: Item { id: candidateDelegate width: candidate.width + highlight.marginHints.left + highlight.marginHints.right height: inputpanel.labelHeight + highlight.marginHints.top + highlight.marginHints.bottom Layout.minimumWidth: width Layout.minimumHeight: height Layout.maximumWidth: width Layout.maximumHeight: height readonly property bool selected: inputpanel.highlightCandidate === model.index || candidateMouseArea.pressed Row { id: candidate width: childrenRect.width height: childrenRect.height x: highlight.marginHints.left y: highlight.marginHints.top baselineOffset: inputpanel.textOffset spacing: Kirigami.Units.smallSpacing PlasmaComponents3.Label { id: tableLabel text: model.label textFormat: Text.PlainText font: inputpanel.preferredFont opacity: 0.8 color: Kirigami.Theme.textColor anchors.baseline: parent.baseline } PlasmaComponents3.Label { text: model.text id: candidateTextLabel textFormat: Text.PlainText font: inputpanel.preferredFont color: Kirigami.Theme.textColor anchors.baseline: parent.baseline } } MouseArea { id: candidateMouseArea anchors.fill: parent hoverEnabled: true onReleased: helper.selectCandidate(model.index) onContainsMouseChanged: { inputpanel.hoveredCandidate = containsMouse ? model.index : -1; } } CandidateHighlight { id: highlight z: -1 visible: inputpanel.highlightCandidate === model.index || inputpanel.hoveredCandidate === model.index selected: candidateDelegate.selected anchors { fill: parent } } } } Row { id: button width: inputpanel.labelHeight * 2 height: inputpanel.labelHeight Layout.minimumWidth: width Layout.minimumHeight: height Layout.maximumWidth: width Layout.maximumHeight: height spacing: Kirigami.Units.smallSpacing Kirigami.Icon { id: prevButton source: inputpanel.verticalLayout ? "arrow-up" : "arrow-left" width: inputpanel.labelHeight height: width scale: prevButtonMouseArea.pressed ? 0.9 : 1 active: prevButtonMouseArea.containsMouse MouseArea { id: prevButtonMouseArea anchors.fill: parent hoverEnabled: true onReleased: inputpanel.helper.lookupTablePageUp() } } Kirigami.Icon { id: nextButton source: inputpanel.verticalLayout ? "arrow-down" : "arrow-right" width: inputpanel.labelHeight height: width scale: nextButtonMouseArea.pressed ? 0.9 : 1 active: nextButtonMouseArea.containsMouse MouseArea { id: nextButtonMouseArea anchors.fill: parent hoverEnabled: true onReleased: inputpanel.helper.lookupTablePageDown() } } } } } Kimpanel.Screen { id: screen } // Kimpanel's update may come in with in several DBus message. Use // timer to delegate the update so we get less flicker. Timer { id: timer interval: 1 onTriggered: inputpanel.updateLookupTable() } Connections { target: inputpanel.helper function onPreeditTextChanged() { var charArray = [...inputpanel.helper.preeditText]; preeditLabel1.text = charArray.slice(0, inputpanel.helper.caretPos).join(''); preeditLabel2.text = charArray.slice(inputpanel.helper.caretPos).join(''); } function onLookupTableChanged() { timer.restart(); } } } function updateLookupTable() { inputpanel.hoveredCandidate = -1; button.visible = helper.lookupTableVisible && (helper.hasPrev || helper.hasNext); var labels = helper.labels; var texts = helper.texts; var length = Math.min(labels.length, texts.length); if (helper.lookupTableVisible) { if (length< tableList.count) { tableList.remove(length, tableList.count - length); } for (var i = 0; i < length; i ++) { if (i >= tableList.count) { tableList.append({'label' : labels[i], 'text': texts[i], 'index': i}); } else { tableList.set(i, {'label' : labels[i], 'text': texts[i], 'index': i}); } } } else { tableList.clear(); } } function updatePosition() { var rect = screen.geometryForPoint(position.x, position.y); var devicePerPixelRatio = screen.devicePixelRatioForPoint(position.x, position.y); var x, y; var width = inputpanel.width * devicePerPixelRatio; var height = inputpanel.height * devicePerPixelRatio; if (position.x < rect.x) { x = rect.x; } else { x = position.x; } if (position.y < rect.y) { y = rect.y; } else { y = position.y + position.height; } if (x + width > rect.x + rect.width) { x = rect.x + rect.width - width; } if (y + height > rect.y + rect.height) { if (y > rect.y + rect.height) { y = rect.y + rect.height - height - 40; } else { y = y - height - (position.height === 0 ? 40 : position.height); } } var newRect = screen.geometryForPoint(x, y); devicePerPixelRatio = screen.devicePixelRatioForPoint(x, y); inputpanel.x = newRect.x + (x - newRect.x) / devicePerPixelRatio; inputpanel.y = newRect.y + (y - newRect.y) / devicePerPixelRatio; } }