plasmoides KDE Store/GitHub (gnome-pager, kMenu, kvitals, windowtitle, claude, launchpad)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.plasma.configuration 2.0
|
||||
|
||||
ConfigModel {
|
||||
ConfigCategory {
|
||||
name: i18n("General")
|
||||
icon: "preferences-desktop-plasma"
|
||||
source: "ConfigGeneral.qml"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile name=""/>
|
||||
|
||||
<group name="General">
|
||||
<entry name="icon" type="String">
|
||||
<label>The name of the icon to use for the compact representation (e.g. on a small panel).</label>
|
||||
<default>start-here-kde-symbolic</default>
|
||||
</entry>
|
||||
<entry name="useCustomButtonImage" type="Bool">
|
||||
<label>Whether to use a custom image instead of an icon in the compact representation (e.g. on a small panel).</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="customButtonImage" type="Url">
|
||||
<label>The URL of the custom image to use instead of an icon in the compact representation (e.g. on a small panel).</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
|
||||
<entry name="appNameFormat" type="Int">
|
||||
<label>The format used in the display of application names: 0 = NameOnly, 1 = GenericNameOnly, 2 = NameAndGenericName, 3 = GenericNameAndName</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="limitDepth" type="Bool">
|
||||
<label>Whether to flatten top-level menu categories to a single level instead of displaying sub-categories.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="alphaSort" type="Bool">
|
||||
<label>Whether to sort menu contents alphabetically or use manual/system sort order.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
|
||||
<entry name="recentOrdering" type="Int">
|
||||
<label>How should the previously used apps/docs be ordered: 0 = RecentFirst, 1 = PopularFirst</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="favoriteApps" type="StringList">
|
||||
<label>List of general favorites. Supported values are menu id's (usually .desktop file names), special URLs that expand into default applications (e.g. preferred://browser), document URLs and KPeople contact URIs.</label>
|
||||
<default>preferred://browser,org.kde.kontact.desktop,systemsettings.desktop,org.kde.dolphin.desktop,org.kde.discover</default>
|
||||
</entry>
|
||||
<entry name="favoriteSystemActions" type="StringList">
|
||||
<label>List of system action favorites.</label>
|
||||
<default>shutdown,reboot,logout,lock-screen</default>
|
||||
</entry>
|
||||
<entry name="favoritesPortedToKAstats" type="Bool">
|
||||
<label>Are the favorites ported to use KActivitiesStats to allow per-activity favorites</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="hiddenApplications" type="StringList">
|
||||
<label>List of menu id's (usually .desktop file names) of apps that should not be shown in the menu.</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
|
||||
<entry name="showRecentApps" type="Bool">
|
||||
<label>Whether to show the "Recent Applications" category.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="showRecentDocs" type="Bool">
|
||||
<label>Whether to show the "Recent Files" category.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
|
||||
<entry name="useExtraRunners" type="Bool">
|
||||
<label>Whether to use additional KRunner plugins to produce results in the search.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="extraRunners" type="StringList">
|
||||
<label>The plugin id's of additional KRunner plugins to use. Only used if useExtraRunners is true.</label>
|
||||
<default>krunner_shell,krunner_bookmarksrunner,baloosearch,locations</default>
|
||||
</entry>
|
||||
<entry name="alignResultsToBottom" type="Bool">
|
||||
<label>Whether to align search results to the bottom of the menu representation (e.g. panel popup) instead of the top.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="showIconsRootLevel" type="Bool">
|
||||
<label>Whether to show icons on the root level of the menu.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="appsIconSize" type="Enum">
|
||||
<label></label>
|
||||
<default>3</default>
|
||||
</entry>
|
||||
<entry name="favsIconSize" type="Enum">
|
||||
<label></label>
|
||||
<default>2</default>
|
||||
</entry>
|
||||
<entry name="systemIconSize" type="Enum">
|
||||
<label></label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
|
||||
</group>
|
||||
</kcfg>
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Aurélien Gâteau <agateau@kde.org>
|
||||
SPDX-FileCopyrightText: 2014-2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property QtObject menu
|
||||
property Item visualParent
|
||||
property variant actionList
|
||||
property bool opened: menu ? (menu.status !== PlasmaExtras.Menu.Closed) : false
|
||||
|
||||
signal actionClicked(string actionId, variant actionArgument)
|
||||
signal closed
|
||||
|
||||
onActionListChanged: refreshMenu();
|
||||
|
||||
onOpenedChanged: {
|
||||
if (!opened) {
|
||||
closed();
|
||||
}
|
||||
}
|
||||
|
||||
function open(x, y) {
|
||||
if (!actionList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (x && y) {
|
||||
menu.open(x, y);
|
||||
} else {
|
||||
menu.open();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshMenu() {
|
||||
if (menu) {
|
||||
menu.destroy();
|
||||
}
|
||||
|
||||
if (!actionList) {
|
||||
return;
|
||||
}
|
||||
|
||||
menu = contextMenuComponent.createObject(root);
|
||||
|
||||
fillMenu(menu, actionList);
|
||||
}
|
||||
|
||||
function fillMenu(menu, items) {
|
||||
items.forEach(function(actionItem) {
|
||||
if (actionItem.subActions) {
|
||||
// This is a menu
|
||||
var submenuItem = contextSubmenuItemComponent.createObject(
|
||||
menu, { "actionItem" : actionItem });
|
||||
|
||||
fillMenu(submenuItem.submenu, actionItem.subActions);
|
||||
|
||||
} else {
|
||||
var item = contextMenuItemComponent.createObject(
|
||||
menu,
|
||||
{
|
||||
"actionItem": actionItem,
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contextMenuComponent
|
||||
|
||||
PlasmaExtras.Menu {
|
||||
visualParent: root.visualParent
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contextSubmenuItemComponent
|
||||
|
||||
PlasmaExtras.MenuItem {
|
||||
id: submenuItem
|
||||
|
||||
property variant actionItem
|
||||
|
||||
text: actionItem.text ? actionItem.text : ""
|
||||
icon: actionItem.icon ? actionItem.icon : null
|
||||
|
||||
property PlasmaExtras.Menu submenu: PlasmaExtras.Menu {
|
||||
visualParent: submenuItem.action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contextMenuItemComponent
|
||||
|
||||
PlasmaExtras.MenuItem {
|
||||
property variant actionItem
|
||||
|
||||
text : actionItem.text ? actionItem.text : ""
|
||||
enabled : actionItem.type !== "title" && ("enabled" in actionItem ? actionItem.enabled : true)
|
||||
separator : actionItem.type === "separator"
|
||||
section : actionItem.type === "title"
|
||||
icon : actionItem.icon ? actionItem.icon : null
|
||||
checkable : actionItem.checkable ? actionItem.checkable : false
|
||||
checked : actionItem.checked ? actionItem.checked : false
|
||||
|
||||
onClicked: {
|
||||
root.actionClicked(actionItem.actionId, actionItem.actionArgument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013-2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import org.kde.plasma.private.kicker 0.1 as Kicker
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool vertical: (Plasmoid.formFactor === PlasmaCore.Types.Vertical)
|
||||
readonly property bool useCustomButtonImage: (Plasmoid.configuration.useCustomButtonImage
|
||||
&& Plasmoid.configuration.customButtonImage.length !== 0)
|
||||
|
||||
readonly property Component dashWindowComponent: kicker.isDash ? Qt.createComponent(Qt.resolvedUrl("./DashboardRepresentation.qml"), root) : null
|
||||
readonly property Kicker.DashboardWindow dashWindow: dashWindowComponent && dashWindowComponent.status === Component.Ready
|
||||
? dashWindowComponent.createObject(root, { visualParent: root }) : null
|
||||
|
||||
onWidthChanged: updateSizeHints()
|
||||
onHeightChanged: updateSizeHints()
|
||||
|
||||
function updateSizeHints() {
|
||||
if (useCustomButtonImage) {
|
||||
if (vertical) {
|
||||
const scaledHeight = Math.floor(parent.width * (buttonIcon.implicitHeight / buttonIcon.implicitWidth));
|
||||
root.Layout.minimumWidth = -1;
|
||||
root.Layout.minimumHeight = scaledHeight;
|
||||
root.Layout.maximumWidth = Kirigami.Units.iconSizes.huge;
|
||||
root.Layout.maximumHeight = scaledHeight;
|
||||
} else {
|
||||
const scaledWidth = Math.floor(parent.height * (buttonIcon.implicitWidth / buttonIcon.implicitHeight));
|
||||
root.Layout.minimumWidth = scaledWidth;
|
||||
root.Layout.minimumHeight = -1;
|
||||
root.Layout.maximumWidth = scaledWidth;
|
||||
root.Layout.maximumHeight = Kirigami.Units.iconSizes.huge;
|
||||
}
|
||||
} else {
|
||||
root.Layout.minimumWidth = Kirigami.Units.iconSizes.huge;
|
||||
root.Layout.minimumHeight = Kirigami.Units.iconSizes.huge;
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: buttonIcon
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property double aspectRatio: root.vertical
|
||||
? implicitHeight / implicitWidth
|
||||
: implicitWidth / implicitHeight
|
||||
|
||||
active: mouseArea.containsMouse && !justOpenedTimer.running
|
||||
source: root.useCustomButtonImage ? Plasmoid.configuration.customButtonImage : Plasmoid.configuration.icon
|
||||
|
||||
// A custom icon could also be rectangular. However, if a square, custom, icon is given, assume it
|
||||
// to be an icon and round it to the nearest icon size again to avoid scaling artifacts.
|
||||
roundToIconSize: !root.useCustomButtonImage || aspectRatio === 1
|
||||
|
||||
onSourceChanged: root.updateSizeHints()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property bool wasExpanded: false;
|
||||
|
||||
activeFocusOnTab: true
|
||||
hoverEnabled: !root.dashWindow || !root.dashWindow.visible
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Space:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Select:
|
||||
Plasmoid.activated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Accessible.name: Plasmoid.title
|
||||
Accessible.description: toolTipSubText
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
onPressed: {
|
||||
if (!kicker.isDash) {
|
||||
wasExpanded = kicker.expanded
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (kicker.isDash) {
|
||||
root.dashWindow.toggle();
|
||||
justOpenedTimer.start();
|
||||
} else {
|
||||
kicker.expanded = !wasExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Plasmoid
|
||||
enabled: kicker.isDash && root.dashWindow !== null
|
||||
|
||||
function onActivated() {
|
||||
root.dashWindow.toggle();
|
||||
justOpenedTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import org.kde.draganddrop 2.0 as DragDrop
|
||||
import org.kde.kirigami 2.5 as Kirigami
|
||||
import org.kde.iconthemes as KIconThemes
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.kcmutils as KCM
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: configGeneral
|
||||
|
||||
property bool isDash: (Plasmoid.pluginName === "org.kde.plasma.kickerdash")
|
||||
|
||||
property string cfg_icon: Plasmoid.configuration.icon
|
||||
property bool cfg_useCustomButtonImage: Plasmoid.configuration.useCustomButtonImage
|
||||
property string cfg_customButtonImage: Plasmoid.configuration.customButtonImage
|
||||
|
||||
property alias cfg_appNameFormat: appNameFormat.currentIndex
|
||||
property alias cfg_limitDepth: limitDepth.checked
|
||||
property alias cfg_alphaSort: alphaSort.checked
|
||||
property alias cfg_showIconsRootLevel: showIconsRootLevel.checked
|
||||
|
||||
property alias cfg_recentOrdering: recentOrdering.currentIndex
|
||||
property alias cfg_showRecentApps: showRecentApps.checked
|
||||
property alias cfg_showRecentDocs: showRecentDocs.checked
|
||||
|
||||
property alias cfg_useExtraRunners: useExtraRunners.checked
|
||||
property alias cfg_alignResultsToBottom: alignResultsToBottom.checked
|
||||
|
||||
property alias cfg_appsIconSize: appsIconSize.currentIndex
|
||||
property alias cfg_favsIconSize: favsIconSize.currentIndex
|
||||
property alias cfg_systemIconSize: systemIconSize.currentIndex
|
||||
|
||||
Kirigami.FormLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Button {
|
||||
id: iconButton
|
||||
|
||||
Kirigami.FormData.label: i18n("Icon:")
|
||||
|
||||
implicitWidth: previewFrame.width + Kirigami.Units.smallSpacing * 2
|
||||
implicitHeight: previewFrame.height + Kirigami.Units.smallSpacing * 2
|
||||
|
||||
// Just to provide some visual feedback when dragging;
|
||||
// cannot have checked without checkable enabled
|
||||
checkable: true
|
||||
checked: dropArea.containsAcceptableDrag
|
||||
|
||||
onPressed: iconMenu.opened ? iconMenu.close() : iconMenu.open()
|
||||
|
||||
DragDrop.DropArea {
|
||||
id: dropArea
|
||||
|
||||
property bool containsAcceptableDrag: false
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onDragEnter: {
|
||||
// Cannot use string operations (e.g. indexOf()) on "url" basic type.
|
||||
var urlString = event.mimeData.url.toString();
|
||||
|
||||
// This list is also hardcoded in KIconDialog.
|
||||
var extensions = [".png", ".xpm", ".svg", ".svgz"];
|
||||
containsAcceptableDrag = urlString.indexOf("file:///") === 0 && extensions.some(function (extension) {
|
||||
return urlString.indexOf(extension) === urlString.length - extension.length; // "endsWith"
|
||||
});
|
||||
|
||||
if (!containsAcceptableDrag) {
|
||||
event.ignore();
|
||||
}
|
||||
}
|
||||
onDragLeave: containsAcceptableDrag = false
|
||||
|
||||
onDrop: {
|
||||
if (containsAcceptableDrag) {
|
||||
// Strip file:// prefix, we already verified in onDragEnter that we have only local URLs.
|
||||
iconDialog.setCustomButtonImage(event.mimeData.url.toString().substr("file://".length));
|
||||
}
|
||||
containsAcceptableDrag = false;
|
||||
}
|
||||
}
|
||||
|
||||
KIconThemes.IconDialog {
|
||||
id: iconDialog
|
||||
|
||||
function setCustomButtonImage(image) {
|
||||
configGeneral.cfg_customButtonImage = image || configGeneral.cfg_icon || "start-here-kde-symbolic"
|
||||
configGeneral.cfg_useCustomButtonImage = true;
|
||||
}
|
||||
|
||||
onIconNameChanged: setCustomButtonImage(iconName);
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: previewFrame
|
||||
anchors.centerIn: parent
|
||||
imagePath: Plasmoid.location === PlasmaCore.Types.Vertical || Plasmoid.location === PlasmaCore.Types.Horizontal
|
||||
? "widgets/panel-background" : "widgets/background"
|
||||
width: Kirigami.Units.iconSizes.large + fixedMargins.left + fixedMargins.right
|
||||
height: Kirigami.Units.iconSizes.large + fixedMargins.top + fixedMargins.bottom
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: width
|
||||
source: configGeneral.cfg_useCustomButtonImage ? configGeneral.cfg_customButtonImage : configGeneral.cfg_icon
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: iconMenu
|
||||
|
||||
// Appear below the button
|
||||
y: +parent.height
|
||||
|
||||
onClosed: iconButton.checked = false;
|
||||
|
||||
MenuItem {
|
||||
text: i18nc("@item:inmenu Open icon chooser dialog", "Choose…")
|
||||
icon.name: "document-open-folder"
|
||||
onClicked: iconDialog.open()
|
||||
}
|
||||
MenuItem {
|
||||
text: i18nc("@item:inmenu Reset icon to default", "Clear Icon")
|
||||
icon.name: "edit-clear"
|
||||
onClicked: {
|
||||
configGeneral.cfg_icon = "start-here-kde-symbolic"
|
||||
configGeneral.cfg_useCustomButtonImage = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: appNameFormat
|
||||
|
||||
Kirigami.FormData.label: i18n("Show applications as:")
|
||||
|
||||
model: [i18n("Name only"), i18n("Description only"), i18n("Name (Description)"), i18n("Description (Name)")]
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: alphaSort
|
||||
visible: false
|
||||
Kirigami.FormData.label: i18n("Behavior:")
|
||||
|
||||
text: i18n("Sort applications alphabetically")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: limitDepth
|
||||
visible: false
|
||||
text: i18n("Flatten sub-menus to a single level")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: showIconsRootLevel
|
||||
visible: false
|
||||
text: i18n("Show icons on the root level of the menu")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: showRecentApps
|
||||
visible: false
|
||||
Kirigami.FormData.label: i18n("Show categories:")
|
||||
|
||||
text: recentOrdering.currentIndex == 0
|
||||
? i18n("Recent applications")
|
||||
: i18n("Often used applications")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: showRecentDocs
|
||||
visible: false
|
||||
text: recentOrdering.currentIndex == 0
|
||||
? i18n("Recent files")
|
||||
: i18n("Often used files")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: recentOrdering
|
||||
visible: false
|
||||
Kirigami.FormData.label: i18n("Sort items in categories by:")
|
||||
model: [i18nc("@item:inlistbox Sort items in categories by [Recently used | Often used]", "Recently used"), i18nc("@item:inlistbox Sort items in categories by [Recently used | Ofetn used]", "Often used")]
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: useExtraRunners
|
||||
visible: false
|
||||
Kirigami.FormData.label: i18n("Search:")
|
||||
text: i18n("Expand search to bookmarks, files and emails")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: alignResultsToBottom
|
||||
visible: false
|
||||
text: i18n("Align search results to bottom")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: appsIconSize
|
||||
Kirigami.FormData.label: i18n("Apps icon size:")
|
||||
model: [i18n("Small"),i18n("Medium"),i18n("Large"), i18n("Huge"),i18n("HUGE"),i18n("Enormous")]
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: favsIconSize
|
||||
Kirigami.FormData.label: i18n("Favs icon size:")
|
||||
model: [i18n("Small"),i18n("Medium"),i18n("Large"), i18n("Huge"),i18n("Enormous")]
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: systemIconSize
|
||||
Kirigami.FormData.label: i18n("Favs icon size:")
|
||||
model: [i18n("Small"),i18n("Medium"),i18n("Large"), i18n("Huge"),i18n("Enormous")]
|
||||
}
|
||||
}
|
||||
}
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import Qt5Compat.GraphicalEffects
|
||||
// Deliberately imported after QtQuick to avoid missing restoreMode property in Binding. Fix in Qt 6.
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.kwindowsystem 1.0
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
import org.kde.plasma.private.shell 2.0
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.plasma.private.kicker 0.1 as Kicker
|
||||
|
||||
import QtQuick.Controls
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
|
||||
import "code/tools.js" as Tools
|
||||
|
||||
|
||||
Kicker.DashboardWindow {
|
||||
id: root
|
||||
|
||||
property bool smallScreen: ((Math.floor(width / Kirigami.Units.iconSizes.huge) <= 22) || (Math.floor(height / Kirigami.Units.iconSizes.huge) <= 14))
|
||||
|
||||
property int iconSize:{ switch(Plasmoid.configuration.appsIconSize){
|
||||
case 0: return Kirigami.Units.iconSizes.smallMedium;
|
||||
case 1: return Kirigami.Units.iconSizes.medium;
|
||||
case 2: return Kirigami.Units.iconSizes.large;
|
||||
case 3: return Kirigami.Units.iconSizes.huge;
|
||||
case 4: return Kirigami.Units.iconSizes.large * 2;
|
||||
case 5: return Kirigami.Units.iconSizes.enormous;
|
||||
default: return 64
|
||||
}
|
||||
}
|
||||
|
||||
property int favsIconSize:{ switch(Plasmoid.configuration.favsIconSize){
|
||||
case 0: return Kirigami.Units.iconSizes.smallMedium;
|
||||
case 1: return Kirigami.Units.iconSizes.medium;
|
||||
case 2: return Kirigami.Units.iconSizes.large;
|
||||
case 3: return Kirigami.Units.iconSizes.huge;
|
||||
case 4: return Kirigami.Units.iconSizes.enormous;
|
||||
default: return 64
|
||||
}
|
||||
}
|
||||
|
||||
property int systemIconSize:{ switch(Plasmoid.configuration.systemIconSize){
|
||||
case 0: return Kirigami.Units.iconSizes.smallMedium;
|
||||
case 1: return Kirigami.Units.iconSizes.medium;
|
||||
case 2: return Kirigami.Units.iconSizes.large;
|
||||
case 3: return Kirigami.Units.iconSizes.huge;
|
||||
case 4: return Kirigami.Units.iconSizes.enormous;
|
||||
default: return 64
|
||||
}
|
||||
}
|
||||
|
||||
property int cellSize: iconSize + (2 * Kirigami.Units.iconSizes.sizeForLabels)
|
||||
+ (2 * Kirigami.Units.largeSpacing)
|
||||
+ (2 * Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
|
||||
highlightItemSvg.margins.left + highlightItemSvg.margins.right))
|
||||
//property int columns: Math.floor(width*0.7/root.cellSize)// Math.floor(((smallScreen ? 85 : 80)/100) * Math.ceil(width / cellSize))
|
||||
property int columns: Math.floor(((smallScreen ? 85 : 80)/100) * Math.ceil(width / cellSize))
|
||||
property bool searching: searchField.text !== ""
|
||||
|
||||
property int favoritesRows: Math.floor(height*0.4/cellSize)
|
||||
|
||||
//keyEventProxy: searchField
|
||||
backgroundColor: "transparent"
|
||||
|
||||
|
||||
onKeyEscapePressed: {
|
||||
if (searching) {
|
||||
searchField.clear();
|
||||
} else {
|
||||
root.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if(visible){
|
||||
animatorMainColumn.start()
|
||||
}else{
|
||||
rootItem.opacity = 0
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
onSearchingChanged: {
|
||||
if (!searching) {
|
||||
//mainView.currentIndex = 1
|
||||
mainView.pop()
|
||||
reset();
|
||||
} else {
|
||||
mainView.push(runnerComponent)
|
||||
//mainView.currentIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
function colorWithAlpha(color: color, alpha: real): color {
|
||||
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function reset() {
|
||||
allAppsGrid.model = rootModel.modelForRow(2)
|
||||
globalFavoritesGrid.model = globalFavorites
|
||||
|
||||
allAppsGrid.currentIndex = -1
|
||||
globalFavoritesGrid.currentIndex = -1;
|
||||
systemFavoritesGrid.currentIndex = -1;
|
||||
|
||||
allAppsGrid.forceLayout()
|
||||
|
||||
searchField.clear();
|
||||
searchField.focus = true
|
||||
|
||||
}
|
||||
|
||||
mainItem: MouseArea {
|
||||
id: rootItem
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
opacity: 0.4
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: kicker
|
||||
|
||||
function onReset() {
|
||||
if (!root.searching) {
|
||||
//filterList.applyFilter();
|
||||
//funnelModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
function onDragSourceChanged() {
|
||||
if (!kicker.dragSource) {
|
||||
// FIXME TODO HACK: Reset all views post-DND to work around
|
||||
// mouse grab bug despite QQuickWindow::mouseGrabberItem==0x0.
|
||||
// Needs a more involved hunt through Qt Quick sources later since
|
||||
// it's not happening with near-identical code in the menu repr.
|
||||
rootModel.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Plasmoid
|
||||
function onUserConfiguringChanged() {
|
||||
if (Plasmoid.userConfiguring) {
|
||||
root.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.Menu {
|
||||
id: contextMenu
|
||||
|
||||
PlasmaExtras.MenuItem {
|
||||
action: Plasmoid.internalAction("configure")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
id: dummyHeading
|
||||
|
||||
visible: false
|
||||
|
||||
width: 0
|
||||
|
||||
level: 1
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: headingMetrics
|
||||
|
||||
font: dummyHeading.font
|
||||
}
|
||||
|
||||
Kicker.FunnelModel {
|
||||
id: funnelModel
|
||||
|
||||
onSourceModelChanged: {
|
||||
if (mainColumn.visible) {
|
||||
mainGrid.currentIndex = -1;
|
||||
mainGrid.forceLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kicker.ContainmentInterface {
|
||||
id: containmentInterface
|
||||
}
|
||||
|
||||
TextField{
|
||||
id: searchField
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: mainView.top
|
||||
anchors.bottomMargin: Kirigami.Units.largeSpacing * 6
|
||||
//focus: true
|
||||
width: Kirigami.Units.gridUnit * 13
|
||||
topPadding: Kirigami.Units.largeSpacing
|
||||
bottomPadding: Kirigami.Units.largeSpacing
|
||||
placeholderText: i18nc("@info:placeholder as in, 'start typing to initiate a search'", "Search")
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
wrapMode: Text.NoWrap
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 0.5
|
||||
|
||||
onTextChanged: {
|
||||
runnerModel.query = searchField.text;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
text = "";
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: colorWithAlpha(Kirigami.Theme.backgroundColor,0.7)
|
||||
radius: 100
|
||||
border.width: 1
|
||||
border.color: colorWithAlpha(Kirigami.Theme.textColor,0.05)
|
||||
}
|
||||
|
||||
function appendText(newText) {
|
||||
if (!root.visible) {
|
||||
return;
|
||||
}
|
||||
focus = true;
|
||||
text = text + newText;
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
if (!root.visible) {
|
||||
return;
|
||||
}
|
||||
focus = true;
|
||||
text = text.slice(0, -1);
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
if (!searchField.selectedText) {
|
||||
return;
|
||||
}
|
||||
|
||||
var delta = text.lastIndexOf(searchField.text, text.length - 2);
|
||||
searchHeading.select(searchField.selectionStart + delta, searchField.selectionEnd + delta);
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
|
||||
event.accepted = true;
|
||||
if(root.searching){
|
||||
mainView.currentItem.tryActivate(0,0)
|
||||
mainView.currentItem.forceActiveFocus()
|
||||
}
|
||||
else{
|
||||
allAppsGrid.tryActivate(0,0)
|
||||
allAppsGrid.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OpacityAnimator{ id: animatorMainColumn ;from: 0; to: 1 ; target: rootItem;}
|
||||
|
||||
StackView {
|
||||
id: mainView
|
||||
width: (root.columns * root.cellSize) + Kirigami.Units.gridUnit
|
||||
height: Math.ceil(root.height*0.7/root.cellSize) * root.cellSize
|
||||
anchors{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
initialItem: Column {
|
||||
id: allAppsColumn
|
||||
clip: true
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
ItemGridView {
|
||||
id: allAppsGrid
|
||||
width: parent.width
|
||||
height: Math.ceil(root.height*0.7/cellHeight) * cellHeight
|
||||
cellWidth: root.cellSize
|
||||
cellHeight: root.cellSize
|
||||
iconSize: root.iconSize
|
||||
dropEnabled: false
|
||||
|
||||
onKeyNavDown: {
|
||||
allAppsGrid.focus = false
|
||||
globalFavoritesGrid.tryActivate(0,0)
|
||||
globalFavoritesGrid.forceActiveFocus()
|
||||
}
|
||||
onKeyNavUp: {
|
||||
allAppsGrid.focus = false
|
||||
searchField.focus = true
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
event.accepted = true;
|
||||
allAppsGrid.focus = false
|
||||
globalFavoritesGrid.tryActivate(0,0)
|
||||
globalFavoritesGrid.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pushEnter: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to:1
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to:0
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
popEnter: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to:1
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
popExit: Transition {
|
||||
PropertyAnimation {
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to:0
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: runnerComponent
|
||||
|
||||
ItemGridView {
|
||||
id: runnerGrid
|
||||
anchors.horizontalCenter: mainView.horizontalCenter
|
||||
width: mainView.width/2
|
||||
clip: true
|
||||
height: mainView.height
|
||||
grabFocus: true
|
||||
cellWidth: root.cellSize
|
||||
cellHeight: root.cellSize
|
||||
iconSize: root.iconSize
|
||||
model: runnerModel.count > 0 ? runnerModel.modelForRow(0) : undefined
|
||||
onKeyNavDown: {
|
||||
runnerGrid.focus = false
|
||||
globalFavoritesGrid.tryActivate(0,0)
|
||||
globalFavoritesGrid.forceActiveFocus()
|
||||
}
|
||||
onKeyNavUp: {
|
||||
runnerGrid.focus = false
|
||||
searchField.focus = true
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
event.accepted = true;
|
||||
runnerGrid.focus = false
|
||||
globalFavoritesGrid.tryActivate(0,0)
|
||||
globalFavoritesGrid.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
anchors.centerIn: globalFavoritesGrid
|
||||
height: globalFavoritesGrid.height + Kirigami.Units.largeSpacing
|
||||
width: globalFavoritesGrid.width + Kirigami.Units.largeSpacing
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: 10
|
||||
opacity: 0.6
|
||||
z:1
|
||||
}
|
||||
|
||||
ItemGridView {
|
||||
id: globalFavoritesGrid
|
||||
width: globalFavoritesGrid.model ? Math.min(Math.floor((root.width*0.85)/cellWidth)*cellWidth,globalFavoritesGrid.model.count*cellWidth) : 0
|
||||
|
||||
iconSize: root.favsIconSize
|
||||
cellHeight: iconSize + Kirigami.Units.largeSpacing
|
||||
cellWidth: cellHeight + Kirigami.Units.largeSpacing
|
||||
clip: true
|
||||
z:2
|
||||
verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOff
|
||||
height: cellHeight
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Kirigami.Units.largeSpacing
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
showLabels: false
|
||||
dropEnabled: true
|
||||
dragEnabled: true
|
||||
|
||||
onKeyNavUp: {
|
||||
if(root.searching){
|
||||
globalFavoritesGrid.focus = false
|
||||
//runnerGrid.tryActivate(0,0)
|
||||
//runnerGrid.forceActiveFocus()
|
||||
mainView.currentItem.tryActivate(0,0)
|
||||
mainView.currentItem.forceActiveFocus()
|
||||
}
|
||||
else{
|
||||
globalFavoritesGrid.focus = false
|
||||
allAppsGrid.tryActivate(0,0)
|
||||
allAppsGrid.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
event.accepted = true;
|
||||
globalFavoritesGrid.focus = false
|
||||
systemFavoritesGrid.tryActivate(0,0)
|
||||
systemFavoritesGrid.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ItemGridView {
|
||||
id: systemFavoritesGrid
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
clip: true
|
||||
width: cellWidth
|
||||
height: systemFavoritesGrid.model ? systemFavoritesGrid.model.count * cellHeight : 0
|
||||
cellWidth: iconSize + Kirigami.Units.largeSpacing * 2
|
||||
cellHeight: cellWidth
|
||||
iconSize: root.systemIconSize
|
||||
z:5
|
||||
showLabels: false
|
||||
model: systemFavorites
|
||||
dragEnabled: false
|
||||
dropEnabled: false
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
event.accepted = true;
|
||||
systemFavoritesGrid.focus = false
|
||||
searchField.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
contextMenu.open(mouse.x, mouse.y);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
root.toggle();
|
||||
}
|
||||
}
|
||||
Keys.onPressed: (event)=> {
|
||||
if(event.modifiers & Qt.ControlModifier ||event.modifiers & Qt.ShiftModifier){
|
||||
searchField.focus = true;
|
||||
return
|
||||
}
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
if (root.searching) {
|
||||
reset();
|
||||
} else {
|
||||
root.visible = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (searchField.focus) {
|
||||
return;
|
||||
}
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
searchField.backspace();
|
||||
} else if (event.text !== "") {
|
||||
event.accepted = true;
|
||||
searchField.appendText(event.text);
|
||||
}
|
||||
//searchField.focus = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
rootModel.refresh();
|
||||
searchField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import "code/tools.js" as Tools
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
width: GridView.view.cellWidth
|
||||
height: GridView.view.cellHeight
|
||||
|
||||
enabled: !model.disabled
|
||||
|
||||
property bool showLabel: true
|
||||
|
||||
property int itemIndex: model.index
|
||||
property string favoriteId: model.favoriteId !== undefined ? model.favoriteId : ""
|
||||
property url url: model.url !== undefined ? model.url : ""
|
||||
property variant icon: model.decoration !== undefined ? model.decoration : ""
|
||||
property var m: model
|
||||
property bool hasActionList: ((model.favoriteId !== null)
|
||||
|| (("hasActionList" in model) && (model.hasActionList === true)))
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: model.display
|
||||
|
||||
function openActionMenu(x, y) {
|
||||
var actionList = hasActionList ? model.actionList : [];
|
||||
Tools.fillActionMenu(i18n, actionMenu, actionList, GridView.view.model.favoritesModel, model.favoriteId);
|
||||
actionMenu.visualParent = item;
|
||||
actionMenu.open(x, y);
|
||||
}
|
||||
|
||||
function actionTriggered(actionId, actionArgument) {
|
||||
var close = (Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument) === true);
|
||||
|
||||
if (close) {
|
||||
root.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
|
||||
y: item.showLabel ? (2 * highlightItemSvg.margins.top) : null
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: item.showLabel ? undefined : parent.verticalCenter
|
||||
|
||||
width: iconSize
|
||||
height: width
|
||||
|
||||
animated: false
|
||||
|
||||
source: model.decoration
|
||||
}
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
id: label
|
||||
|
||||
visible: item.showLabel
|
||||
|
||||
anchors {
|
||||
top: icon.bottom
|
||||
topMargin: Kirigami.Units.smallSpacing
|
||||
left: parent.left
|
||||
leftMargin: highlightItemSvg.margins.left
|
||||
right: parent.right
|
||||
rightMargin: highlightItemSvg.margins.right
|
||||
}
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideMiddle
|
||||
wrapMode: Text.Wrap
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 0.5
|
||||
|
||||
text: ("name" in model ? model.name : model.display)
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
id: toolTip
|
||||
|
||||
property string text: model.display
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
//active: root.visible &&
|
||||
active: root.visible && ( !item.showLabel || label.truncated)
|
||||
mainItem: toolTipDelegate
|
||||
onContainsMouseChanged: item.GridView.view.itemContainsMouseChanged(containsMouse)
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Menu && hasActionList) {
|
||||
event.accepted = true;
|
||||
openActionMenu(item);
|
||||
} else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
event.accepted = true;
|
||||
|
||||
if ("trigger" in GridView.view.model) {
|
||||
GridView.view.model.trigger(index, "", null);
|
||||
root.toggle();
|
||||
}
|
||||
|
||||
itemGrid.itemActivated(index, "", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
FocusScope {
|
||||
id: itemGrid
|
||||
|
||||
signal keyNavLeft
|
||||
signal keyNavRight
|
||||
signal keyNavUp
|
||||
signal keyNavDown
|
||||
|
||||
signal itemActivated(int index, string actionId, string argument)
|
||||
|
||||
property bool dragEnabled: true
|
||||
property bool dropEnabled: false
|
||||
property bool showLabels: true
|
||||
property bool grabFocus: false
|
||||
property alias currentIndex: gridView.currentIndex
|
||||
property alias currentItem: gridView.currentItem
|
||||
property alias contentItem: gridView.contentItem
|
||||
property alias count: gridView.count
|
||||
property alias model: gridView.model
|
||||
property alias verticalLayoutDirection : gridView.verticalLayoutDirection
|
||||
|
||||
property alias cellWidth: gridView.cellWidth
|
||||
property alias cellHeight: gridView.cellHeight
|
||||
property alias iconSize: gridView.iconSize
|
||||
|
||||
property var horizontalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOff
|
||||
property var verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AsNeeded
|
||||
|
||||
onDropEnabledChanged: {
|
||||
if (!dropEnabled && "dropPlaceHolderIndex" in model) {
|
||||
model.dropPlaceHolderIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
// if (!focus && !root.keyEventProxy.activeFocus) {
|
||||
if (!focus) {
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function currentRow() {
|
||||
if (currentIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Math.floor(currentIndex / Math.floor(width / itemGrid.cellWidth));
|
||||
}
|
||||
|
||||
function currentCol() {
|
||||
if (currentIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return currentIndex - (currentRow() * Math.floor(width / itemGrid.cellWidth));
|
||||
}
|
||||
|
||||
function lastRow() {
|
||||
var columns = Math.floor(width / itemGrid.cellWidth);
|
||||
return Math.ceil(count / columns) - 1;
|
||||
}
|
||||
|
||||
function tryActivate(row, col) {
|
||||
if (count) {
|
||||
var columns = Math.floor(width / itemGrid.cellWidth);
|
||||
var rows = Math.ceil(count / columns);
|
||||
row = Math.min(row, rows - 1);
|
||||
col = Math.min(col, columns - 1);
|
||||
currentIndex = Math.min(row ? ((Math.max(1, row) * columns) + col)
|
||||
: col,
|
||||
count - 1);
|
||||
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
function forceLayout() {
|
||||
gridView.forceLayout();
|
||||
}
|
||||
|
||||
ActionMenu {
|
||||
id: actionMenu
|
||||
|
||||
onActionClicked: {
|
||||
visualParent.actionTriggered(actionId, actionArgument);
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: dropArea
|
||||
|
||||
width: itemGrid.width
|
||||
height: itemGrid.height
|
||||
|
||||
onPositionChanged: event => {
|
||||
if (!itemGrid.dropEnabled || gridView.animating || !kicker.dragSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
var x = Math.max(0, event.x - (width % itemGrid.cellWidth));
|
||||
var cPos = mapToItem(gridView.contentItem, x, event.y);
|
||||
var item = gridView.itemAt(cPos.x, cPos.y);
|
||||
|
||||
if (item) {
|
||||
if (kicker.dragSource.parent === gridView.contentItem) {
|
||||
if (item !== kicker.dragSource) {
|
||||
item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex);
|
||||
}
|
||||
} else if (kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
|
||||
&& !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
|
||||
var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
|
||||
|
||||
itemGrid.model.dropPlaceholderIndex = item.itemIndex;
|
||||
|
||||
if (!hasPlaceholder) {
|
||||
gridView.currentIndex = (item.itemIndex - 1);
|
||||
}
|
||||
}
|
||||
} else if (kicker.dragSource.parent !== gridView.contentItem
|
||||
&& kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
|
||||
&& !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
|
||||
var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
|
||||
|
||||
itemGrid.model.dropPlaceholderIndex = hasPlaceholder ? itemGrid.model.count - 1 : itemGrid.model.count;
|
||||
|
||||
if (!hasPlaceholder) {
|
||||
gridView.currentIndex = (itemGrid.model.count - 1);
|
||||
}
|
||||
} else {
|
||||
itemGrid.model.dropPlaceholderIndex = -1;
|
||||
gridView.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if ("dropPlaceholderIndex" in itemGrid.model) {
|
||||
itemGrid.model.dropPlaceholderIndex = -1;
|
||||
gridView.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onDropped: {
|
||||
if (kicker.dragSource && kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model) {
|
||||
itemGrid.model.addFavorite(kicker.dragSource.favoriteId, itemGrid.model.dropPlaceholderIndex);
|
||||
gridView.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: resetAnimationDurationTimer
|
||||
|
||||
interval: 120
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
gridView.animationDuration = interval - 20;
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
width: itemGrid.width
|
||||
height: itemGrid.height
|
||||
|
||||
signal itemContainsMouseChanged(bool containsMouse)
|
||||
|
||||
property int iconSize: Kirigami.Units.iconSizes.huge
|
||||
|
||||
property bool animating: false
|
||||
property int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0
|
||||
|
||||
focus: true
|
||||
|
||||
currentIndex: -1
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
snapMode: ScrollBar.SnapAlways
|
||||
}
|
||||
|
||||
snapMode: GridView.SnapOneRow
|
||||
move: Transition {
|
||||
enabled: itemGrid.dropEnabled
|
||||
|
||||
SequentialAnimation {
|
||||
PropertyAction { target: gridView; property: "animating"; value: true }
|
||||
|
||||
NumberAnimation {
|
||||
duration: gridView.animationDuration
|
||||
properties: "x, y"
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
|
||||
PropertyAction { target: gridView; property: "animating"; value: false }
|
||||
}
|
||||
}
|
||||
|
||||
moveDisplaced: Transition {
|
||||
enabled: itemGrid.dropEnabled
|
||||
|
||||
SequentialAnimation {
|
||||
PropertyAction { target: gridView; property: "animating"; value: true }
|
||||
|
||||
NumberAnimation {
|
||||
duration: gridView.animationDuration
|
||||
properties: "x, y"
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
|
||||
PropertyAction { target: gridView; property: "animating"; value: false }
|
||||
}
|
||||
}
|
||||
|
||||
keyNavigationWraps: false
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
delegate: ItemGridDelegate {
|
||||
showLabel: itemGrid.showLabels
|
||||
}
|
||||
|
||||
highlight: Rectangle { color: colorWithAlpha(Kirigami.Theme.highlightColor,0.65); radius: 10 }
|
||||
|
||||
/*
|
||||
highlight: Item {
|
||||
property bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndex
|
||||
|
||||
PlasmaExtras.Highlight {
|
||||
visible: gridView.currentItem && !isDropPlaceHolder
|
||||
hovered: true
|
||||
pressed: hoverArea.pressed
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
visible: gridView.currentItem && isDropPlaceHolder
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
imagePath: "widgets/viewitem"
|
||||
prefix: "selected"
|
||||
|
||||
opacity: 0.5
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: parent.margins.right
|
||||
bottom: parent.bottom
|
||||
bottomMargin: parent.margins.bottom
|
||||
}
|
||||
|
||||
width: Kirigami.Units.iconSizes.smallMedium
|
||||
height: width
|
||||
|
||||
source: "list-add"
|
||||
active: false
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
highlightFollowsCurrentItem: true
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex !== -1) {
|
||||
hoverArea.hoverEnabled = false
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
animationDuration = 0;
|
||||
resetAnimationDurationTimer.start();
|
||||
if (count > 0 && itemGrid.grabFocus) {
|
||||
currentIndex = 0;
|
||||
focus = true;
|
||||
forceActiveFocus()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onModelChanged: {
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: event => {
|
||||
if (itemGrid.currentCol() !== 0) {
|
||||
event.accepted = true;
|
||||
moveCurrentIndexLeft();
|
||||
} else {
|
||||
itemGrid.keyNavLeft();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onRightPressed: event => {
|
||||
var columns = Math.floor(width / cellWidth);
|
||||
|
||||
if (itemGrid.currentCol() !== columns - 1 && currentIndex !== count -1) {
|
||||
event.accepted = true;
|
||||
moveCurrentIndexRight();
|
||||
} else {
|
||||
itemGrid.keyNavRight();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onUpPressed: event => {
|
||||
if (itemGrid.currentRow() !== 0) {
|
||||
event.accepted = true;
|
||||
moveCurrentIndexUp();
|
||||
positionViewAtIndex(currentIndex, GridView.Contain);
|
||||
} else {
|
||||
itemGrid.keyNavUp();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onDownPressed: event => {
|
||||
if (itemGrid.currentRow() < itemGrid.lastRow()) {
|
||||
// Fix moveCurrentIndexDown()'s lack of proper spatial nav down
|
||||
// into partial columns.
|
||||
event.accepted = true;
|
||||
var columns = Math.floor(itemGrid.width / cellWidth);
|
||||
var newIndex = currentIndex + columns;
|
||||
currentIndex = Math.min(newIndex, gridView.count - 1);
|
||||
positionViewAtIndex(currentIndex, GridView.Visible);
|
||||
} else {
|
||||
itemGrid.keyNavDown();
|
||||
}
|
||||
}
|
||||
|
||||
onItemContainsMouseChanged: containsMouse => {
|
||||
if (!containsMouse) {
|
||||
//if (!actionMenu.opened) {
|
||||
// gridView.currentIndex = -1;
|
||||
//}
|
||||
hoverArea.pressX = -1;
|
||||
hoverArea.pressY = -1;
|
||||
hoverArea.lastX = -1;
|
||||
hoverArea.lastY = -1;
|
||||
hoverArea.pressedItem = null;
|
||||
hoverArea.hoverEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//<>}
|
||||
|
||||
MouseArea {
|
||||
id: hoverArea
|
||||
|
||||
width: itemGrid.width - Kirigami.Units.gridUnit
|
||||
height: itemGrid.height
|
||||
//anchors.fill: parent
|
||||
|
||||
property int pressX: -1
|
||||
property int pressY: -1
|
||||
property int lastX: -1
|
||||
property int lastY: -1
|
||||
property Item pressedItem: null
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
function updatePositionProperties(x, y) {
|
||||
// Prevent hover event synthesis in QQuickWindow interfering
|
||||
// with keyboard navigation by ignoring repeated events with
|
||||
// identical coordinates. As the work done here would be re-
|
||||
// dundant in any case, these are safe to ignore.
|
||||
if (lastX === x && lastY === y) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
|
||||
var cPos = mapToItem(gridView.contentItem, x, y);
|
||||
var item = gridView.itemAt(cPos.x, cPos.y);
|
||||
|
||||
if (!item) {
|
||||
gridView.currentIndex = -1;
|
||||
pressedItem = null;
|
||||
} else {
|
||||
itemGrid.focus = (item.itemIndex !== -1)
|
||||
itemGrid.forceActiveFocus() //<>
|
||||
gridView.currentIndex = item.itemIndex;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
mouse.accepted = true;
|
||||
|
||||
updatePositionProperties(mouse.x, mouse.y);
|
||||
|
||||
pressX = mouse.x;
|
||||
pressY = mouse.y;
|
||||
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
if (gridView.currentItem) {
|
||||
if (gridView.currentItem.hasActionList) {
|
||||
var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y);
|
||||
gridView.currentItem.openActionMenu(mapped.x, mapped.y);
|
||||
}
|
||||
} else {
|
||||
var mapped = mapToItem(rootItem, mouse.x, mouse.y);
|
||||
contextMenu.open(mapped.x, mapped.y);
|
||||
}
|
||||
} else {
|
||||
pressedItem = gridView.currentItem;
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
mouse.accepted = true;
|
||||
updatePositionProperties(mouse.x, mouse.y);
|
||||
|
||||
if (!dragHelper.dragging) {
|
||||
if (pressedItem) {
|
||||
if ("trigger" in gridView.model) {
|
||||
gridView.model.trigger(pressedItem.itemIndex, "", null);
|
||||
root.toggle();
|
||||
}
|
||||
|
||||
itemGrid.itemActivated(pressedItem.itemIndex, "", null);
|
||||
} else if (mouse.button === Qt.LeftButton) {
|
||||
root.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
pressX = pressY = -1;
|
||||
pressedItem = null;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
var item = pressedItem? pressedItem : updatePositionProperties(mouse.x, mouse.y);
|
||||
|
||||
if (gridView.currentIndex !== -1) {
|
||||
if (itemGrid.dragEnabled && pressX !== -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
|
||||
if ("pluginName" in item.m) {
|
||||
dragHelper.startDrag(kicker, item.url, item.icon,
|
||||
"text/x-plasmoidservicename", item.m.pluginName);
|
||||
} else {
|
||||
//dragHelper.startDrag(kicker, item.url, item.icon);
|
||||
dragHelper.startDrag(kicker, item.url);
|
||||
}
|
||||
|
||||
kicker.dragSource = item;
|
||||
|
||||
pressX = -1;
|
||||
pressY = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import "code/tools.js" as Tools
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
width: ListView.view.width
|
||||
height: 40
|
||||
|
||||
enabled: !model.disabled
|
||||
|
||||
property bool showLabel: true
|
||||
|
||||
property int itemIndex: model.index
|
||||
property string favoriteId: model.favoriteId !== undefined ? model.favoriteId : ""
|
||||
property url url: model.url !== undefined ? model.url : ""
|
||||
property variant icon: model.decoration !== undefined ? model.decoration : ""
|
||||
property var m: model
|
||||
property bool hasActionList: ((model.favoriteId !== null)
|
||||
|| (("hasActionList" in model) && (model.hasActionList === true)))
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: model.display
|
||||
|
||||
function openActionMenu(x, y) {
|
||||
var actionList = hasActionList ? model.actionList : [];
|
||||
Tools.fillActionMenu(i18n, actionMenu, actionList, GridView.view.model.favoritesModel, model.favoriteId);
|
||||
actionMenu.visualParent = item;
|
||||
actionMenu.open(x, y);
|
||||
}
|
||||
|
||||
function actionTriggered(actionId, actionArgument) {
|
||||
var close = (Tools.triggerAction(GridView.view.model, model.index, actionId, actionArgument) === true);
|
||||
|
||||
if (close) {
|
||||
root.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
|
||||
//y: item.showLabel ? (2 * highlightItemSvg.margins.top) : undefined
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Kirigami.Units.largeSpacing
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
width: iconSize
|
||||
height: width
|
||||
|
||||
animated: false
|
||||
|
||||
source: model.decoration
|
||||
}
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
id: label
|
||||
visible: item.showLabel
|
||||
anchors {
|
||||
left: icon.right
|
||||
leftMargin: Kirigami.Units.largeSpacing
|
||||
right: desc.left
|
||||
rightMargin: Kirigami.Units.largeSpacing
|
||||
verticalCenter: icon.verticalCenter
|
||||
|
||||
}
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideMiddle
|
||||
//wrapMode: Text.Wrap
|
||||
color: Kirigami.Theme.textColor
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||
text: ("name" in model ? model.name : model.display)
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
id: desc
|
||||
anchors {
|
||||
//top: itemColumns == 2 ? label.bottom: undefined
|
||||
//left: itemColumns == 2 ? icon.right : undefined
|
||||
leftMargin: Kirigami.Units.largeSpacing
|
||||
right: parent.right
|
||||
rightMargin: Kirigami.Units.largeSpacing
|
||||
verticalCenter: icon.verticalCenter
|
||||
}
|
||||
horizontalAlignment: Text.AlignRight
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideMiddle
|
||||
//wrapMode: Text.Wrap
|
||||
|
||||
color: colorWithAlpha(Kirigami.Theme.textColor,0.4)
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize - 1
|
||||
text: ("description" in model ? model.description : "")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
id: toolTip
|
||||
|
||||
property string text: model.display
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.visible && label.truncated
|
||||
mainItem: toolTipDelegate
|
||||
|
||||
//<>onContainsMouseChanged: item.GridView.view.itemContainsMouseChanged(containsMouse)
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Menu && hasActionList) {
|
||||
event.accepted = true;
|
||||
openActionMenu(item);
|
||||
} else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
event.accepted = true;
|
||||
|
||||
if ("trigger" in GridView.view.model) {
|
||||
GridView.view.model.trigger(index, "", null);
|
||||
root.toggle();
|
||||
}
|
||||
|
||||
itemGrid.itemActivated(index, "", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013-2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import org.kde.plasma.private.kicker 0.1 as Kicker
|
||||
|
||||
Kicker.SubMenu {
|
||||
id: itemDialog
|
||||
|
||||
property alias focusParent: itemListView.focusParent
|
||||
property alias model: funnelModel.sourceModel
|
||||
|
||||
property bool aboutToBeDestroyed: false
|
||||
|
||||
visible: false
|
||||
hideOnWindowDeactivate: kicker.hideOnWindowDeactivate
|
||||
location: PlasmaCore.Types.Floating
|
||||
offset: Kirigami.Units.smallSpacing
|
||||
|
||||
onWindowDeactivated: {
|
||||
if (!aboutToBeDestroyed) {
|
||||
kicker.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
mainItem: ItemListView {
|
||||
id: itemListView
|
||||
|
||||
height: {
|
||||
const m = funnelModel.sourceModel;
|
||||
|
||||
if (m === null || m === undefined) {
|
||||
// TODO: setting height to 0 triggers a warning in PlasmaQuick::Dialog
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.min(
|
||||
// either fit in screen boundaries (cut to the nearest item/separator boundary), ...
|
||||
__subFloorMultipleOf(
|
||||
itemDialog.availableScreenRectForItem(itemListView).height
|
||||
- itemDialog.margins.top
|
||||
- itemDialog.margins.bottom,
|
||||
itemHeight
|
||||
) + m.separatorCount * (separatorHeight - itemHeight)
|
||||
,
|
||||
// ...or fit the content itself -- whichever is shorter.
|
||||
((m.count - m.separatorCount) * itemHeight)
|
||||
+ (m.separatorCount * separatorHeight)
|
||||
);
|
||||
}
|
||||
|
||||
// get x rounded down to the multiple of y, minus extra y.
|
||||
function __subFloorMultipleOf(x : real, y : real) : real {
|
||||
return (Math.floor(x / y) - 1) * y;
|
||||
}
|
||||
|
||||
iconsEnabled: true
|
||||
|
||||
dialog: itemDialog
|
||||
|
||||
model: funnelModel
|
||||
|
||||
Kicker.FunnelModel {
|
||||
id: funnelModel
|
||||
|
||||
property bool sorted: sourceModel?.sorted ?? false
|
||||
|
||||
Component.onCompleted: {
|
||||
kicker.reset.connect(funnelModel.reset);
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
if (sourceModel && count === 0) {
|
||||
itemDialog.delayedDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
onSourceModelChanged: {
|
||||
itemListView.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delayedDestroy() {
|
||||
aboutToBeDestroyed = true;
|
||||
Plasmoid.hideOnWindowDeactivate = false;
|
||||
visible = false;
|
||||
|
||||
Qt.callLater(() => itemDialog.destroy());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013-2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
FocusScope {
|
||||
id: itemList
|
||||
|
||||
property real minimumWidth: Kirigami.Units.gridUnit * 14
|
||||
property real maximumWidth: minimumWidth * 2
|
||||
|
||||
width: minimumWidth
|
||||
height: listView.contentHeight
|
||||
|
||||
signal exited
|
||||
signal keyNavigationAtListEnd
|
||||
signal appendSearchText(string text)
|
||||
|
||||
property Item focusParent: null
|
||||
property QtObject dialog: null
|
||||
property QtObject childDialog: null
|
||||
property bool iconsEnabled: false
|
||||
property int itemHeight: Math.ceil((Math.max(Kirigami.Units.iconSizes.sizeForLabels, Kirigami.Units.iconSizes.small)
|
||||
+ Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
|
||||
listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2
|
||||
property int separatorHeight: model.sorted === true ? 0 : lineSvg.horLineHeight + (2 * Kirigami.Units.smallSpacing)
|
||||
|
||||
property alias currentIndex: listView.currentIndex
|
||||
property alias currentItem: listView.currentItem
|
||||
property alias keyNavigationWraps: listView.keyNavigationWraps
|
||||
property alias showChildDialogs: listView.showChildDialogs
|
||||
property alias model: listView.model
|
||||
property alias count: listView.count
|
||||
property alias containsMouse: listener.containsMouse
|
||||
property alias resetOnExitDelay: resetIndexTimer.interval
|
||||
|
||||
onFocusParentChanged: {
|
||||
appendSearchText.connect(focusParent.appendSearchText);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: dialogSpawnTimer
|
||||
|
||||
property bool focusOnSpawn: false
|
||||
|
||||
interval: 70
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
if (!kicker.expanded || model === undefined || currentIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemList.childDialog != null) {
|
||||
itemList.childDialog.delayedDestroy();
|
||||
}
|
||||
|
||||
// Gets reenabled after the dialog spawn causes a focus-in on the dialog window.
|
||||
kicker.hideOnWindowDeactivate = false;
|
||||
|
||||
itemList.childDialog = itemListDialogComponent.createObject(itemList);
|
||||
itemList.childDialog.focusParent = itemList;
|
||||
itemList.childDialog.visualParent = listView.currentItem;
|
||||
itemList.childDialog.model = model.modelForRow(listView.currentIndex);
|
||||
itemList.childDialog.visible = true;
|
||||
|
||||
windowSystem.forceActive(itemList.childDialog.mainItem);
|
||||
itemList.childDialog.mainItem.focus = true;
|
||||
|
||||
if (focusOnSpawn) {
|
||||
itemList.childDialog.mainItem.showChildDialogs = false;
|
||||
itemList.childDialog.mainItem.currentIndex = 0;
|
||||
itemList.childDialog.mainItem.showChildDialogs = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: resetIndexTimer
|
||||
|
||||
interval: (dialog != null) ? 50 : 150
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
if (focus && (!itemList.childDialog || !itemList.childDialog.mainItem.containsMouse)) {
|
||||
currentIndex = -1;
|
||||
itemList.exited();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseEventListener {
|
||||
id: listener
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onContainsMouseChanged: {
|
||||
listView.eligibleWidth = listView.width;
|
||||
|
||||
if (containsMouse) {
|
||||
resetIndexTimer.stop();
|
||||
itemList.forceActiveFocus();
|
||||
} else if ((!itemList.childDialog || !dialog)
|
||||
&& (!currentItem || !currentItem.menu.opened)) {
|
||||
resetIndexTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
|
||||
focus: true
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
property bool showChildDialogs: true
|
||||
property int eligibleWidth: width
|
||||
|
||||
currentIndex: -1
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
snapMode: ListView.SnapToItem
|
||||
spacing: 0
|
||||
keyNavigationEnabled: false
|
||||
|
||||
delegate: ItemListDelegate {
|
||||
onFullTextWidthChanged: {
|
||||
if (fullTextWidth > itemList.width) itemList.width = Math.min(fullTextWidth, itemList.maximumWidth);
|
||||
}
|
||||
}
|
||||
|
||||
highlight: PlasmaExtras.Highlight {
|
||||
visible: !listView.currentItem || !listView.currentItem.isSeparator
|
||||
pressed: listView.currentItem && listView.currentItem.pressed && !listView.currentItem.hasChildren
|
||||
active: listView.currentItem && listView.currentItem.hovered
|
||||
}
|
||||
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onCountChanged: {
|
||||
if (currentIndex == 0) {
|
||||
currentItem.forceActiveFocus();
|
||||
} else {
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex != -1) {
|
||||
if (itemList.childDialog) {
|
||||
if (currentItem && currentItem.hasChildren) {
|
||||
itemList.childDialog.mainItem.width = itemList.minimumWidth;
|
||||
itemList.childDialog.model = model.modelForRow(currentIndex);
|
||||
itemList.childDialog.visualParent = listView.currentItem;
|
||||
} else {
|
||||
itemList.childDialog.delayedDestroy();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentItem == null || !currentItem.hasChildren || !kicker.expanded) {
|
||||
dialogSpawnTimer.stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (showChildDialogs) {
|
||||
dialogSpawnTimer.focusOnSpawn = false;
|
||||
dialogSpawnTimer.restart();
|
||||
}
|
||||
} else if (itemList.childDialog != null) {
|
||||
itemList.childDialog.delayedDestroy();
|
||||
itemList.childDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentItemChanged: {
|
||||
if (currentItem) {
|
||||
currentItem.menu.closed.connect(resetIndexTimer.restart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Right || event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
if (itemList.childDialog === null && currentItem != null && currentItem.hasChildren) {
|
||||
dialogSpawnTimer.focusOnSpawn = true;
|
||||
dialogSpawnTimer.restart();
|
||||
} else if (currentItem != null && currentItem.hasChildren) {
|
||||
windowSystem.forceActive(itemList.childDialog.mainItem);
|
||||
itemList.childDialog.mainItem.focus = true;
|
||||
itemList.childDialog.mainItem.currentIndex = 0;
|
||||
}
|
||||
} else if (event.key === Qt.Key_Up) {
|
||||
event.accepted = true;
|
||||
|
||||
if (!keyNavigationWraps && currentIndex == 0) {
|
||||
itemList.keyNavigationAtListEnd();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
showChildDialogs = false;
|
||||
listView.decrementCurrentIndex();
|
||||
|
||||
if (currentItem.isSeparator) {
|
||||
listView.decrementCurrentIndex();
|
||||
}
|
||||
listView.currentItem.forceActiveFocus()
|
||||
|
||||
showChildDialogs = true;
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
event.accepted = true;
|
||||
|
||||
if (!keyNavigationWraps && currentIndex == count - 1) {
|
||||
itemList.keyNavigationAtListEnd();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
showChildDialogs = false;
|
||||
listView.incrementCurrentIndex();
|
||||
|
||||
if (currentItem.isSeparator) {
|
||||
listView.incrementCurrentIndex();
|
||||
}
|
||||
listView.currentItem.forceActiveFocus()
|
||||
|
||||
showChildDialogs = true;
|
||||
} else if (event.key === Qt.Key_Left && dialog != null) {
|
||||
dialog.destroy();
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
kicker.expanded = false;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
//do nothing, and skip appending text
|
||||
} else if (event.text !== "") {
|
||||
if (/[\x00-\x1F\x7F]/.test(event.text)) {
|
||||
// We still want to focus it
|
||||
appendSearchText("");
|
||||
} else {
|
||||
appendSearchText(event.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
windowSystem.monitorWindowFocus(itemList);
|
||||
|
||||
if (dialog == null) {
|
||||
appendSearchText.connect(root.appendSearchText);
|
||||
}
|
||||
}
|
||||
}
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import org.kde.plasma.private.kicker 0.1 as Kicker
|
||||
|
||||
PlasmaComponents.ScrollView {
|
||||
id: itemMultiGrid
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
|
||||
implicitHeight: itemColumn.implicitHeight
|
||||
|
||||
signal keyNavLeft(int subGridIndex)
|
||||
signal keyNavRight(int subGridIndex)
|
||||
signal keyNavUp()
|
||||
signal keyNavDown()
|
||||
|
||||
property bool grabFocus: false
|
||||
|
||||
property alias model: repeater.model
|
||||
property alias count: repeater.count
|
||||
property alias flickableItem: flickable
|
||||
|
||||
function subGridAt(index) {
|
||||
return repeater.itemAt(index).itemGrid;
|
||||
}
|
||||
|
||||
function tryActivate(row, col) { // FIXME TODO: Cleanup messy algo.
|
||||
if (flickable.contentY > 0) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
var target = null;
|
||||
var rows = 0;
|
||||
|
||||
for (var i = 0; i < repeater.count; i++) {
|
||||
var grid = subGridAt(i);
|
||||
|
||||
if (rows <= row) {
|
||||
target = grid;
|
||||
rows += grid.lastRow() + 2; // Header counts as one.
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target) {
|
||||
rows -= (target.lastRow() + 2);
|
||||
target.tryActivate(row - rows, col);
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (!focus) {
|
||||
for (var i = 0; i < repeater.count; i++) {
|
||||
subGridAt(i).focus = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: itemColumn.implicitHeight
|
||||
//focusPolicy: Qt.NoFocus
|
||||
|
||||
Column {
|
||||
id: itemColumn
|
||||
|
||||
width: itemMultiGrid.width - Kirigami.Units.gridUnit
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
delegate: Item {
|
||||
width: itemColumn.width - Kirigami.Units.gridUnit
|
||||
height: headerHeight + gridView.height + (index == repeater.count - 1 ? 0 : footerHeight)
|
||||
|
||||
property int headerHeight: (gridViewLabel.height
|
||||
+ gridViewLabelUnderline.height + Kirigami.Units.gridUnit)
|
||||
property int footerHeight: (Math.ceil(headerHeight / cellSize) * cellSize) - headerHeight
|
||||
|
||||
property Item itemGrid: gridView
|
||||
|
||||
Kirigami.Heading {
|
||||
id: gridViewLabel
|
||||
|
||||
anchors.top: parent.top
|
||||
|
||||
x: Kirigami.Units.smallSpacing
|
||||
width: parent.width - x
|
||||
height: dummyHeading.height
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
opacity: 1.0
|
||||
|
||||
color: "white" // FIXME TODO: Respect theming?
|
||||
|
||||
level: 1
|
||||
|
||||
text: repeater.model.modelForRow(index).description
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
KSvg.SvgItem {
|
||||
id: gridViewLabelUnderline
|
||||
|
||||
anchors.top: gridViewLabel.bottom
|
||||
|
||||
width: parent.width - Kirigami.Units.gridUnit
|
||||
height: lineSvg.horLineHeight
|
||||
|
||||
svg: lineSvg
|
||||
elementId: "horizontal-line"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
onClicked: root.toggle()
|
||||
}
|
||||
|
||||
ItemGridView {
|
||||
id: gridView
|
||||
|
||||
anchors {
|
||||
top: gridViewLabelUnderline.bottom
|
||||
topMargin: Kirigami.Units.gridUnit
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: Math.ceil(count / Math.floor(width / cellSize)) * cellSize
|
||||
|
||||
cellWidth: cellSize
|
||||
cellHeight: cellSize
|
||||
iconSize: root.iconSize
|
||||
|
||||
verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOff
|
||||
|
||||
model: repeater.model.modelForRow(index)
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
itemMultiGrid.focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
if (itemMultiGrid.grabFocus && index == 0 && count > 0) {
|
||||
currentIndex = 0;
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentItemChanged: {
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0 && currentRow() === 0) {
|
||||
flickable.contentY = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var y = currentItem.y;
|
||||
y = contentItem.mapToItem(flickable.contentItem, 0, y).y;
|
||||
|
||||
if (y < flickable.contentY) {
|
||||
flickable.contentY = y;
|
||||
} else {
|
||||
y += cellSize;
|
||||
y -= flickable.contentY;
|
||||
y -= itemMultiGrid.height;
|
||||
|
||||
if (y > 0) {
|
||||
flickable.contentY += y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onKeyNavLeft: {
|
||||
itemMultiGrid.keyNavLeft(index);
|
||||
}
|
||||
|
||||
onKeyNavRight: {
|
||||
itemMultiGrid.keyNavRight(index);
|
||||
}
|
||||
|
||||
onKeyNavUp: {
|
||||
if (index > 0) {
|
||||
var prevGrid = subGridAt(index - 1);
|
||||
prevGrid.tryActivate(prevGrid.lastRow(), currentCol());
|
||||
} else {
|
||||
itemMultiGrid.keyNavUp();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyNavDown: {
|
||||
if (index < repeater.count - 1) {
|
||||
subGridAt(index + 1).tryActivate(0, currentCol());
|
||||
} else {
|
||||
itemMultiGrid.keyNavDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Steal wheel events from the nested grid view and forward them to
|
||||
// the ScrollView's internal WheelArea.
|
||||
Kicker.WheelInterceptor {
|
||||
anchors.fill: gridView
|
||||
z: 1
|
||||
|
||||
destination: findWheelArea(itemMultiGrid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013-2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
|
||||
import "code/tools.js" as Tools
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
width: root.width
|
||||
height: root.width
|
||||
|
||||
signal actionTriggered(string actionId, variant actionArgument)
|
||||
signal aboutToShowActionMenu(variant actionMenu)
|
||||
|
||||
property bool hasActionList: ((model.favoriteId !== null)
|
||||
|| (("hasActionList" in model) && (model.hasActionList !== null)))
|
||||
property int itemIndex: model.index
|
||||
|
||||
onAboutToShowActionMenu: actionMenu => {
|
||||
const actionList = (model.hasActionList !== null) ? model.actionList : [];
|
||||
Tools.fillActionMenu(i18n, actionMenu, actionList, repeater.model, model.favoriteId);
|
||||
}
|
||||
|
||||
onActionTriggered: (actionId, actionArgument) => {
|
||||
if (Tools.triggerAction(repeater.model, model.index, actionId, actionArgument) === true) {
|
||||
kicker.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openActionMenu(visualParent, x, y) {
|
||||
aboutToShowActionMenu(actionMenu);
|
||||
actionMenu.visualParent = visualParent;
|
||||
actionMenu.open(x, y);
|
||||
}
|
||||
|
||||
ActionMenu {
|
||||
id: actionMenu
|
||||
|
||||
onActionClicked: {
|
||||
actionTriggered(actionId, actionArgument);
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.fill: parent
|
||||
|
||||
active: toolTip.containsMouse
|
||||
|
||||
source: model.decoration
|
||||
}
|
||||
|
||||
MouseEventListener {
|
||||
id: listener
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: - sideBar.margins.left
|
||||
rightMargin: - sideBar.margins.right
|
||||
}
|
||||
|
||||
enabled: (item.parent && !item.parent.animating)
|
||||
|
||||
property bool pressed: false
|
||||
property int pressX: -1
|
||||
property int pressY: -1
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onPressed: {
|
||||
if (mouse.buttons & Qt.RightButton) {
|
||||
if (item.hasActionList) {
|
||||
item.openActionMenu(item, mouse.x, mouse.y);
|
||||
}
|
||||
} else {
|
||||
pressed = true;
|
||||
pressX = mouse.x;
|
||||
pressY = mouse.y;
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (pressed) {
|
||||
repeater.model.trigger(index, "", null);
|
||||
kicker.expanded = false;
|
||||
}
|
||||
|
||||
pressed = false;
|
||||
pressX = -1;
|
||||
pressY = -1;
|
||||
}
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (!containsMouse) {
|
||||
pressed = false;
|
||||
pressX = -1;
|
||||
pressY = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (pressX !== -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
|
||||
kicker.dragSource = item;
|
||||
dragHelper.startDrag(kicker, model.url, model.icon);
|
||||
pressed = false;
|
||||
pressX = -1;
|
||||
pressY = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaCore.ToolTipArea {
|
||||
id: toolTip
|
||||
|
||||
property string text: model.display
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: - sideBar.margins.left
|
||||
rightMargin: - sideBar.margins.right
|
||||
}
|
||||
|
||||
interactive: false
|
||||
location: (((Plasmoid.location === PlasmaCore.Types.RightEdge)
|
||||
|| (Qt.application.layoutDirection === Qt.RightToLeft))
|
||||
? PlasmaCore.Types.RightEdge : PlasmaCore.Types.LeftEdge)
|
||||
|
||||
mainItem: toolTipDelegate
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013-2014 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
import org.kde.draganddrop 2.0
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
DropArea {
|
||||
id: root
|
||||
|
||||
width: Kirigami.Units.iconSizes.medium
|
||||
height: contentHeight
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
property int contentHeight: model ? (model.count * Kirigami.Units.iconSizes.medium) + ((model.count - 1) * flow.spacing) : 0
|
||||
|
||||
property alias model: repeater.model
|
||||
|
||||
onDragMove: event => {
|
||||
if (flow.animating) {
|
||||
return;
|
||||
}
|
||||
|
||||
var above = flow.childAt(event.x, event.y);
|
||||
|
||||
if (above && above !== kicker.dragSource && dragSource.parent == flow) {
|
||||
repeater.model.moveRow(dragSource.itemIndex, above.itemIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Flow {
|
||||
id: flow
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property bool animating: false
|
||||
property int animationDuration: resetAnimationDurationTimer.interval
|
||||
|
||||
move: Transition {
|
||||
SequentialAnimation {
|
||||
PropertyAction { target: flow; property: "animating"; value: true }
|
||||
|
||||
NumberAnimation {
|
||||
duration: flow.animationDuration
|
||||
properties: "x, y"
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
|
||||
PropertyAction { target: flow; property: "animating"; value: false }
|
||||
}
|
||||
}
|
||||
|
||||
spacing: (2 * Kirigami.Units.smallSpacing)
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
delegate: SideBarItem {}
|
||||
|
||||
onCountChanged: {
|
||||
flow.animationDuration = 0;
|
||||
resetAnimationDurationTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: resetAnimationDurationTimer
|
||||
|
||||
interval: 150
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
flow.animationDuration = interval - 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Aurélien Gâteau <agateau@kde.org>
|
||||
SPDX-FileCopyrightText: 2013-2015 Eike Hein <hein@kde.org>
|
||||
SPDX-FileCopyrightText: 2017 Ivan Cukic <ivan.cukic@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
function fillActionMenu(i18n, actionMenu, actionList, favoriteModel, favoriteId) {
|
||||
// Accessing actionList can be a costly operation, so we don't
|
||||
// access it until we need the menu.
|
||||
|
||||
var actions = createFavoriteActions(i18n, favoriteModel, favoriteId);
|
||||
|
||||
if (actions) {
|
||||
if (actionList && actionList.length > 0) {
|
||||
var actionListCopy = Array.from(actionList);
|
||||
var separator = { "type": "separator" };
|
||||
actionListCopy.push(separator);
|
||||
// actionList = actions.concat(actionList); // this crashes Qt O.o
|
||||
actionListCopy.push.apply(actionListCopy, actions);
|
||||
actionList = actionListCopy;
|
||||
} else {
|
||||
actionList = actions;
|
||||
}
|
||||
}
|
||||
|
||||
actionMenu.actionList = actionList;
|
||||
}
|
||||
|
||||
function createFavoriteActions(i18n, favoriteModel, favoriteId) {
|
||||
if (!favoriteModel || !favoriteModel.enabled || !favoriteId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (favoriteModel.activities === undefined ||
|
||||
favoriteModel.activities.runningActivities.length <= 1) {
|
||||
var action = {};
|
||||
|
||||
if (favoriteModel.isFavorite(favoriteId)) {
|
||||
action.text = i18n("Remove from Favorites");
|
||||
action.icon = "bookmark-remove";
|
||||
action.actionId = "_kicker_favorite_remove";
|
||||
} else if (favoriteModel.maxFavorites === -1 || favoriteModel.count < favoriteModel.maxFavorites) {
|
||||
action.text = i18n("Add to Favorites");
|
||||
action.icon = "bookmark-new";
|
||||
action.actionId = "_kicker_favorite_add";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
action.actionArgument = { favoriteModel: favoriteModel, favoriteId: favoriteId };
|
||||
|
||||
return [action];
|
||||
|
||||
} else {
|
||||
var actions = [];
|
||||
|
||||
var linkedActivities = favoriteModel.linkedActivitiesFor(favoriteId);
|
||||
|
||||
var activities = favoriteModel.activities.runningActivities;
|
||||
|
||||
// Adding the item to link/unlink to all activities
|
||||
|
||||
var linkedToAllActivities =
|
||||
!(linkedActivities.indexOf(":global") === -1);
|
||||
|
||||
actions.push({
|
||||
text : i18n("On All Activities"),
|
||||
checkable : true,
|
||||
|
||||
actionId : linkedToAllActivities ?
|
||||
"_kicker_favorite_remove_from_activity" :
|
||||
"_kicker_favorite_set_to_activity",
|
||||
checked : linkedToAllActivities,
|
||||
|
||||
actionArgument : {
|
||||
favoriteModel: favoriteModel,
|
||||
favoriteId: favoriteId,
|
||||
favoriteActivity: ""
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Adding items for each activity separately
|
||||
|
||||
var addActivityItem = function(activityId, activityName) {
|
||||
var linkedToThisActivity =
|
||||
!(linkedActivities.indexOf(activityId) === -1);
|
||||
|
||||
actions.push({
|
||||
text : activityName,
|
||||
checkable : true,
|
||||
checked : linkedToThisActivity && !linkedToAllActivities,
|
||||
|
||||
actionId :
|
||||
// If we are on all activities, and the user clicks just one
|
||||
// specific activity, unlink from everything else
|
||||
linkedToAllActivities ? "_kicker_favorite_set_to_activity" :
|
||||
|
||||
// If we are linked to the current activity, just unlink from
|
||||
// that single one
|
||||
linkedToThisActivity ? "_kicker_favorite_remove_from_activity" :
|
||||
|
||||
// Otherwise, link to this activity, but do not unlink from
|
||||
// other ones
|
||||
"_kicker_favorite_add_to_activity",
|
||||
|
||||
actionArgument : {
|
||||
favoriteModel : favoriteModel,
|
||||
favoriteId : favoriteId,
|
||||
favoriteActivity : activityId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Adding the item to link/unlink to the current activity
|
||||
|
||||
addActivityItem(favoriteModel.activities.currentActivity, i18n("On the Current Activity"));
|
||||
|
||||
actions.push({
|
||||
type: "separator",
|
||||
actionId: "_kicker_favorite_separator"
|
||||
});
|
||||
|
||||
// Adding the items for each activity
|
||||
|
||||
activities.forEach(function(activityId) {
|
||||
addActivityItem(activityId, favoriteModel.activityNameForId(activityId));
|
||||
});
|
||||
|
||||
return [{
|
||||
text : i18n("Show in Favorites"),
|
||||
icon : "favorite",
|
||||
subActions : actions
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerAction(model, index, actionId, actionArgument) {
|
||||
function startsWith(txt, needle) {
|
||||
return txt.substr(0, needle.length) === needle;
|
||||
}
|
||||
|
||||
if (startsWith(actionId, "_kicker_favorite_")) {
|
||||
handleFavoriteAction(actionId, actionArgument);
|
||||
return;
|
||||
}
|
||||
|
||||
var closeRequested = model.trigger(index, actionId, actionArgument);
|
||||
|
||||
if (closeRequested) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleFavoriteAction(actionId, actionArgument) {
|
||||
var favoriteId = actionArgument.favoriteId;
|
||||
var favoriteModel = actionArgument.favoriteModel;
|
||||
|
||||
if (favoriteModel === null || favoriteId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (actionId === "_kicker_favorite_remove") {
|
||||
favoriteModel.removeFavorite(favoriteId);
|
||||
} else if (actionId === "_kicker_favorite_add") {
|
||||
favoriteModel.addFavorite(favoriteId);
|
||||
} else if (actionId === "_kicker_favorite_remove_from_activity") {
|
||||
favoriteModel.removeFavoriteFrom(favoriteId, actionArgument.favoriteActivity);
|
||||
} else if (actionId === "_kicker_favorite_add_to_activity") {
|
||||
favoriteModel.addFavoriteTo(favoriteId, actionArgument.favoriteActivity);
|
||||
} else if (actionId === "_kicker_favorite_set_to_activity") {
|
||||
favoriteModel.setFavoriteOn(favoriteId, actionArgument.favoriteActivity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014-2015 Eike Hein <hein@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import org.kde.plasma.private.kicker 0.1 as Kicker
|
||||
import org.kde.plasma.plasma5support 2.0 as P5Support
|
||||
|
||||
|
||||
PlasmoidItem {
|
||||
id: kicker
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
signal reset
|
||||
|
||||
property bool isDash: true // Plasmoid.pluginName === "org.kde.plasma.kickerdash"
|
||||
|
||||
switchWidth: isDash || !fullRepresentationItem ? 0 :fullRepresentationItem.Layout.minimumWidth
|
||||
switchHeight: isDash || !fullRepresentationItem ? 0 :fullRepresentationItem.Layout.minimumHeight
|
||||
|
||||
// this is a bit of a hack to prevent Plasma from spawning a dialog on its own when we're Dash
|
||||
preferredRepresentation: isDash ?fullRepresentation : null
|
||||
|
||||
compactRepresentation: isDash ? null : compactRepresentation
|
||||
fullRepresentation: compactRepresentation
|
||||
|
||||
property Component itemListDialogComponent: Qt.createComponent(Qt.resolvedUrl("./ItemListDialog.qml"))
|
||||
property Item dragSource: null
|
||||
|
||||
property QtObject globalFavorites: rootModel.favoritesModel
|
||||
property QtObject systemFavorites: rootModel.systemFavoritesModel
|
||||
|
||||
Plasmoid.icon: Plasmoid.configuration.useCustomButtonImage ? Plasmoid.configuration.customButtonImage : Plasmoid.configuration.icon
|
||||
|
||||
onSystemFavoritesChanged: {
|
||||
systemFavorites.favorites = Plasmoid.configuration.favoriteSystemActions;
|
||||
}
|
||||
|
||||
function action_menuedit() {
|
||||
processRunner.runMenuEditor();
|
||||
}
|
||||
|
||||
function updateSvgMetrics() {
|
||||
lineSvg.horLineHeight = lineSvg.elementSize("horizontal-line").height;
|
||||
lineSvg.vertLineWidth = lineSvg.elementSize("vertical-line").width;
|
||||
}
|
||||
|
||||
Component {
|
||||
id: compactRepresentation
|
||||
CompactRepresentation {}
|
||||
}
|
||||
|
||||
Kicker.RootModel {
|
||||
id: rootModel
|
||||
|
||||
autoPopulate: false
|
||||
|
||||
appNameFormat: Plasmoid.configuration.appNameFormat
|
||||
flat: kicker.isDash || Plasmoid.configuration.limitDepth
|
||||
sorted: Plasmoid.configuration.alphaSort
|
||||
showSeparators: !kicker.isDash
|
||||
// TODO: appletInterface property now can be ported to "applet" and have the real Applet* assigned directly
|
||||
appletInterface: kicker
|
||||
|
||||
showAllApps: kicker.isDash
|
||||
showAllAppsCategorized: false //<> true
|
||||
showTopLevelItems: !kicker.isDash
|
||||
showRecentApps: true //<>Plasmoid.configuration.showRecentApps
|
||||
showRecentDocs: true //<>Plasmoid.configuration.showRecentDocs
|
||||
recentOrdering: Plasmoid.configuration.recentOrdering
|
||||
|
||||
onShowRecentAppsChanged: {
|
||||
Plasmoid.configuration.showRecentApps = showRecentApps;
|
||||
}
|
||||
|
||||
onShowRecentDocsChanged: {
|
||||
Plasmoid.configuration.showRecentDocs = showRecentDocs;
|
||||
}
|
||||
|
||||
onRecentOrderingChanged: {
|
||||
Plasmoid.configuration.recentOrdering = recentOrdering;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
favoritesModel.initForClient("org.kde.plasma.kicker.favorites.instance-" + Plasmoid.id)
|
||||
|
||||
if (!Plasmoid.configuration.favoritesPortedToKAstats) {
|
||||
if (favoritesModel.count < 1) {
|
||||
favoritesModel.portOldFavorites(Plasmoid.configuration.favoriteApps);
|
||||
}
|
||||
Plasmoid.configuration.favoritesPortedToKAstats = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: globalFavorites
|
||||
|
||||
function onFavoritesChanged() {
|
||||
Plasmoid.configuration.favoriteApps = target.favorites;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: systemFavorites
|
||||
|
||||
function onFavoritesChanged() {
|
||||
Plasmoid.configuration.favoriteSystemActions = target.favorites;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Plasmoid.configuration
|
||||
|
||||
function onFavoriteAppsChanged() {
|
||||
globalFavorites.favorites = Plasmoid.configuration.favoriteApps;
|
||||
}
|
||||
|
||||
function onFavoriteSystemActionsChanged() {
|
||||
systemFavorites.favorites = Plasmoid.configuration.favoriteSystemActions;
|
||||
}
|
||||
}
|
||||
|
||||
P5Support.DataSource {
|
||||
id: pmEngine
|
||||
engine: "powermanagement"
|
||||
connectedSources: ["PowerDevil", "Sleep States"]
|
||||
function performOperation(what) {
|
||||
var service = serviceForSource("PowerDevil")
|
||||
var operation = service.operationDescription(what)
|
||||
service.startOperationCall(operation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Kicker.RunnerModel {
|
||||
id: runnerModel
|
||||
|
||||
appletInterface: kicker
|
||||
|
||||
favoritesModel: globalFavorites
|
||||
mergeResults: true
|
||||
|
||||
runners: {
|
||||
const results = ["krunner_services", "krunner_systemsettings"];
|
||||
|
||||
if (kicker.isDash) {
|
||||
results.push("krunner_sessions", "krunner_powerdevil", "calculator", "unitconverter");
|
||||
}
|
||||
|
||||
if (Plasmoid.configuration.useExtraRunners) {
|
||||
results.push(...Plasmoid.configuration.extraRunners);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
Kicker.DragHelper {
|
||||
id: dragHelper
|
||||
|
||||
dragIconSize: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
|
||||
Kicker.ProcessRunner {
|
||||
id: processRunner
|
||||
}
|
||||
|
||||
Kicker.WindowSystem {
|
||||
id: windowSystem
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: highlightItemSvg
|
||||
|
||||
visible: false
|
||||
|
||||
imagePath: "widgets/viewitem"
|
||||
prefix: "hover"
|
||||
}
|
||||
|
||||
KSvg.FrameSvgItem {
|
||||
id: listItemSvg
|
||||
|
||||
visible: false
|
||||
|
||||
imagePath: "widgets/listitem"
|
||||
prefix: "normal"
|
||||
}
|
||||
|
||||
KSvg.Svg {
|
||||
id: lineSvg
|
||||
imagePath: "widgets/line"
|
||||
|
||||
property int horLineHeight
|
||||
property int vertLineWidth
|
||||
}
|
||||
|
||||
PlasmaComponents3.Label {
|
||||
id: toolTipDelegate
|
||||
|
||||
width: contentWidth
|
||||
height: undefined
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize + 0.5
|
||||
|
||||
property Item toolTip
|
||||
|
||||
text: toolTip ? toolTip.text : ""
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: justOpenedTimer
|
||||
|
||||
repeat: false
|
||||
interval: 600
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: kicker
|
||||
|
||||
function onExpandedChanged(expanded) {
|
||||
if (expanded) {
|
||||
windowSystem.monitorWindowVisibility(Plasmoid.fullRepresentationItem);
|
||||
justOpenedTimer.start();
|
||||
} else {
|
||||
kicker.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetDragSource() {
|
||||
dragSource = null;
|
||||
}
|
||||
|
||||
function enableHideOnWindowDeactivate() {
|
||||
kicker.hideOnWindowDeactivate = true;
|
||||
}
|
||||
|
||||
Plasmoid.contextualActions: [
|
||||
PlasmaCore.Action {
|
||||
text: i18n("Edit Applications…")
|
||||
icon.name: "kmenuedit"
|
||||
visible: Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
|
||||
onTriggered: processRunner.runMenuEditor()
|
||||
}
|
||||
]
|
||||
|
||||
Component.onCompleted: {
|
||||
if (Plasmoid.hasOwnProperty("activationTogglesExpanded")) {
|
||||
Plasmoid.activationTogglesExpanded = !kicker.isDash
|
||||
}
|
||||
|
||||
windowSystem.focusIn.connect(enableHideOnWindowDeactivate);
|
||||
kicker.hideOnWindowDeactivate = true;
|
||||
|
||||
updateSvgMetrics();
|
||||
PlasmaCore.Theme.themeChanged.connect(updateSvgMetrics);
|
||||
|
||||
rootModel.refreshed.connect(reset);
|
||||
|
||||
dragHelper.dropped.connect(resetDragSource);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user