Skip to content

Instantly share code, notes, and snippets.

@Zren
Created July 29, 2018 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zren/7b72293743f39b2b2ebac3d11f3ad59b to your computer and use it in GitHub Desktop.
Save Zren/7b72293743f39b2b2ebac3d11f3ad59b to your computer and use it in GitHub Desktop.
KDE5 Locally Integrated Menu
diff --git a/plugins/kdecorations/aurorae/src/aurorae.cpp b/plugins/kdecorations/aurorae/src/aurorae.cpp
index 4c5879b..ff00e22 100644
--- a/plugins/kdecorations/aurorae/src/aurorae.cpp
+++ b/plugins/kdecorations/aurorae/src/aurorae.cpp
@@ -448,6 +448,11 @@ QVariant Decoration::readConfig(const QString &key, const QVariant &defaultValue
return config->group(m_themeName).readEntry(key, defaultValue);
}
+qulonglong Decoration::windowIdAsLong() const
+{
+ return (qulonglong)(client().data()->windowId());
+}
+
void Decoration::setupBorders(QQuickItem *item)
{
m_borders = item->findChild<KWin::Borders*>(QStringLiteral("borders"));
diff --git a/plugins/kdecorations/aurorae/src/aurorae.h b/plugins/kdecorations/aurorae/src/aurorae.h
index 3b28099..db42453 100644
--- a/plugins/kdecorations/aurorae/src/aurorae.h
+++ b/plugins/kdecorations/aurorae/src/aurorae.h
@@ -56,6 +56,8 @@ public:
KDecoration2::DecoratedClient *clientPointer() const;
+ Q_INVOKABLE qulonglong windowIdAsLong() const;
+
public Q_SLOTS:
void init() override;
void installTitleItem(QQuickItem *item);
diff --git a/plugins/kdecorations/aurorae/src/qml/aurorae.qml b/plugins/kdecorations/aurorae/src/qml/aurorae.qml
index 5b81241..d866526 100644
--- a/plugins/kdecorations/aurorae/src/qml/aurorae.qml
+++ b/plugins/kdecorations/aurorae/src/qml/aurorae.qml
@@ -15,8 +15,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtGraphicalEffects 1.0
import org.kde.kwin.decoration 0.1
import org.kde.plasma.core 2.0 as PlasmaCore
+import org.kde.plasma.components 2.0 as PlasmaComponents
+import org.kde.plasma.private.appmenu 1.0 as AppMenuPrivate
Decoration {
id: root
@@ -155,6 +159,7 @@ Decoration {
}
Text {
id: caption
+ visible: false
text: decoration.client.caption
textFormat: Text.PlainText
horizontalAlignment: auroraeTheme.horizontalAlignment
@@ -178,7 +183,117 @@ Decoration {
duration: auroraeTheme.animationTime
}
}
+
+ // opacity: appMenuArea.shown ? 0.3 : 1
+ // Behavior on opacity {
+ // enabled: root.animate
+ // NumberAnimation {
+ // duration: appMenuArea.showDuration
+ // }
+ // }
+
+ onHeightChanged: console.log('caption.height', height, 'implicitHeight', implicitHeight)
+ }
+
+ LinearGradient {
+ id: captionMask
+ anchors.fill: caption
+ source: caption
+ start: Qt.point(0, 0)
+ end: Qt.point(width, 0)
+
+ readonly property real appMenuRight: Math.max(0, appMenuButtonsLayout.implicitWidth - (caption.anchors.leftMargin - appMenuArea.anchors.leftMargin))
+ readonly property real fadeInWidth: 10 // * dpi
+ readonly property real appMenuRatio: appMenuRight / width
+ readonly property real fadeInEndRatio: (appMenuRight + fadeInWidth) / width
+
+ gradient: Gradient {
+ GradientStop { position: 0; color: "transparent" }
+ GradientStop { position: captionMask.appMenuRatio; color: "transparent" }
+ GradientStop { position: captionMask.fadeInEndRatio; color: "white" }
+ GradientStop { position: 1; color: "white" }
+ }
+ }
+
+ // Rectangle {
+ // anchors.fill: captionMask
+ // color: "transparent"
+ // border.width: 1
+ // border.color: "#f00"
+ // }
+
+ Item {
+ id: appMenuArea
+ anchors.top: caption.top
+ anchors.bottom: caption.bottom
+ anchors.left: leftButtonGroup.right
+ anchors.leftMargin: auroraeTheme.buttonSpacing * auroraeTheme.buttonSizeFactor
+
+ readonly property int showDuration: 200
+ readonly property bool shown: decoration.client.active
+
+ AppMenuPrivate.AppMenuModel {
+ id: appMenuModel
+ // onRequestActivateIndex: plasmoid.nativeInterface.requestActivateIndex(index)
+ useActiveWindowId: false
+ // windowId: decoration.client.windowId
+
+ Component.onCompleted: {
+ // console.log('C decoration.client.windowId', decoration.client.windowId())
+ console.log('C decoration.windowIdAsLong', decoration.windowIdAsLong())
+ appMenuModel.setWindowIdByLong(decoration.windowIdAsLong())
+
+ // decoration.client.windowIdChanged.connect(function(){
+ // // console.log('S decoration.client.windowId', decoration.client.windowId())
+ // console.log('S decoration.windowIdAsLong', decoration.windowIdAsLong())
+ // // appMenuModel.setWindowId(decoration.client.windowId())
+ // // appMenuModel.setWindowId(decoration.client.windowId())
+ // })
+ }
+ }
+
+ RowLayout {
+ id: appMenuButtonsLayout
+ anchors.fill: parent
+ spacing: 0
+
+ Repeater {
+ id: buttonRepeater
+ // model: [
+ // { activeMenu: '&File' },
+ // { activeMenu: '&View' },
+ // { activeMenu: '&Help' }
+ // ]
+ model: appMenuModel
+
+ PlasmaComponents.ToolButton {
+ id: appMenuButton
+ readonly property int buttonIndex: index
+ Layout.preferredWidth: minimumWidth
+ Layout.fillHeight: true
+ // text: modelData.activeMenu
+ text: model.activeMenu
+ onClicked: {
+ // app menu
+ var pos = appMenuButton.mapToItem(null, 0, 0); // null = "map to scene"
+ var rect = Qt.rect(pos.x, pos.y, appMenuButton.width, appMenuButton.height);
+ var actionId = model.actionId; // 0 = root
+ decoration.requestShowApplicationMenu(rect, actionId);
+ console.log('index', index)
+ console.log('model.activeMenu', model.activeMenu)
+ console.log('model.activeActions', model.activeActions)
+ console.log('model.index', model.index)
+ console.log('model.actionId', model.actionId)
+ }
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+ }
}
+
PlasmaCore.FrameSvgItem {
id: innerBorder
anchors {
diff --git a/applets/appmenu/plugin/appmenumodel.cpp b/applets/appmenu/plugin/appmenumodel.cpp
index 1b5dc7b..049405a 100644
--- a/applets/appmenu/plugin/appmenumodel.cpp
+++ b/applets/appmenu/plugin/appmenumodel.cpp
@@ -68,14 +68,16 @@ AppMenuModel::AppMenuModel(QObject *parent)
: QAbstractListModel(parent),
m_serviceWatcher(new QDBusServiceWatcher(this))
{
- connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
connect(this, &AppMenuModel::modelNeedsUpdate, this, [this] {
if (!m_updatePending) {
m_updatePending = true;
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
}
});
- onActiveWindowChanged(KWindowSystem::activeWindow());
+ if (m_useActiveWindowId) {
+ connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
+ onActiveWindowChanged(KWindowSystem::activeWindow());
+ }
m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
//if our current DBus connection gets lost, close the menu
@@ -121,10 +123,47 @@ void AppMenuModel::update()
m_updatePending = false;
}
+bool AppMenuModel::useActiveWindowId() const
+{
+ return m_useActiveWindowId;
+}
+
+void AppMenuModel::setUseActiveWindowId(bool set)
+{
+ if (m_useActiveWindowId != set) {
+ if (set) {
+ connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
+ onActiveWindowChanged(KWindowSystem::activeWindow());
+ } else {
+ disconnect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
+ }
+ m_useActiveWindowId = set;
+ emit useActiveWindowIdChanged();
+ }
+}
+
void AppMenuModel::onActiveWindowChanged(WId id)
{
- qApp->removeNativeEventFilter(this);
+ setWindowId(id);
+}
+
+WId AppMenuModel::windowId() const
+{
+ return m_currentWindowId;
+}
+
+
+void AppMenuModel::setWindowIdByLong(qulonglong id)
+{
+ setWindowId((WId)id);
+}
+
+void AppMenuModel::setWindowId(WId id)
+{
+ if (m_useActiveWindowId) {
+ qApp->removeNativeEventFilter(this);
+ }
if (!id) {
setMenuAvailable(false);
@@ -202,8 +241,11 @@ void AppMenuModel::onActiveWindowChanged(WId id)
// monitor whether an app menu becomes available later
// this can happen when an app starts, shows its window, and only later announces global menu (e.g. Firefox)
- qApp->installNativeEventFilter(this);
+ if (m_useActiveWindowId) {
+ qApp->installNativeEventFilter(this);
+ }
m_currentWindowId = id;
+ emit windowIdChanged();
//no menu found, set it to unavailable
setMenuAvailable(false);
@@ -219,9 +261,12 @@ QHash<int, QByteArray> AppMenuModel::roleNames() const
QHash<int, QByteArray> roleNames;
roleNames[MenuRole] = QByteArrayLiteral("activeMenu");
roleNames[ActionRole] = QByteArrayLiteral("activeActions");
+ roleNames[ActionIdRole] = QByteArrayLiteral("actionId");
return roleNames;
}
+static const char *DBUSMENU_PROPERTY_ID = "_dbusmenu_id"; // From libdbusmenuqt/dbusmenuimporter.cpp
+
QVariant AppMenuModel::data(const QModelIndex &index, int role) const
{
const int row = index.row();
@@ -238,6 +283,8 @@ QVariant AppMenuModel::data(const QModelIndex &index, int role) const
return actions.at(row)->text();
} else if (role == ActionRole) {
return qVariantFromValue((void *) actions.at(row));
+ } else if (role == ActionIdRole) {
+ return actions.at(row)->property(DBUSMENU_PROPERTY_ID).toInt();
}
return QVariant();
diff --git a/applets/appmenu/plugin/appmenumodel.h b/applets/appmenu/plugin/appmenumodel.h
index 7bd35fc..5632e29 100644
--- a/applets/appmenu/plugin/appmenumodel.h
+++ b/applets/appmenu/plugin/appmenumodel.h
@@ -36,6 +36,8 @@ class AppMenuModel : public QAbstractListModel, public QAbstractNativeEventFilte
Q_OBJECT
Q_PROPERTY(bool menuAvailable READ menuAvailable WRITE setMenuAvailable NOTIFY menuAvailableChanged)
+ Q_PROPERTY(bool useActiveWindowId READ useActiveWindowId WRITE setUseActiveWindowId NOTIFY useActiveWindowIdChanged)
+ // Q_PROPERTY(int windowId READ windowId WRITE setWindowId NOTIFY windowIdChanged)
public:
explicit AppMenuModel(QObject *parent = nullptr);
@@ -43,7 +45,8 @@ public:
enum AppMenuRole {
MenuRole = Qt::UserRole+1, // TODO this should be Qt::DisplayRole
- ActionRole
+ ActionRole,
+ ActionIdRole
};
QVariant data(const QModelIndex &index, int role) const override;
@@ -55,6 +58,15 @@ public:
bool menuAvailable() const;
void setMenuAvailable(bool set);
+ bool useActiveWindowId() const;
+ void setUseActiveWindowId(bool set);
+
+ WId windowId() const;
+ Q_INVOKABLE void setWindowIdByLong(qulonglong set);
+ void setWindowId(WId set);
+
+ void setWindowIdByDecoration(WId set);
+
signals:
void requestActivateIndex(int index);
@@ -68,10 +80,13 @@ private Q_SLOTS:
signals:
void menuAvailableChanged();
void modelNeedsUpdate();
+ void useActiveWindowIdChanged();
+ void windowIdChanged();
private:
bool m_menuAvailable;
bool m_updatePending = false;
+ bool m_useActiveWindowId = true;
WId m_currentWindowId = 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment