feat(settings): add input method selection page

main
reionwong 2 months ago
parent 88abc2fec4
commit 30738dd13a

@ -61,6 +61,7 @@ set(SRCS
src/vpn/nm-pptp-service.h
src/networkproxy.cpp
src/defaultapplications.cpp
src/inputmethod.cpp
)
set(RESOURCES

@ -14,6 +14,7 @@
#include "docksettings.h"
#include "fonts/fonts.h"
#include "fontsmodel.h"
#include "inputmethod.h"
#include "language.h"
#include "networkproxy.h"
#include "notifications.h"
@ -75,6 +76,7 @@ Application::Application(int &argc, char **argv)
qmlRegisterType<CursorThemeModel>(uri, 1, 0, "CursorThemeModel");
qmlRegisterType<About>(uri, 1, 0, "About");
qmlRegisterType<Language>(uri, 1, 0, "Language");
qmlRegisterType<InputMethod>(uri, 1, 0, "InputMethod");
qmlRegisterType<Fonts>(uri, 1, 0, "Fonts");
qmlRegisterType<PowerManager>(uri, 1, 0, "PowerManager");
qmlRegisterType<Mouse>(uri, 1, 0, "Mouse");

@ -0,0 +1,12 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="#fff">
<path d="m2.5 3.5c-1.1046 0-2 0.89543-2 2v5c0 1.1046 0.89543 2 2 2h11c1.1046 0 2-0.89543 2-2v-5c0-1.1046-0.89543-2-2-2zm0 1.25h11c0.41421 0 0.75 0.33579 0.75 0.75v5c0 0.41421-0.33579 0.75-0.75 0.75h-11c-0.41421 0-0.75-0.33579-0.75-0.75v-5c0-0.41421 0.33579-0.75 0.75-0.75z"/>
<rect x="3" y="6" width="1.5" height="1.25" rx=".3"/>
<rect x="5.5" y="6" width="1.5" height="1.25" rx=".3"/>
<rect x="8" y="6" width="1.5" height="1.25" rx=".3"/>
<rect x="10.5" y="6" width="1.5" height="1.25" rx=".3"/>
<rect x="12.25" y="8.25" width="1" height="1.25" rx=".3"/>
<rect x="3" y="8.25" width="1.5" height="1.25" rx=".3"/>
<rect x="5.5" y="8.5" width="5" height="1" rx=".3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 810 B

@ -0,0 +1,101 @@
#include "inputmethod.h"
#include "desktopentry.h"
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#include <QDBusReply>
#include <QDBusServiceWatcher>
#include <QDebug>
#include <QFile>
#include <QStandardPaths>
namespace {
constexpr auto service = "com.cutefish.InputMethod1";
constexpr auto path = "/com/cutefish/InputMethod1";
QVariantMap describe(const QString &id)
{
QVariantMap method {{QStringLiteral("id"), id}, {QStringLiteral("name"), id}};
const QString file = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, id);
QFile desktop(file);
DesktopEntryData data;
if (!file.isEmpty() && desktop.open(QIODevice::ReadOnly)
&& DesktopEntry::parse(id, file, desktop.readAll(), &data)) {
if (!data.name.isEmpty())
method[QStringLiteral("name")] = data.name;
method[QStringLiteral("icon")] = data.icon;
}
return method;
}
} // namespace
InputMethod::InputMethod(QObject *parent)
: QObject(parent)
{
auto *watcher = new QDBusServiceWatcher(QLatin1String(service), QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForOwnerChange, this);
connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this,
&InputMethod::refreshInterface);
refreshInterface();
}
void InputMethod::refreshInterface()
{
delete m_interface;
m_interface = new QDBusInterface(QLatin1String(service), QLatin1String(path),
QLatin1String(service), QDBusConnection::sessionBus(), this);
if (m_interface->isValid()) {
connect(m_interface, SIGNAL(inputMethodChanged()), this, SIGNAL(currentChanged()));
connect(m_interface, SIGNAL(runningChanged()), this, SIGNAL(runningChanged()));
}
emit availableChanged();
emit currentChanged();
emit runningChanged();
refreshMethods();
}
void InputMethod::refreshMethods()
{
m_methods.clear();
if (m_interface->isValid()) {
const QDBusReply<QStringList> reply =
m_interface->call(QStringLiteral("availableInputMethods"));
for (const auto &id : reply.value())
m_methods << describe(id);
}
emit methodsChanged();
}
bool InputMethod::available() const
{
return m_interface->isValid();
}
QString InputMethod::current() const
{
return m_interface->isValid() ? m_interface->property("inputMethod").toString() : QString();
}
bool InputMethod::running() const
{
return m_interface->isValid() && m_interface->property("running").toBool();
}
void InputMethod::select(const QString &id)
{
if (!m_interface->isValid())
return;
auto *watcher = new QDBusPendingCallWatcher(
m_interface->asyncCall(QStringLiteral("setInputMethod"), id), this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this, id](QDBusPendingCallWatcher *call) {
const QDBusPendingReply<bool> reply = *call;
if (reply.isError() || !reply.value()) {
qWarning() << "Cannot select input method" << id << reply.error().message();
refreshMethods();
}
emit currentChanged();
call->deleteLater();
});
}

