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

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

    SPDX-License-Identifier: GPL-2.0-or-later
*/

import QtQuick 2.6
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.private.kimpanel 0.1 as Kimpanel
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.plasmoid 2.0

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
    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: preferredFont
        }
        Column {
            spacing: Kirigami.Units.smallSpacing
            Row {
                id: textLabel
                width: auxLabel.width + preedit.width
                height: inputpanel.labelHeight
                visible: helper.auxVisible || helper.preeditVisible
                baselineOffset: inputpanel.textOffset
                PlasmaComponents3.Label {
                    id: auxLabel
                    anchors.baseline: parent.baseline
                    font: preferredFont
                    text: helper.auxText
                    textFormat: Text.PlainText
                    visible: helper.auxVisible
                }
                Item {
                    id: preedit
                    width: preeditLabel1.width + preeditLabel2.width + 2
                    height: parent.height
                    clip: true
                    visible: helper.preeditVisible
                    baselineOffset: inputpanel.textOffset
                    PlasmaComponents3.Label {
                        id: preeditLabel1
                        anchors.baseline: parent.baseline
                        anchors.left: parent.left
                        font: 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: 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: preferredFont
                                opacity: 0.8
                                color: Kirigami.Theme.textColor
                                anchors.baseline: parent.baseline
                            }
                            PlasmaComponents3.Label {
                                id: textLabel
                                text: model.text
                                textFormat: Text.PlainText
                                font: 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: 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: 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: updateLookupTable()
        }

        Connections {
            target: helper

            function onPreeditTextChanged() {
                var charArray = [...helper.preeditText];
                preeditLabel1.text = charArray.slice(0, helper.caretPos).join('');
                preeditLabel2.text = charArray.slice(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;
    }
}