Added small taskbar support, fixed some bugs and strange behavior in SevenTasks.

This commit is contained in:
wackyideas 2023-09-23 00:51:26 +02:00
parent 213a5fa60c
commit ef01020632
28 changed files with 4241 additions and 885 deletions

View file

@ -86,7 +86,7 @@ This only applies to regular windows (most windows with server-side decorations)
## Configuration menu <a name="config"></a>
The accent color can be edited in real time through the configuration menu. Internally, the color is stored in the RGB color model as that's what OpenGL expects during rendering. The color mixer window is designed to look and function like Windows 7's Personalization menu, and includes the accent colors found on Windows 7, which were directly pulled from the following registry key:
The accent color can be edited in real time through the configuration menu. Real time editing was made possible through the use of [QSharedMemory](https://doc.qt.io/qt-6/qsharedmemory.html) and calling the ```reconfigure()``` method through qdbus whenever a change occurs. Internally, the color is stored in the RGB color model as that's what OpenGL expects during rendering. The color mixer window is designed to look and function like Windows 7's Personalization menu, and includes the accent colors found on Windows 7, which were directly pulled from the following registry key:
```[ HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor ]```

View file

@ -26,6 +26,21 @@ The first notable thing about SevenStart is the use of animated icons or "orbs"
In earlier versions of SevenStart, only two images were used, but now three images are used due to upstream updates causing subtle timing issues that would cause undesirable visual effects. Using three images did end up allowing SevenStart to replicate the effects of Vista and 7's Start menu more accurately. By default, SevenStart uses internal icons if no external icons are provided in the configuration window.
Users can also force the orb to have a constant size, which allows the orb to stick out of the panel if the panel's height is lower than the orb's height. This allows the user to have a small panel with the orb protruding out of the panel, much like Windows 7 and Vista. The implementation is very similar to the user icon sticking out of the menu, which is described in more detail below. This implementation introduces a couple of problems:
1. Right clicking on this dialog window won't open the standard context menu as expected, as it actually isn't part of the compact or full representation.
2. When compositing is disabled, even with the NoBackground hint, this dialog window will simply be drawn with an opaque, black background.
The first problem is solved by implementing a separate context menu that pulls the appropriate actions from [ContainmentInterface](https://api.kde.org/frameworks/plasma-framework/html/classContainmentInterface.html) and [AppletInterface](https://api.kde.org/frameworks/plasma-framework/html/classAppletInterface.html), which is actually the ```plasmoid``` object.
Potential solutions to the second problem are far less trivial, as all of them require some sort of compromise. The potential solutions are:
1. Reverting to the regular orb while compositing is disabled, which is embedded within the panel, and/or resizing the orb back to its unaltered scale. This solution is aesthetically not pleasing at all, and it pretty much defeats the purpose of even enabling this feature to begin with.
2. Adding a compiled component to this plasmoid, which would expose more Qt and KDE API methods that can define an opacity mask around the orb. The downside to this solution is that a compiled component reduces portability and makes the installation process slightly more complicated. To see how such an implementation would work however, see this [link](https://github.com/ryanmcalister/donutwindow).
3. Applying an opacity mask through an already existing SVG file which can be applied to the dialog window. The downside to this solution is that it feels pretty much like a hack/workaround, and because it uses the provided frameworks in an unintended way.
This implementation goes for the third solution, which takes advantage of the solid appearance variant found in Plasma themes. The dialog window loads in a completely transparent tooltip SVG that has a customized opacity mask that is created based on the orb texture. This does mean that if the user wants to have a different kind of orb texture that's not spherical in shape, they would also need to provide a correct SVG that represents the opacity mask of the orb. See the source code for more implementation details.
Another notable thing about the compact representation is that it is used in an unusual way compared to how plasmoids are generally designed to behave. Plasmoids have two representations:
1. Compact representation, which is used when the plasmoid is in a panel.
@ -88,3 +103,6 @@ Files:
|OftenUsedView.qml|Used to display recently opened programs.|
|SidePanelItemDelegate.qml|Displays and holds information about the sidebar menu entries.|
|ApplicationsView.qml|Used to display all applications installed on the system.|
|StartOrb.qml|Dialog window representing the orb that is used for the small taskbar layout.|
|FloatingOrb.qml|The actual orb button that handles the visual animations and functionality.|
|ContextMenu.qml|Reimplemented context menu for StartOrb to bypass Dialog limitations.|

View file

@ -34,6 +34,8 @@ Regular textures are used for all other windows (plasmoids, OSD popups, task swi
The opaque appearance state aims to be a recreation of Aero Basic (WIP). Currently, only plasmoids using solid textures and the window manager don't comply with the aesthetic, but everything else does. A potential solution is to detect a change in compositing in plasmoids that use solid textures, change the dialog type to ```NoBackground``` and display a custom texture as the background while compositing is disabled. A similar solution can also be applied to the window manager.
Another important note is that the tooltip SVG used in the solid appearance is now used as a workaround to allow orb transparency in SevenStart. For more information, go to [SevenStart](../Software/Plasmoids/SevenStart.md)'s documentation.
### Icons
This theme also features a small set of icons used mainly by plasmoids like the system tray. Most of these icons are simply just scaled down variants of icons taken from Breeze, while other icons are taken directly from the main icon theme of this project.
@ -54,7 +56,7 @@ The "Unique" column describes if the file itself is exclusive to Seven-Black.
|panel-background.svg |widgets/ |Default taskbar texture. |Y |Y |N |
|tooltip.svg |widgets/ |Default tooltip texture. |Y |Y |N |
|button-close.svg |widgets/ |Close button icon used in SevenTasks. |N |N |Y |
|menuitem.svg |widgets/ |Texture used for menu items in SevenStart and SevenTasks|N |N |Y |
|menuitem.svg |widgets/ |Texture used for menu items in SevenStart and SevenTasks.|N |N |Y |
|showdesktop.svg |widgets/ |Texture used for the Show desktop plasmoid. |N |N |Y |
|sidebaritem.svg |widgets/ |Texture used for sidebar entries in SevenStart. |N |N |Y |
|startmenu-buttons.svg |widgets/ |Texture used for the shut down button in SevenStart.|N |N |Y |
@ -70,6 +72,7 @@ The "Unique" column describes if the file itself is exclusive to Seven-Black.
|panel-background.svg |opaque/widgets/|Used when compositing is disabled. |N |Y |N |
|tooltip.svg |opaque/widgets/|Used when compositing is disabled. |N |Y |N |
|background.svg |solid/dialogs/ |Used by the system tray and date and time plasmoid. |Y |Y |N |
|tooltip.svg |solid/widgets/ |Used to provide an opacity mask to SevenStart's orb. |Y |Y |N |
## Nonstandard SVGs <a name="svgs"></a>

View file

@ -72,7 +72,7 @@ Starting off with the simplest modifications, this is a list of recommended sett
- Under KWin Scripts:
- Enable 'MinimizeAll'
When editing Plasma's bottom panel, make sure its width is set to 40 pixels.
When editing Plasma's bottom panel, make sure its width is set to 40 pixels (30 if using a small panel).
### KDE Plasma Theme <a name="plasma-theme"></a>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 168 KiB

View file

@ -12,6 +12,9 @@
<entry name="useCustomButtonImage" type="Bool">
<default>true</default>
</entry>
<entry name="stickOutOrb" type="Bool">
<default>false</default>
</entry>
<entry name="customButtonImage" type="Url">
<default></default>
</entry>

View file

@ -22,10 +22,10 @@ import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kwindowsystem 1.0
Item {
id: root
readonly property var screenGeometry: plasmoid.screenGeometry
readonly property bool inPanel: (plasmoid.location == PlasmaCore.Types.TopEdge
|| plasmoid.location == PlasmaCore.Types.RightEdge
@ -33,23 +33,28 @@ Item {
|| plasmoid.location == PlasmaCore.Types.LeftEdge)
readonly property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical)
readonly property bool useCustomButtonImage: (plasmoid.configuration.useCustomButtonImage)
// Should the orb be rendered in its own dialog window so that it can stick out of the panel?
readonly property bool stickOutOrb: plasmoid.configuration.stickOutOrb && inPanel && !editMode
property QtObject dashWindow: null
property QtObject orb: null
property QtObject contextMenu: null
Plasmoid.status: dashWindow && dashWindow.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus
onWidthChanged: updateSizeHints()
onHeightChanged: updateSizeHints()
clip: true
function updateSizeHints() {
if (useCustomButtonImage) {
if (vertical) {
var scaledHeight = Math.floor(parent.width * (buttonIcon.implicitHeight / buttonIcon.implicitWidth));
var scaledHeight = Math.floor(parent.width * (floatingOrbPanel.buttonIcon.implicitHeight / floatingOrbPanel.buttonIcon.implicitWidth));
root.Layout.minimumHeight = scaledHeight;
root.Layout.maximumHeight = scaledHeight;
root.Layout.minimumWidth = units.iconSizes.small;
root.Layout.maximumWidth = inPanel ? units.iconSizeHints.panel : -1;
} else {
var scaledWidth = Math.floor(parent.height * (buttonIcon.implicitWidth / buttonIcon.implicitHeight));
var scaledWidth = Math.floor(parent.height * (floatingOrbPanel.buttonIcon.implicitWidth / floatingOrbPanel.buttonIcon.implicitHeight));
root.Layout.minimumWidth = scaledWidth;
root.Layout.maximumWidth = scaledWidth;
root.Layout.minimumHeight = units.iconSizes.small;
@ -61,15 +66,89 @@ Item {
root.Layout.minimumHeight = units.iconSizes.small
root.Layout.maximumHeight = inPanel ? units.iconSizeHints.panel : -1;
}
if(stickOutOrb && orb) {
root.Layout.minimumWidth = orb.width + panelSvg.margins.right*(compositing ? 0 : 1);
root.Layout.maximumWidth = orb.width + panelSvg.margins.right*(compositing ? 0 : 1);
root.Layout.minimumHeight = orb.height;
root.Layout.maximumHeight = orb.height;
}
}
onStickOutOrbChanged: {
updateSizeHints();
positionOrb();
}
/* The following code gets the ContainmentInterface instance which is used for two things:
* 1. Getting context menu actions for entering edit mode and adding plasmoids
* 2. Keeping track on when edit mode is enabled. This allows us to hide the StartOrb
* object so the user can actually highlight and select this plasmoid during edit mode.
*/
property var containmentInterface: null
readonly property bool editMode: containmentInterface ? containmentInterface.editMode : false
onParentChanged: {
if (parent) {
for (var obj = root, depth = 0; !!obj; obj = obj.parent, depth++) {
if (obj.toString().startsWith('ContainmentInterface')) {
// desktop containment / plasmoidviewer
// Note: This doesn't always work. FolderViewDropArea may not yet have
// ContainmentInterface as a parent when this loop runs.
if (typeof obj['editMode'] === 'boolean') {
root.containmentInterface = obj
break
}
} else if (obj.toString().startsWith('DeclarativeDropArea')) {
// panel containment
if (typeof obj['Plasmoid'] !== 'undefined' && obj['Plasmoid'].toString().startsWith('ContainmentInterface')) {
if (typeof obj['Plasmoid']['editMode'] === 'boolean') {
root.containmentInterface = obj.Plasmoid
break
}
}
}
}
}
}
Connections {
target: units.iconSizeHints
function onPanelChanged() { updateSizeHints(); }
}
property bool compositing: kwindowsystem.compositingActive
// If the url is empty (default value), then use the fallback url.
/* We want to change the background hint for the orb dialog window depending
* on the compositing state. In this case, 0 refers to NoBackground, while
* 2 refers to SolidBackground.
*/
onCompositingChanged: {
if(compositing) {
orb.backgroundHints = 0;
} else {
orb.backgroundHints = 2;
}
updateSizeHints();
positionOrb();
// Add a little padding to the orb.
if(compositing)
orb.x += panelSvg.margins.left;
}
function positionOrb() {
var pos = plasmoid.mapToGlobal(kicker.x, kicker.y); // Gets the global position of this plasmoid, in screen coordinates.
orb.width = floatingOrbPanel.buttonIcon.implicitWidth + panelSvg.margins.left;
orb.height = floatingOrbPanel.buttonIcon.implicitHeight;
orb.x = pos.x;
orb.y = pos.y + panelSvg.margins.bottom;
// Keep the orb positioned exactly on the bottom if it is rendered out of bounds (beyond the screen geometry)
if(orb.y + orb.height > plasmoid.screenGeometry.height) {
orb.y -= orb.y + orb.height - plasmoid.screenGeometry.height;
}
}
// If the url is empty (default value), then use the fallback url. Otherwise, return the url path relative to
// the location of the source code.
function getResolvedUrl(url, fallback) {
if(url.toString() === "") {
return Qt.resolvedUrl(fallback);
@ -78,6 +157,12 @@ Item {
}
property int opacityDuration: 250
function createContextMenu(pos) {
contextMenu = Qt.createQmlObject("ContextMenu {}", root);
contextMenu.fillActions();
contextMenu.show();
}
/*
* Three IconItems are used in order to achieve the same look and feel as Windows 7's
* orbs. When the menu is closed, hovering over the orb results in the hovered icon
@ -85,73 +170,48 @@ Item {
* visibility, where the normal and hovered icons are invisible, and the pressed icon
* is visible.
*
* These icons will by default try to fill up as much space as they can in the compact
* representation.
* When they're bounded by the panel, these icons will by default try to fill up as
* much space as they can in the compact representation while preserving their aspect
* ratio.
*/
PlasmaCore.IconItem {
id: buttonIcon
FloatingOrb {
id: floatingOrbPanel
visible: (!stickOutOrb)
anchors.fill: parent
opacity: 1
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
source: getResolvedUrl(plasmoid.configuration.customButtonImage, "orbs/normal.png")
smooth: true
roundToIconSize: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
}
PlasmaCore.IconItem {
id: buttonIconPressed
anchors.fill: parent
opacity: 1
visible: dashWindow.visible
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
source: getResolvedUrl(plasmoid.configuration.customButtonImageActive, "orbs/selected.png") //
smooth: true
roundToIconSize: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
}
PlasmaCore.IconItem {
id: buttonIconHovered
z: 1
source: getResolvedUrl(plasmoid.configuration.customButtonImageHover, "orbs/hovered.png");
opacity: mouseArea.containsMouse
visible: !dashWindow.visible
anchors.fill: parent
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
smooth: true
Behavior on opacity {
NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; duration: opacityDuration }
}
// 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: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
objectName: "innerorb"
}
// Clicking on the plasmoid or activating it in any way causes the Full representation
// to show/hide.
// Covers the entire compact representation just in case the orb dialog doesn't cover
// the entire area by itself.
MouseArea
{
id: mouseArea
id: mouseAreaCompositingOff
anchors.fill: parent
hoverEnabled: true
visible: stickOutOrb
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
dashWindow.visible = !dashWindow.visible;
dashWindow.showingAllPrograms = false;
if(mouse.button == Qt.RightButton) {
var pos = plasmoid.mapToGlobal(mouse.x, mouse.y);
createContextMenu(pos);
} else {
dashWindow.visible = !dashWindow.visible;
dashWindow.showingAllPrograms = false;
}
}
}
Component.onCompleted: {
dashWindow = Qt.createQmlObject("MenuRepresentation {}", root);
orb = Qt.createQmlObject("StartOrb {}", root);
plasmoid.activated.connect(function() {
dashWindow.visible = !dashWindow.visible;
dashWindow.showingAllPrograms = false;
});
positionOrb();
}
}

View file

@ -48,6 +48,7 @@ Item {
property alias cfg_appNameFormat: appNameFormat.currentIndex
property alias cfg_switchCategoriesOnHover: switchCategoriesOnHover.checked
property alias cfg_stickOutOrb: stickOutOrb.checked
property alias cfg_useExtraRunners: useExtraRunners.checked
@ -61,7 +62,7 @@ Item {
spacing: units.smallSpacing
Label {
text: i18n("Icon:")
text: i18n("Orb:")
}
IconPicker {
@ -73,7 +74,7 @@ Item {
}
Label {
text: i18n("Hover Icon:")
text: i18n("Hover Orb:")
}
IconPicker {
id: iconPickerHover
@ -84,7 +85,7 @@ Item {
}
Label {
text: i18n("Active Icon:")
text: i18n("Clicked Orb:")
}
IconPicker {
id: iconPickerActive
@ -123,6 +124,11 @@ Item {
text: i18n("Switch categories on hover")
}
CheckBox {
id: stickOutOrb
text: i18n("Force constant orb size")
}
}
}

View file

@ -0,0 +1,93 @@
import QtQuick 2.15
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
PlasmaComponents.ContextMenu {
id: menu
placement: {
if (plasmoid.location === PlasmaCore.Types.LeftEdge) {
return PlasmaCore.Types.RightPosedTopAlignedPopup;
} else if (plasmoid.location === PlasmaCore.Types.TopEdge) {
return PlasmaCore.Types.BottomPosedLeftAlignedPopup;
} else if (plasmoid.location === PlasmaCore.Types.RightEdge) {
return PlasmaCore.Types.LeftPosedTopAlignedPopup;
} else {
return PlasmaCore.Types.TopPosedLeftAlignedPopup;
}
}
minimumWidth: root.width
function show() {
openRelative();
}
function newMenuItem(parent) {
return Qt.createQmlObject(`
import org.kde.plasma.components 2.0 as PlasmaComponents
PlasmaComponents.MenuItem { }
`, parent);
}
function newSeparator(parent) {
return Qt.createQmlObject(`
import org.kde.plasma.components 2.0 as PlasmaComponents
PlasmaComponents.MenuItem { separator: true }
`, parent);
}
function fillItem(item, action) {
item.text = action.text;
item.icon = action.icon;
item.visible = action.visible;
item.objectName = action.objectName;
item.clicked.connect(function() {
action.trigger();
});
addMenuItem(item);
}
function fillActions() {
plasmoid.prepareContextualActions();
for(var index = 0; index < plasmoid.contextualActions.length; index++) {
var action = plasmoid.contextualActions[index];
var item = newMenuItem(menu);
fillItem(item, action);
}
var action = plasmoid.action("alternatives");
if(action && action.enabled) {
var item = newMenuItem(menu);
fillItem(item, action);
}
item = newMenuItem(menu);
action = plasmoid.action("configure");
fillItem(item, action);
item = newSeparator(menu);
addMenuItem(item);
action = containmentInterface.action("add widgets");
if(action) {
item = newMenuItem(menu);
fillItem(item, action);
};
action = containmentInterface.action("configure");
if(action) {
item = newMenuItem(menu);
fillItem(item, action);
};
}
onStatusChanged: {
if (status == PlasmaComponents.DialogStatus.Closed) {
menu.destroy();
}
}
}

View file

@ -0,0 +1,103 @@
import QtQuick 2.4
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.1
import QtGraphicalEffects 1.0
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.private.kicker 0.1 as Kicker
import org.kde.kcoreaddons 1.0 as KCoreAddons // kuser
import org.kde.plasma.private.shell 2.0
import org.kde.kwindowsystem 1.0
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.private.quicklaunch 1.0
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons
import org.kde.kwindowsystem 1.0
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
Item {
id: floatingOrb
width: buttonIcon.implicitWidth
height: buttonIcon.implicitHeight
property alias buttonIcon: buttonIcon
property alias buttonIconPressed: buttonIconPressed
property alias buttonIconHovered: buttonIconHovered
property alias mouseArea: mouseArea
PlasmaCore.IconItem {
id: buttonIcon
anchors.fill: parent
opacity: 1
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
source: getResolvedUrl(plasmoid.configuration.customButtonImage, "orbs/normal.png")
smooth: true
roundToIconSize: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
}
PlasmaCore.IconItem {
id: buttonIconPressed
anchors.fill: parent
opacity: 1
visible: dashWindow.visible
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
source: getResolvedUrl(plasmoid.configuration.customButtonImageActive, "orbs/selected.png") //
smooth: true
roundToIconSize: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
}
PlasmaCore.IconItem {
id: buttonIconHovered
z: 1
source: getResolvedUrl(plasmoid.configuration.customButtonImageHover, "orbs/hovered.png");
opacity: mouseArea.containsMouse || mouseAreaCompositingOff.containsMouse
visible: !dashWindow.visible
anchors.fill: parent
readonly property double aspectRatio: (vertical ? implicitHeight / implicitWidth
: implicitWidth / implicitHeight)
smooth: true
Behavior on opacity {
NumberAnimation { properties: "opacity"; easing.type: Easing.InOutQuad; duration: opacityDuration }
}
// 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: !useCustomButtonImage || aspectRatio === 1
onSourceChanged: updateSizeHints()
}
MouseArea
{
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if(mouse.button == Qt.RightButton) {
var pos = plasmoid.mapToGlobal(mouse.x, mouse.y);
createContextMenu(pos);
} else {
dashWindow.visible = !dashWindow.visible;
dashWindow.showingAllPrograms = false;
}
}
}
}

View file

@ -126,7 +126,10 @@ PlasmaExtras.ScrollArea {
delegate: Item {
width: itemColumn.width
height: gridViewLabel.height + gridView.height + (index == repeater.count - 1 ? 0 : units.smallSpacing)
visible: repeater.model.modelForRow(index).count > 0
visible: {
if(!repeater.model.modelForRow(index)) return false;
return repeater.model.modelForRow(index).count > 0
}
property alias currentIndex: gridView.currentIndex
property alias count: gridView.count
@ -143,7 +146,10 @@ PlasmaExtras.ScrollArea {
color: "#1d3287";
level: 4
verticalAlignment: Qt.AlignVCenter
text: repeater.model.modelForRow(index).description + " (" + repeater.model.modelForRow(index).count +")"
text: {
if(!repeater.model.modelForRow(index)) return "";
return repeater.model.modelForRow(index).description + " (" + repeater.model.modelForRow(index).count +")";
}
}
//Line that extends from the header to the right of the search view.

View file

@ -65,6 +65,7 @@ ListModel {
}
function parseModel(appList, model, path) {
// console.log(path, model, model.description, model.count);
for (var i = 0; i < model.count; i++) {
var item = model.modelForRow(i);
if (!item) {
@ -72,8 +73,10 @@ ListModel {
}
var itemPath = (path || []).concat(i);
if (item && item.hasChildren) {
// console.log(item)
parseModel(appList, item, itemPath);
} else {
// console.log(itemPath, item, item.description);
appList.push(item);
}
}

View file

@ -42,7 +42,7 @@ import org.kde.plasma.private.quicklaunch 1.0
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons
import org.kde.kwindowsystem 1.0
PlasmaCore.Dialog {
@ -61,7 +61,7 @@ PlasmaCore.Dialog {
property bool searching: (searchField.text != "")
property bool showingAllPrograms: false
property bool firstTimePopup: false // To make sure the user icon is displayed properly.
property bool compositingEnabled: kwindowsystem.compositingActive
property int slideAnimationDuration: 105
@ -182,7 +182,7 @@ PlasmaCore.Dialog {
focus: true
clip: true
KWindowSystem { id: kwindowsystem } // Used for detecting compositing changes.
KCoreAddons.KUser { id: kuser } // Used for getting the username and icon.
Logic { id: logic } // Probably useful.

View file

@ -57,7 +57,7 @@ Item {
if (!searchField.text) {
repeaterModelIndex = 0;
runnerGrid.repeater.currentModelIndex = 0;
runnerModel.model = null;
if(runnerModel.model) runnerModel.model = null;
} else {
if(runnerGrid.count != 0) {
runnerGrid.repeater.currentModelIndex = 0;
@ -72,6 +72,7 @@ Item {
if(!focus) repeaterModelIndex = runnerGrid.repeater.currentModelIndex;
else {
runnerGrid.repeater.currentModelIndex = repeaterModelIndex;
console.log("home.");
}
}
}

View file

@ -0,0 +1,103 @@
import QtQuick 2.4
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.1
import QtGraphicalEffects 1.0
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.private.kicker 0.1 as Kicker
import org.kde.kcoreaddons 1.0 as KCoreAddons // kuser
import org.kde.plasma.private.shell 2.0
import org.kde.kwindowsystem 1.0
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.private.quicklaunch 1.0
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons
import org.kde.kwindowsystem 1.0
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
/*
* This is the Dialog that displays the Start menu orb when it sticks out
* of the panel. In principle, it works in almost the same way as the
* Start menu user icon, in fact most of the code is directly copied from
* there.
*
* With this approach, two important problems come up:
* 1. Right clicking on this dialog window won't open the standard
* context menu as expected, as it actually isn't part of the
* compact or full representation.
* 2. When compositing is disabled, even with the NoBackground hint, this
* dialog window will simply be drawn with an opaque, black background.
*
* This source code tackles both problems in such a way to avoid having to
* introduce a compiled component of the plasmoid, which would greatly
* reduce its portability and ease of installation. The downside of this
* approach is that the solution to the second problem leaves us bound to
* the Seven-Black Plasma theme, as it uses the SVG file found in
*
* /solid/widgets/tooltip.svg
*
* However, this can be circumvented by copying this file over to any other
* theme, as this is probably the *least* used SVG texture in KDE, if not
* completely unused. This SVG file contains only the opacity and input
* mask of the dialog window, which gets applied when X11 compositing is
* disabled. Another slight limitation to this approach is that the
* possible choice of menu orbs is limited to perfect spheres in this
* particular configuration. This can be corrected if the opacity mask is
* changed in the SVG file, meaning that installing custom orbs might
* potentially be a two-step process now.
*
* Compared to the popup avatar, this dialog window should NOT have any
* visualParent set, as it causes inexplicable behavior where the orb
* moves away. I have no idea why it does that.
*
* This has been developed only for the bottom/south oriented panel, and
* other orientations should receive support when I begin giving pretty
* much *everything* else support for other orientations.
*
*/
PlasmaCore.Dialog {
id: iconUser
flags: Qt.WindowStaysOnTopHint | Qt.Popup | Qt.X11BypassWindowManagerHint // To prevent the icon from animating its opacity when its visibility is changed
type: "Tooltip" // Crucial to making this work under no compositing. See above for details.
location: "Floating"
// Positions are defined when the plasmoid has been fully loaded, to prevent undefined behavior.
x: 0
y: 0
onYChanged: { // Tries to circumvent possible inexplicable changes in position. Currently doesn't seem to run in practice anymore, as it has most likely been fixed by keeping the visualParent undefined.
var pos = plasmoid.mapToGlobal(kicker.x, kicker.y);
if(iconUser.y - pos.y < 0) {
iconUser.y -= iconUser.y - pos.y + panelSvg.margins.bottom*2 + panelSvg.margins.top*2;
}
}
backgroundHints: PlasmaCore.Types.SolidBackground
visible: root.visible && stickOutOrb
opacity: iconUser.visible // To prevent even more NP-hard unpredictable behavior
// The actual orb button, this dialog window is just a container for it.
mainItem: FloatingOrb {
id: floatingOrbIcon
}
Component.onCompleted: {
/*
* When compositing is enabled, we want to simply use the NoBackground
* hint to render a fully transparent, blur-free window. If we used the
* same opacity mask for both cases, blur would be applied behind the
* orb, which would also by extension render reflections and colorize
* the entire area as well.
*/
iconUser.backgroundHints = compositing ? 0 : 2;
}
}

View file

@ -24,6 +24,8 @@ import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.private.kicker 0.1 as Kicker
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
import org.kde.kwindowsystem 1.0
Item {
id: kicker
@ -33,6 +35,7 @@ Item {
anchors.fill: parent
property bool isDash: false
property Item dragSource: null
clip: true
// With this we can make the compact representation be any
// item we want.
@ -42,6 +45,7 @@ Item {
property QtObject globalFavorites: rootModel.favoritesModel
property QtObject systemFavorites: rootModel.systemFavoritesModel
property bool compositingEnabled: kwindowsystem.compositingActive
// Runs KMenuEdit.
function action_menuedit() {
@ -82,7 +86,7 @@ Item {
}
signal exited(string cmd, int exitCode, int exitStatus, string stdout, string stderr)
}
KWindowSystem { id: kwindowsystem } // Used for detecting compositing changes.
Kicker.RootModel {
id: rootModel
@ -230,5 +234,6 @@ Item {
rootModel.refreshed.connect(reset);
dragHelper.dropped.connect(resetDragSource);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -58,6 +58,7 @@ MouseArea {
property real taskHeight: 0
property string previousState: ""
property bool rightClickDragging: false
property bool toolTipOpen: false
property Item audioStreamOverlay
property var audioStreams: []
@ -153,6 +154,7 @@ MouseArea {
}
hoverEnabled = true;
taskList.updateHoverFunc();
toolTipArea.tooltipClicked = true;
}
onContainsMouseChanged: {
@ -176,6 +178,7 @@ MouseArea {
hoverEnabled = true;
updateMousePosition(ma.mouseX);
toolTipArea.tooltipClicked = true;
}
@ -226,6 +229,9 @@ MouseArea {
if (plasmoid.configuration.showToolTips && toolTipArea.active) {
hideToolTipTemporarily();
}
/*if(childCount >= 2 && plasmoid.configuration.showToolTips && toolTipArea.active) {
toolTipArea.tooltipClicked = !toolTipArea.tooltipClicked;
}*/
TaskTools.activateTask(modelIndex(), model, mouse.modifiers, task);
} else if (mouse.button === Qt.BackButton || mouse.button === Qt.ForwardButton) {
@ -452,7 +458,7 @@ MouseArea {
opacity: 0.6
visible: childCount >= 3 ? true : false
anchors.rightMargin: PlasmaCore.Units.smallSpacing
enabledBorders: Plasma.FrameSvg.EnabledBorders.RightBorder
enabledBorders: PlasmaCore.FrameSvg.RightBorder
}
@ -711,8 +717,8 @@ MouseArea {
PlasmaCore.ToolTipArea {
id: toolTipArea
z: -1
//backgroundHints: "SolidBackground"
MouseArea {
MouseArea {
id: ma
hoverEnabled: true
propagateComposedEvents: true
@ -720,34 +726,32 @@ MouseArea {
onPositionChanged: {
task.updateMousePosition(ma.mouseX);
task.positionChanged(mouse);
//var xtr = toolTipArea.backgroundHints();
}
onContainsMouseChanged: {
task.updateMousePosition(ma.mouseX);
//task.onContainsMouseChanged();
//toolTipArea.onContainsMouseChanged();
//mouse.accepted = false;
}
onPressed: mouse.accepted = false;
onReleased: mouse.accepted = false;
onWheel: wheel.accepted = false;
//onExited: { hoverGradient.horizontalOffset = 0;
//task.onExited();
//}
}
anchors.fill: parent
location: plasmoid.location
active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips
property bool tooltipClicked: true
active: !inPopup && !groupDialog.visible && (plasmoid.configuration.showToolTips || tasks.toolTipOpenedByClick === toolTipArea)
interactive: model.IsWindow === true
mainItem: (model.IsWindow === true) ? openWindowToolTipDelegate : pinnedAppToolTipDelegate
property alias mainToolTip: toolTipArea.mainItem
onToolTipVisibleChanged: {
task.toolTipOpen = toolTipVisible;
if(!toolTipVisible) {
tasks.toolTipOpenedByClick = null;
} else {
tasks.toolTipAreaItem = toolTipArea;
}
}
onContainsMouseChanged: {
updateMousePosition(ma.mouseX);
if (containsMouse) {
mainItem.parentTask = task;
@ -802,6 +806,9 @@ MouseArea {
mainItem.smartLauncherCount = Qt.binding(function() {
return mainItem.smartLauncherCountVisible ? task.smartLauncherItem.count : 0;
});
tasks.toolTipAreaItem = toolTipArea;
} else {
tasks.toolTipOpenedByClick = null;
}
}
}

View file

@ -148,7 +148,11 @@ function activateTask(index, model, modifiers, task) {
else if (plasmoid.configuration.showToolTips
&& plasmoid.configuration.groupedTaskVisualization === 1
) {
task.showToolTip();
if(tasks.toolTipOpenedByClick) tasks.toolTipOpenedByClick.hideImmediately();
else {
tasks.toolTipOpenedByClick = task.toolTipAreaItem;
task.showToolTip();
}
}
// Option 3: show Present Windows for all child tasks

View file

@ -27,7 +27,8 @@ MouseArea {
// This property tells the plasmoid to render labels next to task icons.
// Previously, this property was determined by the value of (plasmoid.pluginName === "org.kde.plasma.icontasks")
property bool iconsOnly: !plasmoid.configuration.labelVisible
property var toolTipOpenedByClick: null
property var toolTipAreaItem: null
//property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml");
property QtObject tasksMenuComponent: Qt.createComponent("TasksMenu.qml");
property QtObject pulseAudioComponent: Qt.createComponent("PulseAudio.qml");
@ -60,6 +61,7 @@ MouseArea {
signal requestLayout
signal windowsHovered(variant winIds, bool hovered)
signal activateWindowView(variant winIds)
signal presentWindows(variant winIds)
states: State {
@ -511,10 +513,12 @@ MouseArea {
// With this, we can update each task icon pretty much globally.
function updateHoverFunc() {
for(var i = 0; i < taskRepeater.count; i++) {
taskRepeater.itemAt(i).updateHoverColor();
if(taskRepeater.itemAt(i)) {
taskRepeater.itemAt(i).updateHoverColor();
}
}
tasks.state = "";
console.log("Updated hovers");
//console.log("Updated hovers");
}
Timer {
@ -604,7 +608,8 @@ MouseArea {
tasks.requestLayout.connect(layoutTimer.restart);
tasks.requestLayout.connect(iconGeometryTimer.restart);
tasks.windowsHovered.connect(backend.windowsHovered);
tasks.presentWindows.connect(backend.presentWindows);
//tasks.presentWindows.connect(backend.presentWindows);
tasks.activateWindowView.connect(backend.activateWindowView);
dragHelper.dropped.connect(resetDragSource);
taskList.updateHoverFunc();
}

View file

@ -1,774 +0,0 @@
/*
SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org>
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.taskmanager 0.1 as TaskManager
import "code/layout.js" as LayoutManager
PlasmaComponents.ContextMenu {
id: menu
property QtObject backend
property QtObject mpris2Source
property var modelIndex
readonly property var atm: TaskManager.AbstractTasksModel
property bool showAllPlaces: false
placement: {
if (plasmoid.location === PlasmaCore.Types.LeftEdge) {
return PlasmaCore.Types.RightPosedTopAlignedPopup;
} else if (plasmoid.location === PlasmaCore.Types.TopEdge) {
return PlasmaCore.Types.BottomPosedLeftAlignedPopup;
} else if (plasmoid.location === PlasmaCore.Types.RightEdge) {
return PlasmaCore.Types.LeftPosedTopAlignedPopup;
} else {
return PlasmaCore.Types.TopPosedLeftAlignedPopup;
}
}
minimumWidth: visualParent.width
//We have all of the above
//Might be important, at least the opening part
onStatusChanged: {
if (visualParent && get(atm.LauncherUrlWithoutIcon) != "" && status == PlasmaComponents.DialogStatus.Open) {
activitiesDesktopsMenu.refresh();
} else if (status == PlasmaComponents.DialogStatus.Closed) {
menu.destroy();
backend.ungrabMouse(visualParent);
}
}
Component.onCompleted: {
// Cannot have "Connections" as child of PlasmaCoponents.ContextMenu.
backend.showAllPlaces.connect(showContextMenuWithAllPlaces);
}
Component.onDestruction: {
backend.showAllPlaces.disconnect(showContextMenuWithAllPlaces);
}
function showContextMenuWithAllPlaces() {
visualParent.showContextMenu({showAllPlaces: true});
}
function get(modelProp) {
return tasksModel.data(modelIndex, modelProp)
}
function show() {
loadDynamicLaunchActions(get(atm.LauncherUrlWithoutIcon));
openRelative();
}
function newMenuItem(parent) {
return Qt.createQmlObject(`
import org.kde.plasma.components 2.0 as PlasmaComponents
PlasmaComponents.MenuItem {}
`, parent);
}
function newSeparator(parent) {
return Qt.createQmlObject(`
import org.kde.plasma.components 2.0 as PlasmaComponents
PlasmaComponents.MenuItem { separator: true }
`, parent);
}
function loadDynamicLaunchActions(launcherUrl) {
var sections = [
{
title: i18n("Places"),
group: "places",
actions: backend.placesActions(launcherUrl, showAllPlaces, menu)
},
{
title: i18n("Recent Files"),
group: "recents",
actions: backend.recentDocumentActions(launcherUrl, menu)
},
{
title: i18n("Actions"),
group: "actions",
actions: backend.jumpListActions(launcherUrl, menu)
}
]
// C++ can override section heading by returning a QString as first action
sections.forEach((section) => {
if (typeof section.actions[0] === "string") {
section.title = section.actions.shift(); // take first
}
});
// QMenu does not limit its width automatically. Even if we set a maximumWidth
// it would just cut off text rather than eliding. So we do this manually.
var textMetrics = Qt.createQmlObject("import QtQuick 2.4; TextMetrics {}", menu);
var maximumWidth = LayoutManager.maximumContextMenuTextWidth();
sections.forEach(function (section) {
if (section["actions"].length > 0 || section["group"] == "actions") {
// Don't add the "Actions" header if the menu has nothing but actions
// in it, because then it's redundant (all menus have actions)
if (
(section["group"] != "actions") ||
(section["group"] == "actions" && (sections[0]["actions"].length > 0 || sections[1]["actions"].length > 0))
) {
var sectionHeader = newMenuItem(menu);
sectionHeader.text = section["title"];
sectionHeader.section = true;
menu.addMenuItem(sectionHeader, startNewInstanceItem);
}
}
for (var i = 0; i < section["actions"].length; ++i) {
var item = newMenuItem(menu);
item.action = section["actions"][i];
// Crude way of manually eliding...
var elided = false;
textMetrics.text = Qt.binding(function() {
return item.action.text;
});
while (textMetrics.width > maximumWidth) {
item.action.text = item.action.text.slice(0, -1);
elided = true;
}
if (elided) {
item.action.text += "…";
}
menu.addMenuItem(item, startNewInstanceItem);
}
});
// Add Media Player control actions
var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl, get(atm.AppPid));
if (sourceName && !(get(atm.WinIdList) !== undefined && get(atm.WinIdList).length > 1)) {
var playerData = mpris2Source.data[sourceName]
if (playerData.CanControl) {
var playing = (playerData.PlaybackStatus === "Playing");
var menuItem = menu.newMenuItem(menu);
menuItem.text = i18nc("Play previous track", "Previous Track");
menuItem.icon = "media-skip-backward";
menuItem.enabled = Qt.binding(function() {
return playerData.CanGoPrevious;
});
menuItem.clicked.connect(function() {
mpris2Source.goPrevious(sourceName);
});
menu.addMenuItem(menuItem, startNewInstanceItem);
menuItem = menu.newMenuItem(menu);
// PlasmaCore Menu doesn't actually handle icons or labels changing at runtime...
menuItem.text = Qt.binding(function() {
// if CanPause, toggle the menu entry between Play & Pause, otherwise always use Play
return playing && playerData.CanPause ? i18nc("Pause playback", "Pause") : i18nc("Start playback", "Play");
});
menuItem.icon = Qt.binding(function() {
return playing && playerData.CanPause ? "media-playback-pause" : "media-playback-start";
});
menuItem.enabled = Qt.binding(function() {
return playing ? playerData.CanPause : playerData.CanPlay;
});
menuItem.clicked.connect(function() {
if (playing) {
mpris2Source.pause(sourceName);
} else {
mpris2Source.play(sourceName);
}
});
menu.addMenuItem(menuItem, startNewInstanceItem);
menuItem = menu.newMenuItem(menu);
menuItem.text = i18nc("Play next track", "Next Track");
menuItem.icon = "media-skip-forward";
menuItem.enabled = Qt.binding(function() {
return playerData.CanGoNext;
});
menuItem.clicked.connect(function() {
mpris2Source.goNext(sourceName);
});
menu.addMenuItem(menuItem, startNewInstanceItem);
menuItem = menu.newMenuItem(menu);
menuItem.text = i18nc("Stop playback", "Stop");
menuItem.icon = "media-playback-stop";
menuItem.enabled = Qt.binding(function() {
return playerData.PlaybackStatus !== "Stopped";
});
menuItem.clicked.connect(function() {
mpris2Source.stop(sourceName);
});
menu.addMenuItem(menuItem, startNewInstanceItem);
// Technically media controls and audio streams are separate but for the user they're
// semantically related, don't add a separator inbetween.
if (!menu.visualParent.hasAudioStream) {
menu.addMenuItem(newSeparator(menu), startNewInstanceItem);
}
// If we don't have a window associated with the player but we can quit
// it through MPRIS we'll offer a "Quit" option instead of "Close"
if (!closeWindowItem.visible && playerData.CanQuit) {
menuItem = menu.newMenuItem(menu);
menuItem.text = i18nc("Quit media player app", "Quit");
menuItem.icon = "application-exit";
menuItem.visible = Qt.binding(function() {
return !closeWindowItem.visible;
});
menuItem.clicked.connect(function() {
mpris2Source.quit(sourceName);
});
menu.addMenuItem(menuItem);
}
// If we don't have a window associated with the player but we can raise
// it through MPRIS we'll offer a "Restore" option
if (get(atm.IsLauncher) === true && !startNewInstanceItem.visible && playerData.CanRaise) {
menuItem = menu.newMenuItem(menu);
menuItem.text = i18nc("Open or bring to the front window of media player app", "Restore");
menuItem.icon = playerData["Desktop Icon Name"];
menuItem.visible = Qt.binding(function() {
return !startNewInstanceItem.visible;
});
menuItem.clicked.connect(function() {
mpris2Source.raise(sourceName);
});
menu.addMenuItem(menuItem, startNewInstanceItem);
}
}
}
// We allow mute/unmute whenever an application has a stream, regardless of whether it
// is actually playing sound.
// This way you can unmute, e.g. a telephony app, even after the conversation has ended,
// so you still have it ringing later on.
if (menu.visualParent.hasAudioStream) {
var muteItem = menu.newMenuItem(menu);
muteItem.checkable = true;
muteItem.checked = Qt.binding(function() {
return menu.visualParent && menu.visualParent.muted;
});
muteItem.clicked.connect(function() {
menu.visualParent.toggleMuted();
});
muteItem.text = i18n("Mute");
muteItem.icon = "audio-volume-muted";
menu.addMenuItem(muteItem, startNewInstanceItem);
menu.addMenuItem(newSeparator(menu), startNewInstanceItem);
}
}
PlasmaComponents.MenuItem {
id: startNewInstanceItem
visible: get(atm.CanLaunchNewInstance)
text: i18n("Open New Window")
icon: "window-new"
onClicked: tasksModel.requestNewInstance(modelIndex)
}
PlasmaComponents.MenuItem {
id: virtualDesktopsMenuItem
visible: virtualDesktopInfo.numberOfDesktops > 1
&& (visualParent && get(atm.IsLauncher) !== true
&& get(atm.IsStartup) !== true
&& get(atm.IsVirtualDesktopsChangeable) === true)
enabled: visible
text: i18n("Move to &Desktop")
icon: "virtual-desktops"
Connections {
target: virtualDesktopInfo
function onNumberOfDesktopsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
function onDesktopIdsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
function onDesktopNamesChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
}
PlasmaComponents.ContextMenu {
id: virtualDesktopsMenu
visualParent: virtualDesktopsMenuItem.action
function refresh() {
clearMenuItems();
if (virtualDesktopInfo.numberOfDesktops <= 1) {
return;
}
var menuItem = menu.newMenuItem(virtualDesktopsMenu);
menuItem.text = i18n("Move &To Current Desktop");
menuItem.enabled = Qt.binding(function() {
return menu.visualParent && menu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.currentDesktop) === -1;
});
menuItem.clicked.connect(function() {
tasksModel.requestVirtualDesktops(menu.modelIndex, [virtualDesktopInfo.currentDesktop]);
});
menuItem = menu.newMenuItem(virtualDesktopsMenu);
menuItem.text = i18n("&All Desktops");
menuItem.checkable = true;
menuItem.checked = Qt.binding(function() {
return menu.visualParent && menu.get(atm.IsOnAllVirtualDesktops) === true;
});
menuItem.clicked.connect(function() {
tasksModel.requestVirtualDesktops(menu.modelIndex, []);
});
backend.setActionGroup(menuItem.action);
menu.newSeparator(virtualDesktopsMenu);
for (var i = 0; i < virtualDesktopInfo.desktopNames.length; ++i) {
menuItem = menu.newMenuItem(virtualDesktopsMenu);
menuItem.text = i18nc("1 = number of desktop, 2 = desktop name", "&%1 %2", i + 1, virtualDesktopInfo.desktopNames[i]);
menuItem.checkable = true;
menuItem.checked = Qt.binding((function(i) {
return function() { return menu.visualParent && menu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.desktopIds[i]) > -1 };
})(i));
menuItem.clicked.connect((function(i) {
return function() { return tasksModel.requestVirtualDesktops(menu.modelIndex, [virtualDesktopInfo.desktopIds[i]]); };
})(i));
backend.setActionGroup(menuItem.action);
}
menu.newSeparator(virtualDesktopsMenu);
menuItem = menu.newMenuItem(virtualDesktopsMenu);
menuItem.text = i18n("&New Desktop");
menuItem.clicked.connect(function() {
tasksModel.requestNewVirtualDesktop(menu.modelIndex);
});
}
Component.onCompleted: refresh()
}
}
PlasmaComponents.MenuItem {
id: activitiesDesktopsMenuItem
visible: activityInfo.numberOfRunningActivities > 1
&& (visualParent && !get(atm.IsLauncher)
&& !get(atm.IsStartup))
enabled: visible
text: i18n("Show in &Activities")
icon: "activities"
Connections {
target: activityInfo
function onNumberOfRunningActivitiesChanged() {
activitiesDesktopsMenu.refresh()
}
}
PlasmaComponents.ContextMenu {
id: activitiesDesktopsMenu
visualParent: activitiesDesktopsMenuItem.action
function refresh() {
clearMenuItems();
if (activityInfo.numberOfRunningActivities <= 1) {
return;
}
var menuItem = menu.newMenuItem(activitiesDesktopsMenu);
menuItem.text = i18n("Add To Current Activity");
menuItem.enabled = Qt.binding(function() {
return menu.visualParent && menu.get(atm.Activities).length > 0 &&
menu.get(atm.Activities).indexOf(activityInfo.currentActivity) < 0;
});
menuItem.clicked.connect(function() {
tasksModel.requestActivities(menu.modelIndex, menu.get(atm.Activities).concat(activityInfo.currentActivity));
});
menuItem = menu.newMenuItem(activitiesDesktopsMenu);
menuItem.text = i18n("All Activities");
menuItem.checkable = true;
menuItem.checked = Qt.binding(function() {
return menu.visualParent && menu.get(atm.Activities).length === 0;
});
menuItem.toggled.connect(function(checked) {
let newActivities = []; // will cast to an empty QStringList i.e all activities
if (!checked) {
newActivities = new Array(activityInfo.currentActivity);
}
tasksModel.requestActivities(menu.modelIndex, newActivities);
});
menu.newSeparator(activitiesDesktopsMenu);
var runningActivities = activityInfo.runningActivities();
for (var i = 0; i < runningActivities.length; ++i) {
var activityId = runningActivities[i];
menuItem = menu.newMenuItem(activitiesDesktopsMenu);
menuItem.text = activityInfo.activityName(runningActivities[i]);
menuItem.checkable = true;
menuItem.checked = Qt.binding( (function(activityId) {
return function() {
return menu.visualParent && menu.get(atm.Activities).indexOf(activityId) >= 0;
};
})(activityId));
menuItem.toggled.connect((function(activityId) {
return function (checked) {
var newActivities = menu.get(atm.Activities);
if (checked) {
newActivities = newActivities.concat(activityId);
} else {
var index = newActivities.indexOf(activityId)
if (index < 0) {
return;
}
newActivities.splice(index, 1);
}
return tasksModel.requestActivities(menu.modelIndex, newActivities);
};
})(activityId));
}
menu.newSeparator(activitiesDesktopsMenu);
for (var i = 0; i < runningActivities.length; ++i) {
var activityId = runningActivities[i];
var onActivities = menu.get(atm.Activities);
// if the task is on a single activity, don't insert a "move to" item for that activity
if(onActivities.length == 1 && onActivities[0] == activityId) {
continue;
}
menuItem = menu.newMenuItem(activitiesDesktopsMenu);
menuItem.text = i18n("Move to %1", activityInfo.activityName(activityId))
menuItem.clicked.connect((function(activityId) {
return function () {
return tasksModel.requestActivities(menu.modelIndex, [activityId]);
};
})(activityId));
}
menu.newSeparator(activitiesDesktopsMenu);
}
Component.onCompleted: refresh()
}
}
PlasmaComponents.MenuItem {
id: launcherToggleAction
visible: visualParent
&& get(atm.IsLauncher) !== true
&& get(atm.IsStartup) !== true
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
&& (activityInfo.numberOfRunningActivities < 2)
&& !doesBelongToCurrentActivity()
enabled: visualParent && get(atm.LauncherUrlWithoutIcon) != ""
text: i18n("&Pin to Task Manager")
icon: "window-pin"
function doesBelongToCurrentActivity() {
return tasksModel.launcherActivities(get(atm.LauncherUrlWithoutIcon)).some(function(activity) {
return activity === activityInfo.currentActivity || activity === activityInfo.nullUuid;
});
}
onClicked: {
tasksModel.requestAddLauncher(get(atm.LauncherUrl));
}
}
PlasmaComponents.MenuItem {
id: showLauncherInActivitiesItem
text: i18n("&Pin to Task Manager")
icon: "window-pin"
visible: visualParent
&& get(atm.IsStartup) !== true
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
&& (activityInfo.numberOfRunningActivities >= 2)
Connections {
target: activityInfo
function onNumberOfRunningActivitiesChanged() {
activitiesDesktopsMenu.refresh()
}
}
PlasmaComponents.ContextMenu {
id: activitiesLaunchersMenu
visualParent: showLauncherInActivitiesItem.action
function refresh() {
clearMenuItems();
if (menu.visualParent === null) return;
var createNewItem = function(id, title, url, activities) {
var result = menu.newMenuItem(activitiesLaunchersMenu);
result.text = title;
result.visible = true;
result.checkable = true;
result.checked = activities.some(function(activity) { return activity === id });
result.clicked.connect(
function() {
if (result.checked) {
tasksModel.requestAddLauncherToActivity(url, id);
} else {
tasksModel.requestRemoveLauncherFromActivity(url, id);
}
}
);
return result;
}
if (menu.visualParent === null) return;
var url = menu.get(atm.LauncherUrlWithoutIcon);
var activities = tasksModel.launcherActivities(url);
createNewItem(activityInfo.nullUuid, i18n("On All Activities"), url, activities);
if (activityInfo.numberOfRunningActivities <= 1) {
return;
}
createNewItem(activityInfo.currentActivity, i18n("On The Current Activity"), url, activities);
menu.newSeparator(activitiesLaunchersMenu);
var runningActivities = activityInfo.runningActivities();
runningActivities.forEach(function(id) {
createNewItem(id, activityInfo.activityName(id), url, activities);
});
}
Component.onCompleted: {
menu.onVisualParentChanged.connect(refresh);
refresh();
}
}
}
PlasmaComponents.MenuItem {
visible: (visualParent
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
&& !launcherToggleAction.visible
&& activityInfo.numberOfRunningActivities < 2)
text: i18n("Unpin from Task Manager")
icon: "window-unpin"
onClicked: {
tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon));
}
}
PlasmaComponents.MenuItem {
id: moreActionsMenuItem
visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
enabled: visible
text: i18n("More")
icon: "view-more-symbolic"
PlasmaComponents.ContextMenu {
visualParent: moreActionsMenuItem.action
PlasmaComponents.MenuItem {
enabled: menu.visualParent && menu.get(atm.IsMovable) === true
text: i18n("&Move")
icon: "transform-move"
onClicked: tasksModel.requestMove(menu.modelIndex)
}
PlasmaComponents.MenuItem {
enabled: menu.visualParent && menu.get(atm.IsResizable) === true
text: i18n("Re&size")
icon: "transform-scale"
onClicked: tasksModel.requestResize(menu.modelIndex)
}
PlasmaComponents.MenuItem {
visible: (menu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
enabled: menu.visualParent && get(atm.IsMaximizable) === true
checkable: true
checked: menu.visualParent && get(atm.IsMaximized) === true
text: i18n("Ma&ximize")
icon: "window-maximize"
onClicked: tasksModel.requestToggleMaximized(modelIndex)
}
PlasmaComponents.MenuItem {
visible: (menu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
enabled: menu.visualParent && get(atm.IsMinimizable) === true
checkable: true
checked: menu.visualParent && get(atm.IsMinimized) === true
text: i18n("Mi&nimize")
icon: "window-minimize"
onClicked: tasksModel.requestToggleMinimized(modelIndex)
}
PlasmaComponents.MenuItem {
checkable: true
checked: menu.visualParent && menu.get(atm.IsKeepAbove) === true
text: i18n("Keep &Above Others")
icon: "window-keep-above"
onClicked: tasksModel.requestToggleKeepAbove(menu.modelIndex)
}
PlasmaComponents.MenuItem {
checkable: true
checked: menu.visualParent && menu.get(atm.IsKeepBelow) === true
text: i18n("Keep &Below Others")
icon: "window-keep-below"
onClicked: tasksModel.requestToggleKeepBelow(menu.modelIndex)
}
PlasmaComponents.MenuItem {
enabled: menu.visualParent && menu.get(atm.IsFullScreenable) === true
checkable: true
checked: menu.visualParent && menu.get(atm.IsFullScreen) === true
text: i18n("&Fullscreen")
icon: "view-fullscreen"
onClicked: tasksModel.requestToggleFullScreen(menu.modelIndex)
}
PlasmaComponents.MenuItem {
enabled: menu.visualParent && menu.get(atm.IsShadeable) === true
checkable: true
checked: menu.visualParent && menu.get(atm.IsShaded) === true
text: i18n("&Shade")
icon: "window-shade"
onClicked: tasksModel.requestToggleShaded(menu.modelIndex)
}
PlasmaComponents.MenuItem {
separator: true
}
PlasmaComponents.MenuItem {
visible: (plasmoid.configuration.groupingStrategy !== 0) && menu.get(atm.IsWindow) === true
checkable: true
checked: menu.visualParent && menu.get(atm.IsGroupable) === true
text: i18n("Allow this program to be grouped")
icon: "view-group"
onClicked: tasksModel.requestToggleGrouping(menu.modelIndex)
}
PlasmaComponents.MenuItem {
separator: true
}
PlasmaComponents.MenuItem {
property QtObject configureAction: null
enabled: configureAction && configureAction.enabled
visible: configureAction && configureAction.visible
text: configureAction ? configureAction.text : ""
icon: configureAction ? configureAction.icon : ""
onClicked: configureAction.trigger()
Component.onCompleted: configureAction = plasmoid.action("configure")
}
PlasmaComponents.MenuItem {
property QtObject alternativesAction: null
enabled: alternativesAction && alternativesAction.enabled
visible: alternativesAction && alternativesAction.visible
text: alternativesAction ? alternativesAction.text : ""
icon: alternativesAction ? alternativesAction.icon : ""
onClicked: alternativesAction.trigger()
Component.onCompleted: alternativesAction = plasmoid.action("alternatives")
}
}
}
PlasmaComponents.MenuItem { separator: true }
PlasmaComponents.MenuItem {
id: closeWindowItem
visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
enabled: visualParent && get(atm.IsClosable) === true
text: i18n("&Close")
icon: "window-close"
onClicked: tasksModel.requestClose(modelIndex)
}
}

View file

@ -58,6 +58,7 @@ MouseArea {
property real taskHeight: 0
property string previousState: ""
property bool rightClickDragging: false
property bool toolTipOpen: false
property Item audioStreamOverlay
property var audioStreams: []
@ -153,6 +154,7 @@ MouseArea {
}
hoverEnabled = true;
taskList.updateHoverFunc();
toolTipArea.tooltipClicked = true;
}
onContainsMouseChanged: {
@ -176,6 +178,7 @@ MouseArea {
hoverEnabled = true;
updateMousePosition(ma.mouseX);
toolTipArea.tooltipClicked = true;
}
@ -226,6 +229,9 @@ MouseArea {
if (plasmoid.configuration.showToolTips && toolTipArea.active) {
hideToolTipTemporarily();
}
/*if(childCount >= 2 && plasmoid.configuration.showToolTips && toolTipArea.active) {
toolTipArea.tooltipClicked = !toolTipArea.tooltipClicked;
}*/
TaskTools.activateTask(modelIndex(), model, mouse.modifiers, task);
} else if (mouse.button === Qt.BackButton || mouse.button === Qt.ForwardButton) {
@ -452,7 +458,7 @@ MouseArea {
opacity: 0.6
visible: childCount >= 3 ? true : false
anchors.rightMargin: PlasmaCore.Units.smallSpacing
enabledBorders: Plasma.FrameSvg.EnabledBorders.RightBorder
enabledBorders: PlasmaCore.FrameSvg.RightBorder
}
@ -711,8 +717,8 @@ MouseArea {
PlasmaCore.ToolTipArea {
id: toolTipArea
z: -1
//backgroundHints: "SolidBackground"
MouseArea {
MouseArea {
id: ma
hoverEnabled: true
propagateComposedEvents: true
@ -720,34 +726,32 @@ MouseArea {
onPositionChanged: {
task.updateMousePosition(ma.mouseX);
task.positionChanged(mouse);
//var xtr = toolTipArea.backgroundHints();
}
onContainsMouseChanged: {
task.updateMousePosition(ma.mouseX);
//task.onContainsMouseChanged();
//toolTipArea.onContainsMouseChanged();
//mouse.accepted = false;
}
onPressed: mouse.accepted = false;
onReleased: mouse.accepted = false;
onWheel: wheel.accepted = false;
//onExited: { hoverGradient.horizontalOffset = 0;
//task.onExited();
//}
}
anchors.fill: parent
location: plasmoid.location
active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips
property bool tooltipClicked: true
active: !inPopup && !groupDialog.visible && (plasmoid.configuration.showToolTips || tasks.toolTipOpenedByClick === toolTipArea)
interactive: model.IsWindow === true
mainItem: (model.IsWindow === true) ? openWindowToolTipDelegate : pinnedAppToolTipDelegate
property alias mainToolTip: toolTipArea.mainItem
onToolTipVisibleChanged: {
task.toolTipOpen = toolTipVisible;
if(!toolTipVisible) {
tasks.toolTipOpenedByClick = null;
} else {
tasks.toolTipAreaItem = toolTipArea;
}
}
onContainsMouseChanged: {
updateMousePosition(ma.mouseX);
if (containsMouse) {
mainItem.parentTask = task;
@ -802,6 +806,9 @@ MouseArea {
mainItem.smartLauncherCount = Qt.binding(function() {
return mainItem.smartLauncherCountVisible ? task.smartLauncherItem.count : 0;
});
tasks.toolTipAreaItem = toolTipArea;
} else {
tasks.toolTipOpenedByClick = null;
}
}
}

View file

@ -148,7 +148,11 @@ function activateTask(index, model, modifiers, task) {
else if (plasmoid.configuration.showToolTips
&& plasmoid.configuration.groupedTaskVisualization === 1
) {
task.showToolTip();
if(tasks.toolTipOpenedByClick) tasks.toolTipOpenedByClick.hideImmediately();
else {
tasks.toolTipOpenedByClick = task.toolTipAreaItem;
task.showToolTip();
}
}
// Option 3: show Present Windows for all child tasks

View file

@ -27,7 +27,8 @@ MouseArea {
// This property tells the plasmoid to render labels next to task icons.
// Previously, this property was determined by the value of (plasmoid.pluginName === "org.kde.plasma.icontasks")
property bool iconsOnly: !plasmoid.configuration.labelVisible
property var toolTipOpenedByClick: null
property var toolTipAreaItem: null
//property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml");
property QtObject tasksMenuComponent: Qt.createComponent("TasksMenu.qml");
property QtObject pulseAudioComponent: Qt.createComponent("PulseAudio.qml");
@ -60,6 +61,7 @@ MouseArea {
signal requestLayout
signal windowsHovered(variant winIds, bool hovered)
signal activateWindowView(variant winIds)
signal presentWindows(variant winIds)
states: State {
@ -511,10 +513,12 @@ MouseArea {
// With this, we can update each task icon pretty much globally.
function updateHoverFunc() {
for(var i = 0; i < taskRepeater.count; i++) {
taskRepeater.itemAt(i).updateHoverColor();
if(taskRepeater.itemAt(i)) {
taskRepeater.itemAt(i).updateHoverColor();
}
}
tasks.state = "";
console.log("Updated hovers");
//console.log("Updated hovers");
}
Timer {
@ -604,7 +608,8 @@ MouseArea {
tasks.requestLayout.connect(layoutTimer.restart);
tasks.requestLayout.connect(iconGeometryTimer.restart);
tasks.windowsHovered.connect(backend.windowsHovered);
tasks.presentWindows.connect(backend.presentWindows);
//tasks.presentWindows.connect(backend.presentWindows);
tasks.activateWindowView.connect(backend.activateWindowView);
dragHelper.dropped.connect(resetDragSource);
taskList.updateHoverFunc();
}

View file

@ -86,6 +86,7 @@ Lastly, feel free to fork this project and/or contribute to it in any way. Any a
<img src="Screenshots/Taskbar.png">
<img src="Screenshots/Taskbar_2.png">
<img src="Screenshots/Taskbar_3.png">
### Clock

BIN
Screenshots/Taskbar_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB