🏡 index : quine-global-labs/mirror-kde-plasma-desktop.git

/*
    SPDX-FileCopyrightText: 2014-2017 Weng Xuetian <wengxt@gmail.com>

    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;
    }
}