@ -0,0 +1,40 @@
#pragma once
#include <QDBusInterface>
#include <QObject>
#include <QVariantList>
// Client of the compositor's com.cutefish.InputMethod1 service.
class InputMethod : public QObject
{
Q_OBJECT
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(QVariantList methods READ methods NOTIFY methodsChanged)
Q_PROPERTY(QString current READ current NOTIFY currentChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
public:
explicit InputMethod(QObject *parent = nullptr);
bool available() const;
QVariantList methods() const { return m_methods; }
QString current() const;
bool running() const;
// An empty id turns the input method off.
Q_INVOKABLE void select(const QString &id);
signals:
void availableChanged();
void methodsChanged();
void currentChanged();
void runningChanged();
private:
void refreshInterface();
void refreshMethods();
QDBusInterface *m_interface = nullptr;
QVariantList m_methods;
};

@ -0,0 +1,120 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import FishUI 1.0 as FishUI
import Cutefish.Settings 1.0
import "../"
ItemPage {
headerTitle: qsTr("Input Method")
InputMethod {
id: inputMethod
}
Scrollable {
anchors.fill: parent
contentHeight: layout.implicitHeight
ColumnLayout {
id: layout
anchors.fill: parent
spacing: FishUI.Units.largeSpacing * 2
RoundedItem {
Label {
text: qsTr("Input method")
color: FishUI.Theme.disabledTextColor
}
Label {
Layout.fillWidth: true
visible: !inputMethod.available
text: qsTr("Input methods can only be selected in a Cutefish Wayland session.")
wrapMode: Text.WordWrap
}
Repeater {
model: inputMethod.available
? [{ "id": "", "name": qsTr("None"), "icon": "" }]
.concat(inputMethod.methods)
: []
delegate: MouseArea {
id: item
property bool isSelected: modelData.id === inputMethod.current
Layout.fillWidth: true
implicitHeight: Math.max(36, row.implicitHeight + FishUI.Units.largeSpacing)
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: inputMethod.select(modelData.id)
Rectangle {
anchors.fill: parent
radius: FishUI.Theme.smallRadius
color: item.isSelected ? FishUI.Theme.highlightColor
: item.containsMouse ? FishUI.Theme.disabledTextColor
: "transparent"
opacity: item.isSelected ? 1 : 0.1
}
RowLayout {
id: row
anchors.fill: parent
anchors.leftMargin: FishUI.Units.smallSpacing
anchors.rightMargin: FishUI.Units.largeSpacing
spacing: FishUI.Units.largeSpacing
FishUI.IconItem {
width: 24
height: 24
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: modelData.icon ? modelData.icon : "input-keyboard"
smooth: true
}
Label {
Layout.fillWidth: true
text: modelData.name
elide: Text.ElideRight
color: item.isSelected ? FishUI.Theme.highlightedTextColor
: FishUI.Theme.textColor
}
Image {
width: 16
height: 16
sourceSize: Qt.size(16, 16)
source: "qrc:/images/dark/checked.svg"
visible: item.isSelected
smooth: false
}
}
}
}
Label {
Layout.fillWidth: true
visible: inputMethod.available && inputMethod.current !== "" && !inputMethod.running
text: qsTr("The selected input method is not running.")
color: FishUI.Theme.disabledTextColor
wrapMode: Text.WordWrap
}
}
Label {
Layout.fillWidth: true
visible: inputMethod.available
text: qsTr("Applications started before the input method may need to be restarted.")
color: FishUI.Theme.disabledTextColor
wrapMode: Text.WordWrap
leftPadding: FishUI.Units.largeSpacing * 1.5
rightPadding: FishUI.Units.largeSpacing * 1.5
}
}
}
}

@ -275,6 +275,15 @@ Item {
category: qsTr("System")
}
ListElement {
title: qsTr("Input Method")
name: "inputmethod"
page: "qrc:/qml/InputMethod/Main.qml"
iconSource: "inputmethod.svg"
iconColor: "#6F7DE8"
category: qsTr("System")
}
ListElement {
title: qsTr("Battery")
name: "battery"

@ -60,6 +60,7 @@
<file>images/sidebar/dark/display.svg</file>
<file>images/sidebar/dark/dock.svg</file>
<file>images/sidebar/dark/language.svg</file>
<file>images/sidebar/dark/inputmethod.svg</file>
<file>images/sidebar/dark/network.svg</file>
<file>images/sidebar/dark/wallpaper.svg</file>
<file>images/sidebar/light/about.svg</file>
@ -108,6 +109,7 @@
<file>images/sidebar/dark/hotspot.svg</file>
<file>qml/Application/Main.qml</file>
<file>qml/Cursor/Main.qml</file>
<file>qml/InputMethod/Main.qml</file>
<file>images/sidebar/dark/cursor.svg</file>
<file>qml/DateTime/Main.qml</file>
<file>qml/DateTime/TimeZoneDialog.qml</file>

@ -52,6 +52,36 @@
</context>
<context>
<name>Main</name>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="10"/>
<source>Input Method</source>
<translation>输入法</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="27"/>
<source>Input method</source>
<translation>输入法</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="34"/>
<source>Input methods can only be selected in a Cutefish Wayland session.</source>
<translation>只能在 Cutefish Wayland 会话中选择输入法。</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="42"/>
<source>None</source>
<translation>无</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="103"/>
<source>The selected input method is not running.</source>
<translation>所选输入法未在运行。</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="112"/>
<source>Applications started before the input method may need to be restarted.</source>
<translation>在输入法启动前打开的应用可能需要重新启动。</translation>
</message>
<message>
<location filename="../src/qml/About/Main.qml" line="28"/>
<source>About</source>
@ -908,6 +938,11 @@
</context>
<context>
<name>SideBar</name>
<message>
<location filename="../src/qml/SideBar.qml" line="279"/>
<source>Input Method</source>
<translation>输入法</translation>
</message>
<message>
<location filename="../src/qml/SideBar.qml" line="109"/>
<source>Display</source>

@ -70,6 +70,36 @@
</context>
<context>
<name>Main</name>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="10"/>
<source>Input Method</source>
<translation>輸入法</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="27"/>
<source>Input method</source>
<translation>輸入法</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="34"/>
<source>Input methods can only be selected in a Cutefish Wayland session.</source>
<translation>只能在 Cutefish Wayland 工作階段中選擇輸入法。</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="42"/>
<source>None</source>
<translation>無</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="103"/>
<source>The selected input method is not running.</source>
<translation>所選輸入法未在執行。</translation>
</message>
<message>
<location filename="../src/qml/InputMethod/Main.qml" line="112"/>
<source>Applications started before the input method may need to be restarted.</source>
<translation>在輸入法啟動前開啟的應用程式可能需要重新啟動。</translation>
</message>
<message>
<location filename="../src/qml/About/Main.qml" line="28"/>
<source>About</source>
@ -533,6 +563,11 @@
</context>
<context>
<name>SideBar</name>
<message>
<location filename="../src/qml/SideBar.qml" line="279"/>
<source>Input Method</source>
<translation>輸入法</translation>
</message>
<message>
<location filename="../src/qml/SideBar.qml" line="131"/>
<source>User</source>

Loading…
Cancel
Save