├── kwinOverview.png ├── kwinOverviewNew.png ├── metadata.desktop ├── contents ├── ui │ ├── ActivitiesContainer.qml │ ├── Search.qml │ ├── Desktops.qml │ ├── Clients.qml │ ├── ClientThumbnail.qml │ └── main.qml └── code │ └── createClients.js └── README.md /kwinOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astatide/kwinOverview/HEAD/kwinOverview.png -------------------------------------------------------------------------------- /kwinOverviewNew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astatide/kwinOverview/HEAD/kwinOverviewNew.png -------------------------------------------------------------------------------- /metadata.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=kwinOverview 3 | Comment=Overview for KWin as a QtQuick script. 4 | Icon=dashboard-show 5 | 6 | X-Plasma-API=declarativescript 7 | X-Plasma-MainScript=ui/main.qml 8 | X-Plasma-DefaultSize=1024,768 9 | 10 | X-KDE-PluginInfo-Author=A. J. Pratt 11 | X-KDE-PluginInfo-Email=ajp105@pitt.edu 12 | X-KDE-PluginInfo-Name=kwinOverview 13 | X-KDE-PluginInfo-Version=1.0 14 | 15 | X-KDE-PluginInfo-Depends= 16 | X-KDE-PluginInfo-License=GPL 17 | X-KDE-ServiceTypes=KWin/Script,Plasma/Applet 18 | Type=Service 19 | -------------------------------------------------------------------------------- /contents/ui/ActivitiesContainer.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | //import org.kde.kwin 2.0 as KWin 3 | //import org.kde.kwin 2.0 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 2.0 as Plasma 6 | 7 | // This is how we get the activity background. 8 | import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher 9 | import org.kde.activities 0.1 as Activities 10 | 11 | Repeater { 12 | id: activityRepeater 13 | // This will generate all of our activities. We can then iterate through 14 | // and get information about our activities at will. 15 | model: ActivitySwitcher.Backend.runningActivitiesModel() 16 | 17 | Item { 18 | // We just want our activities and background objects to display. 19 | id: newRepeater 20 | property string activityId: model.id 21 | property string background: model.background 22 | property bool isCurrent: model.isCurrent 23 | } 24 | 25 | // We're returning the current background image. 26 | // Useful for drawing various things. 27 | function getCurrentBackground() { 28 | var i; 29 | for (i = 0; i < activityRepeater.count; i++) { 30 | console.log('Testing activity model'); 31 | //console.log(model[i].id); 32 | console.log(Object.getOwnPropertyNames(activityRepeater.itemAt(i))); 33 | if (activityRepeater.itemAt(i).isCurrent == true) { 34 | console.log(activityRepeater.itemAt(i).background); 35 | return activityRepeater.itemAt(i).background; 36 | } 37 | } 38 | return ''; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | kwinOverview 2 | =============== 3 | 4 | In progress clone of GNOME's activities for KWin, as a QML/JS script (kwinOverview is not the final name). It owes a great debt to both GNOME Shell and Actos Dashboard (which doesn't work with KF5). It's sort of a spiritual successor to Actos Dashboard, and in fact reading through/tinkering with that code allowed me to learn enough QML/JS to write this. Written using QtQuick 2.7. Currently in beta, so expect bugs. Probably lots of them. Especially since this is the first QML/JS thing I've ever written. 5 | 6 | ![](kwinOverview.png) 7 | ![](kwinOverviewNew.png) 8 | 9 | ## Installation 10 | 11 | ``` 12 | git clone https://github.com/ajoshpratt/kwinOverview.git 13 | cd kwinOverview 14 | plasmapkg2 --type kwinscript -i . 15 | ``` 16 | 17 | ## Activation 18 | 19 | Like most other KWin scripts, it can be activated from the KDE system settings. The default registered shortcut is "Meta+A". 20 | 21 | ## Usage 22 | 23 | Once you've hit Meta+A, you can start typing to search for applications to launch. You can switch desktops by clicking/using the shortcut, and dragging/dropping applications to other desktops. You can click on another activity on the bottom dash to switch to that activity (hover over for the activity name). In addition, you can click and drag (or use the scroll wheel) the desktop dash above if there are more desktops than you have space available for! So it supports the maximum number of desktops you can have on Plasma. 24 | 25 | Hovering at the bottom of the screen brings the activity dock up (it then autohides after you move the mouse away), allowing you to switch between activities. 26 | 27 | Works incredibly well with https://github.com/rliang/kwin-dynamic-virtual-desktops, although there are now also buttons to add/remove desktops. You can still use the above mentioned script to handle your desktop creation/destruction. 28 | 29 | Both the auto hiding and buttons will be made 'optional'. 30 | 31 | ## Design Decisions 32 | 33 | * Mostly, it was designed to be fast on my aging macbook pro. The biggest slowdown comes from painting the active live thumbnails. If you find this to be slowing down your computer, change the thumbnail scaling quality in KWin's compositor settings. 34 | 35 | * In addition, I wanted to keep activities close but separate; hence why they're shown down below. 36 | 37 | * I've avoided using plasma components wherever possible (although it's still tied pretty heavily to activities) to ensure that regardless of what theme you're using, the components are still visible. 38 | 39 | * By using the KWin scripting interface, it doesn't actually rely on compositing! Although it really just shows the icon when compositing is disabled, so it's not THAT useful at the moment when that's the case. Perhaps I'll add some text for when compositing is disabled. 40 | 41 | * Although I like scaling animations as much as the next person (visually, they're easy to parse), I found implementing them slowed down even my desktop with more than two windows on the screen; something about the repaints for the thumbnails is slow. Hence why most of the animations are fading/sliding around. 42 | 43 | ## Known Bugs 44 | 45 | * Sometimes the 'type to start searching' function doesn't work on switching desktops. Simply hide/show the dashboard to regain focus (if you're using the default shortcut, just hit Meta+A twice). 46 | 47 | * Sometimes, swapping between activities 'hides' all the programs in the dashboard. Hide/show fixes this. 48 | 49 | * I suspect the thumbnails are still being painted even when the dashboard is hidden, increasing CPU usage for KWin. 50 | 51 | * Does not work on Wayland, AT ALL. Crashes KWin completely : ( I suspect this is because Activities themselves do not currently work on Wayland, and this makes heavy use of them. 52 | 53 | ## Future Directions 54 | 55 | It's mostly feature complete, although I'll probably tinker around with the design a bit. 56 | 57 | * Add a configuration dialog. In particular, I'd like to make sure the blur effects are configurable (I can see how those would slow down the computer, under certain circumstances). 58 | 59 | * Adjust how the grid configuration is done so that it takes more advantage of available screen space. 60 | 61 | * Reduce CPU usage. 62 | 63 | * Tinker with init/end animation timing. Ending feels a bit slow. 64 | 65 | * Remove the silly grid bouncing effect when windows are added. 66 | 67 | * Give it an actual name, instead of a working title. 68 | 69 | * Add buttons to add/remove desktops? 70 | 71 | * Cleanup the incredibly messy code. 72 | 73 | * Add a license? 74 | -------------------------------------------------------------------------------- /contents/code/createClients.js: -------------------------------------------------------------------------------- 1 | 2 | // This creates our new thumbnails. 3 | function createAllClientThumbnails(parentContainer, dashboard, columns, height, width, isLarge) { 4 | var c; // client 5 | for (c = 0; c < workspace.clientList().length; c++) { 6 | if (true) { 7 | // Don't make ones for docks. Or desktops. 8 | console.log('WHAT SORT ARE WE?'); 9 | console.log(workspace.clientList()[c].windowType); 10 | if (!workspace.clientList()[c].dock && workspace.clientList()[c].normalWindow) { 11 | //if (!workspace.clientList()[c].dock) { 12 | var clientThumbnail = Qt.createComponent('../ui/ClientThumbnail.qml') 13 | if( clientThumbnail.status == Component.Error ) 14 | console.debug("Error:"+ clientThumbnail.errorString() ); 15 | console.log('Attempting creation'); 16 | clientThumbnail.createObject(parentContainer, 17 | // Custom ID for destruction later. 18 | {id: 'clientId' + c, 19 | //'background': model.get(0).background, 20 | 'clientObject': workspace.clientList()[c], 21 | 'originalWidth': width / columns, 22 | 'originalHeight': height / columns, 23 | 'scale': (height / width) / (dashboard.screenHeight/dashboard.screenWidth), 24 | 'clientId': workspace.clientList()[c].windowId, 25 | // We'll use this to determine where to switch from. 26 | 'currentDesktop': workspace.clientList()[c].desktop, 27 | 'newDesktop': workspace.clientList()[c].desktop, 28 | 'isLarge': isLarge, 29 | 'newActivity': workspace.clientList()[c].activities, 30 | // We don't want to actually SHOW these, yet. 31 | // We'll just distribute them accordingly. 32 | 'visible': false, 33 | 'x': 0, 'y': 0, 34 | 'clientRealX': workspace.clientList()[c].x, 35 | // Account for the fucking dock, if any. 36 | 'clientRealY': workspace.clientList()[c].y, 37 | 'clientRealWidth': workspace.clientList()[c].width, 38 | 'clientRealHeight': workspace.clientList()[c].height, 39 | 'height': height / columns, 40 | 'width': width / columns}); 41 | console.log('Client created!'); 42 | } 43 | } 44 | } 45 | } 46 | // Here, we just create a new thumbnail as necessary. Later, we'll reparent it. 47 | function createNewClientThumbnails(parentContainer, dashboard, columns, height, width, isLarge, c) { 48 | //if (!c.dock) { 49 | console.log('WHAT SORT ARE WE?'); 50 | console.log(c.windowType, c.windowRole, c.desktopWindow, c.normalWindow); 51 | if (!c.dock && c.normalWindow) { 52 | //if (!c.dock) { 53 | var clientThumbnail = Qt.createComponent('../ui/ClientThumbnail.qml') 54 | if( clientThumbnail.status == Component.Error ) 55 | console.debug("Error:"+ clientThumbnail.errorString() ); 56 | clientThumbnail.createObject(parentContainer, 57 | // Custom ID for destruction later. 58 | {id: 'clientId' + c, 59 | //'background': model.get(0).background, 60 | 'clientObject': c, 61 | 'originalWidth': width / columns, 62 | 'originalHeight': height / columns, 63 | 'scale': (height / width) / (dashboard.screenHeight/dashboard.screenWidth), 64 | 'clientId': c.windowId, 65 | 'currentDesktop': c.desktop, 66 | 'newDesktop': c.desktop, 67 | 'isLarge': isLarge, 68 | 'newActivity': c.activities, 69 | // We don't want to actually SHOW these, yet. 70 | // We'll just distribute them accordingly. 71 | 'visible': false, 72 | 'x': 0, 'y': 0, 73 | 'clientRealX': c.x, 74 | // Account for the fucking dock, if any. 75 | 'clientRealY': c.y, 76 | 'clientRealWidth': c.width, 77 | 'clientRealHeight': c.height, 78 | 'height': height / columns, 79 | 'width': width / columns}); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /contents/ui/Search.qml: -------------------------------------------------------------------------------- 1 | // A lot of this was taken directly from the Search plasmoid shipped with 2 | // KDE, so all credit should go there. Therefore, that's also how the licensing 3 | // should be handled. 4 | 5 | import QtQuick 2.7 6 | import QtQuick.Layouts 1.1 7 | 8 | import QtQuick.Controls 1.4 9 | import QtQuick.Controls.Styles 1.4 10 | import org.kde.milou 0.1 as Milou 11 | 12 | import org.kde.plasma.components 2.0 as PlasmaComponents 13 | 14 | Item { 15 | id: mainSearch 16 | 17 | // Why aren't we using plasma? 18 | // 1. We're not a plasmoid. 19 | // 2. I would like this to be as 'portable' as possible, 20 | // to main the strength of KWin not being tied to Plasma. 21 | // While I already break that rule with the inclusion (and rather extensive) 22 | // use of activities, I do hope to be able to make that something optional. 23 | 24 | Rectangle { 25 | width: dashboard.screenWidth/2 26 | anchors.horizontalCenter: parent.horizontalCenter 27 | height: searchField.height 28 | opacity: 0.5 29 | color: 'black' 30 | } 31 | 32 | /*Keys.onPressed: { 33 | console.log(JSON.stringify(event.key)); 34 | console.log('WHEEEE'); 35 | searchField.forceActiveFocus(); 36 | //searchFieldAndResults.focus = true; 37 | //searchField.text = "" 38 | //currentDesktopGrid.visible = !currentDesktopGrid.visible; 39 | }*/ 40 | 41 | PlasmaComponents.TextField { 42 | id: searchField 43 | signal searchTextChanged() 44 | width: dashboard.screenWidth/2 45 | anchors.horizontalCenter: parent.horizontalCenter 46 | // I would LIKE for this to all work, but heeeey.... 47 | focus: true 48 | style: TextFieldStyle { 49 | textColor: '#a89984' 50 | font.bold: true 51 | background: Rectangle { 52 | //radius: 2 53 | opacity: 1 54 | color: 'transparent' 55 | implicitWidth: 100 56 | implicitHeight: 24 57 | //border.color: "#333" 58 | //border.width: 1 59 | /*Rectangle { 60 | color: 'black' 61 | border.color: 'black' 62 | radius: 1 63 | height: 1 64 | width: searchField.width 65 | anchors.top: parent.bottom 66 | }*/ 67 | } 68 | } 69 | /*onSearchTextChanged: { 70 | listView.setQueryString(text) 71 | }*/ 72 | Timer { 73 | id: timer 74 | interval: 200 75 | //onTriggered: TextField.searchTextChanged() 76 | onTriggered: { 77 | // Fade out the bigDesktopRepeater opacity 78 | console.log('yay!'); 79 | listView.setQueryString(searchField.text) 80 | // Not sure why this doesn't work, but hey. 81 | //currentDesktopGrid.visible = !currentDesktopGrid.visible; 82 | // This propery enables and disables the large grid clients. 83 | if (searchField.text == '') { 84 | mainSearch.visible = false; 85 | currentDesktopGridThumbnailContainer.state = 'showDesktop'; 86 | //searchField.focus = false; 87 | } else { 88 | mainSearch.visible = true; 89 | currentDesktopGridThumbnailContainer.state = 'showSearch'; 90 | } 91 | searchField.forceActiveFocus() 92 | } 93 | } 94 | 95 | onTextChanged: { 96 | if (searchField.text == '') { 97 | mainSearch.visible = false; 98 | //currentDesktopGridThumbnailContainer.state = 'showDesktop'; 99 | //searchField.focus = false; 100 | } else { 101 | mainSearch.visible = true; 102 | //currentDesktopGridThumbnailContainer.state = 'showSearch'; 103 | } 104 | timer.restart() 105 | } 106 | 107 | /*MouseArea { 108 | anchors.fill: parent 109 | onClicked: { 110 | // we really just want to make sure that when we click here, 111 | // our qml window has focus. 112 | //dashboard.requestActivate(); 113 | searchField.focus = true; 114 | } 115 | }*/ 116 | } 117 | 118 | // This is where the actual business happens. 119 | Milou.ResultsView { 120 | id: listView 121 | //in case is expanded 122 | //clip: true 123 | visible: true 124 | //height: 100 125 | //width: 100 126 | height: parent.height 127 | //width: parent.width 128 | width: dashboard.screenWidth/2 129 | anchors.horizontalCenter: parent.horizontalCenter 130 | anchors.top: searchField.bottom 131 | //anchors.left: parent.left 132 | anchors.verticalCenter: parent.verticalCenter 133 | 134 | onActivated: { 135 | searchField.text = ""; 136 | dashboard.toggleBoth(); 137 | //searchField.forceActiveFocus(); 138 | } 139 | } 140 | 141 | Component.onCompleted: { 142 | //searchField.setFocus(); 143 | } 144 | 145 | Keys.onEscapePressed: { 146 | //currentDesktopGridThumbnailContainer.state = 'showDesktop'; 147 | searchField.text = ""; 148 | //searchField.forceActiveFocus(); 149 | //currentDesktopGrid.visible = !currentDesktopGrid.visible; 150 | } 151 | 152 | Keys.forwardTo: listView 153 | 154 | /*Keys.onReturnPressed: { 155 | searchField.text = ''; 156 | }*/ 157 | 158 | 159 | } 160 | -------------------------------------------------------------------------------- /contents/ui/Desktops.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import org.kde.kwin 2.0 as KWinLib 3 | //import org.kde.kwin 2.0; 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 2.0 as Plasma 6 | 7 | // Trying to get the image provider to work. 8 | import org.kde.activities.settings 0.1 9 | import org.kde.plasma.components 2.0 as PlasmaComponents 10 | import org.kde.plasma.extras 2.0 as PlasmaExtras 11 | 12 | import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents 13 | 14 | import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher 15 | 16 | import org.kde.activities 0.1 as Activities 17 | 18 | Item { 19 | id: kwinDesktopThumbnailContainer 20 | x: 0 21 | y: 10 22 | property int desktop: 0 23 | property string background: '' 24 | property string activityId: '' 25 | property var activityModel: '' 26 | property bool isCurrent: true 27 | property var nClients: '' 28 | property var clients: '' 29 | 30 | Component.onCompleted: { 31 | //console.log(activityId); 32 | //console.log(Object.getOwnPropertyNames(activityModel)); 33 | if (isCurrent == false) { 34 | kwinDesktopThumbnailContainer.visible = false; 35 | } 36 | } 37 | 38 | // First, define the states. 39 | states: [ 40 | State { 41 | name: 'Picture' 42 | PropertyChanges { target: kwinDesktopThumbnail; visible: true } 43 | PropertyChanges { target: kwinDesktopName; visible: false } 44 | }, 45 | State { 46 | name: 'Text' 47 | PropertyChanges { target: kwinDesktopThumbnail; visible: false } 48 | PropertyChanges { target: kwinDesktopName; visible: true } 49 | } 50 | ] 51 | // Now, place in the mouse area. What happens when we click? Ultimately, nothing, but. 52 | 53 | MouseArea { 54 | id: mouseArea 55 | anchors.fill: parent 56 | //onClicked: kwinDesktopThumbnailContainer.state == 'Picture' ? kwinDesktopThumbnailContainer.state = "Text" : kwinDesktopThumbnailContainer.state = 'Picture'; 57 | onClicked: { 58 | if (workspace.currentDesktop != kwinDesktopThumbnailContainer.desktop+1) { 59 | workspace.currentDesktop = kwinDesktopThumbnailContainer.desktop+1; 60 | } else { 61 | dashboard.toggleBoth(); 62 | } 63 | } 64 | } 65 | 66 | // Now the actual items. On click, we'll flip back and forth from text to pictures. 67 | // Just as a test, anyway. 68 | 69 | // Let's just pull the background from the activity manager, since we have 70 | // access to it. We'll need it for displaying activities, anyway. 71 | Image { 72 | id: kwinDesktopThumbnail 73 | visible: true 74 | anchors.fill: parent 75 | source: "image://wallpaperthumbnail/" + background 76 | } 77 | 78 | Grid { 79 | id: clientGridLayout 80 | visible: true 81 | x: 0 82 | y: 0 83 | //anchors.fill: parent 84 | //rows: workspace.clientList().length 85 | anchors.verticalCenter: parent.verticalCenter 86 | rows: { return _returnMatrixSize() } 87 | // No order guaranteed, here. 88 | columns: { return _returnMatrixSize() } 89 | 90 | function _overlapsDesktop(x, y) { 91 | // Here, we're going to determine if we're in a new desktop. 92 | //console.log(workspace.currentDesktop); 93 | //console.log(x, y); 94 | // If we drag it out of the bar, send it to the current desktop. 95 | if (y > dash.gridHeight) { 96 | return workspace.currentDesktop; 97 | } 98 | for (var d = 1; d <= workspace.desktops; d++) { 99 | // We need to check if we're within the new bounds. That's height and width! 100 | // or just width, actually. 101 | // x and y are now global coordinates. 102 | // We have workspace.desktops, and our screen width is activeScreen.width 103 | //console.log(x, (d)*kwinDesktopThumbnailContainer.width + desktopThumbnailGridBackgrounds.width/(workspace.desktops) + dash.gridHeight*main.screenRatio, d); 104 | if (x < (d)*kwinDesktopThumbnailContainer.width + desktopThumbnailGridBackgrounds.width/(workspace.desktops) + dash.gridHeight*dashboard.screenRatio) { 105 | return d-1 106 | } 107 | //if (x > (d-1*width)+activeScreen.width/(2*workspace.desktops)) { 108 | // return d; 109 | //} 110 | } 111 | return 0; 112 | 113 | } 114 | 115 | function _onDesktop() { 116 | var c; 117 | var oD = 0; 118 | for (c = 0; c < workspace.clientList().length; c++) { 119 | if (workspace.clientList()[c].desktop-1 == desktop) { 120 | oD++; 121 | } 122 | } 123 | if (clientGridLayout.onDesktop == 0) { 124 | oD = 1; 125 | } 126 | return oD; 127 | } 128 | 129 | function _returnMatrixSize() { 130 | // Figure out how many we have on the desktop, then calculate an 131 | // an appropriate row x column size. 132 | var oD = _onDesktop(); 133 | // Just do it manually for the moment; not elegant, but effective. 134 | // Not sure what math library I'd need and I'm feeling lazy. 135 | if (oD <= 1) 136 | return 1 137 | if (oD <= 4) 138 | return 2 139 | if (oD <= 9) 140 | return 3 141 | if (oD <= 16) 142 | return 4 143 | if (oD < 25) 144 | return 5 145 | return 36 146 | } 147 | 148 | Repeater { 149 | // Now, we build up our windows. 150 | model: workspace.clientList().length 151 | Item { 152 | id: kwinClientThumbnail 153 | visible: true 154 | // We need to dynamically set these. 155 | property int originalWidth: kwinDesktopThumbnailContainer.width / clientGridLayout.columns 156 | property int originalHeight: kwinDesktopThumbnailContainer.height / clientGridLayout.columns 157 | // Setting the height/width seems to break EVERYTHING, as the thumbnails are busted. 158 | //width: kwinDesktopThumbnailContainer.width / clientGridLayout.columns 159 | //height: kwinDesktopThumbnailContainer.height / clientGridLayout.columns 160 | // Get our actual client information. This way, we can move through desktops/activities. 161 | property var clientObject: { workspace.clientList()[model.index] } 162 | //anchors.fill: parent 163 | 164 | opacity: 0 165 | 166 | property int originalX: 0 167 | property int originalY: 0 168 | x: 0 169 | y: 0 170 | 171 | property bool isHeld: false 172 | 173 | /*Text { 174 | //id: kwinDesktopName 175 | visible: true 176 | text: "Test" 177 | font.family: "Helvetica" 178 | font.pointSize: 12 179 | color: "red" 180 | // These two commands center the text in our parent box. 181 | anchors.verticalCenter: parent.verticalCenter 182 | horizontalAlignment: Text.AlignHCenter 183 | height: 12 184 | }*/ 185 | 186 | // Connect to the client signal. 187 | //signal desktopChanged: { return workspace.clientList()[model.index].desktopChanged } 188 | 189 | // Are THESE breaking it? What the shit. 190 | // These DO seem to break it! What the fuck. 191 | // Something about the way they're painted, maybe? Not so good. 192 | KWinLib.ThumbnailItem { 193 | id: actualThumbnail 194 | //anchors.verticalCenter: parent.verticalCenter 195 | anchors.fill: parent 196 | wId: workspace.clientList()[model.index].windowId 197 | //width: kwinClientThumbnail.width 198 | //height: kwinClientThumbnail.height 199 | //x: kwinClientThumbnail.x 200 | x: 0 201 | y: 0 202 | //y: kwinClientThumbnail.y 203 | visible: true 204 | clip: true 205 | } 206 | // I want this to show up if compositing is disabled. 207 | Rectangle { 208 | id: thumbnailBackgroundRectangle 209 | anchors.fill: parent 210 | //width: kwinClientThumbnail.width 211 | //height: kwinClientThumbnail.height 212 | color: 'black' 213 | opacity: 1 214 | visible: true 215 | //clip: true 216 | x: 0 217 | y: 0 218 | } 219 | // These don't really work yet, but hey. 220 | ParallelAnimation { 221 | id: growthAnim 222 | NumberAnimation { target: kwinClientThumbnail; property: "height"; from: originalHeight; to: originalHeight*2} 223 | NumberAnimation { target: kwinClientThumbnail; property: "width"; from: originalWidth; to: originalWidth*2} 224 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width*2} 225 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height*2} 226 | } 227 | ParallelAnimation { 228 | id: shrinkAnim 229 | NumberAnimation { target: kwinClientThumbnail; property: "height"; from: height; to: originalHeight} 230 | NumberAnimation { target: kwinClientThumbnail; property: "width"; from: width; to: originalWidth} 231 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width/2} 232 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height/2} 233 | } 234 | ParallelAnimation on width { 235 | id: newShrinkAnim 236 | NumberAnimation { target: kwinClientThumbnail; property: "height"; from: originalHeight; to: height} 237 | NumberAnimation { target: kwinClientThumbnail; property: "width"; from: originalWidth; to: width} 238 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width/2} 239 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height/2} 240 | } 241 | ParallelAnimation { 242 | id: returnAnim 243 | NumberAnimation { target: kwinClientThumbnail; property: "x"; from: x; to: kwinClientThumbnail.originalX} 244 | NumberAnimation { target: kwinClientThumbnail; property: "y"; from: y; to: kwinClientThumbnail.originalY} 245 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width/2} 246 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height/2} 247 | } 248 | ParallelAnimation { 249 | id: growFromNothing 250 | NumberAnimation { target: kwinClientThumbnail; property: "height"; from: 0; to: originalHeight} 251 | NumberAnimation { target: kwinClientThumbnail; property: "width"; from: 0; to: originalWidth} 252 | NumberAnimation { target: thumbnailBackgroundRectangle; property: "height"; from: 0; to: kwinClientThumbnail.originalHeight} 253 | NumberAnimation { target: thumbnailBackgroundRectangle; property: "width"; from: 0; to: kwinClientThumbnail.originalWidth} 254 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width*2} 255 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height*2} 256 | } 257 | ParallelAnimation { 258 | id: shrinkToNothing 259 | NumberAnimation { target: kwinClientThumbnail; property: "height"; from: height; to: 0} 260 | NumberAnimation { target: kwinClientThumbnail; property: "width"; from: width; to: 0} 261 | NumberAnimation { target: thumbnailBackgroundRectangle; property: "height"; from: height; to: 0} 262 | NumberAnimation { target: thumbnailBackgroundRectangle; property: "width"; from: width; to: 0} 263 | //NumberAnimation { target: actualThumbnail; property: "width"; from: width; to: width*2} 264 | //NumberAnimation { target: actualThumbnail; property: "height"; from: height; to: height*2} 265 | } 266 | 267 | 268 | MouseArea { 269 | id: mouseArea 270 | anchors.fill: parent 271 | drag.axis: 'XAndYAxis' 272 | drag.target: kwinClientThumbnail 273 | hoverEnabled: true 274 | //drag.maximumX: clientGridLayout.width 275 | //onClicked: { 276 | // console.log(mouse); 277 | // if (kwinClientThumbnail.isHeld == false) { 278 | // actualThumbnail.visible = !actualThumbnail.visible; 279 | // } 280 | //} 281 | onClicked: { 282 | workspace.activeClient = clientObject; 283 | } 284 | onPressed: { 285 | kwinClientThumbnail.originalX = kwinClientThumbnail.x; 286 | kwinClientThumbnail.originalY = kwinClientThumbnail.y; 287 | kwinClientThumbnail.isHeld = true; 288 | //growthAnim.running = true; 289 | } 290 | onReleased: { 291 | // We want to move it to the desktop. 292 | //console.log(Object.getOwnPropertyNames(mouse)); 293 | // These are where the mouse CLICKED on the object. Blah. 294 | var newDesktop = clientGridLayout._overlapsDesktop(kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).x, kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).y); 295 | //console.log(kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y)); 296 | console.log('New Desktop!'); 297 | console.log(newDesktop); 298 | // Hey, it works. Yay. 299 | //console.log(Object.getOwnPropertyNames(workspace)); 300 | //console.log(Object.getOwnPropertyNames(clientObject)); 301 | if (clientObject.desktop == newDesktop ) { 302 | //console.log(newDesktop); 303 | returnAnim.running = true; 304 | } else if (newDesktop == 0) { 305 | returnAnim.running = true; 306 | } else { 307 | clientObject.desktop = newDesktop; 308 | // We need to make it invisible, as well. 309 | kwinClientThumbnail.visible = false; 310 | returnAnim.running = true; 311 | // We want the others to pop up, so. 312 | } 313 | } 314 | } 315 | 316 | function setVisible() { 317 | if (workspace.clientList()[model.index].desktop-1 == desktop) { 318 | kwinClientThumbnail.visible = true; 319 | //actualThumbnail.visible = true; 320 | } else { 321 | kwinClientThumbnail.visible = false; 322 | //actualThumbnail.visible = false; 323 | } 324 | //growFromNothing.running = true; 325 | } 326 | 327 | Component.onCompleted: { 328 | // We just check to see whether we're on the current desktop. 329 | // If not, don't show it. 330 | setVisible(); 331 | // WHY DOES THIS FIX IT? 332 | shrinkToNothing.running = false; 333 | growFromNothing.running = true; 334 | workspace.clientList()[model.index].desktopChanged.connect(updateGrid); 335 | //workspace.currentDesktopChanged.connect(updateGrid); 336 | } 337 | 338 | function updateGrid(i, client) { 339 | // Probably won't work. 340 | // Don't change the desktop in this function! 341 | //kwinDesktopThumbnailContainer.desktop = workspace.currentDesktop-1; 342 | // But we actually need to rebuild the whole grid. Huh! 343 | clientGridLayout.rows = clientGridLayout._returnMatrixSize(); 344 | clientGridLayout.columns = clientGridLayout._returnMatrixSize(); 345 | setVisible(); 346 | // Hack for the moment. 347 | //currentDesktopGrid.updateGrid(); 348 | //width = kwinDesktopThumbnailContainer.width / clientGridLayout.columns; 349 | //height = kwinDesktopThumbnailContainer.height / clientGridLayout.columns; 350 | //clientGridLayout.height = dashboard.screenHeight - dash.gridHeight - 30 351 | //width: (dashboard.screenHeight - dash.gridHeight - 30)*dashboard.screenRatio 352 | //clientGridLayout.width = dashboard.screenWidth 353 | } 354 | } 355 | } 356 | } 357 | 358 | Text { 359 | id: kwinDesktopName 360 | visible: false 361 | text: "Test" 362 | font.family: "Helvetica" 363 | font.pointSize: 12 364 | color: "red" 365 | //x: kwinDesktopThumbnailContainer.width/2-kwinDesktopName.width/2 366 | //y: kwinDesktopThumbnailContainer.height/2-kwinDesktopName.height/2 367 | // These two commands center the text in our parent box. 368 | anchors.verticalCenter: parent.verticalCenter 369 | //anchors.horizontalCenter: parent.horizontalCenter 370 | //verticalAlignment: Text.AlignVCenter 371 | horizontalAlignment: Text.AlignHCenter 372 | height: 12 373 | width: kwinDesktopThumbnailContainer.width 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /contents/ui/Clients.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import org.kde.kwin 2.0 as KWinLib 3 | //import org.kde.kwin 2.0; 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 2.0 as Plasma 6 | 7 | // Trying to get the image provider to work. 8 | import org.kde.activities.settings 0.1 9 | import org.kde.plasma.components 2.0 as PlasmaComponents 10 | import org.kde.plasma.extras 2.0 as PlasmaExtras 11 | 12 | import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents 13 | 14 | import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher 15 | 16 | import org.kde.activities 0.1 as Activities 17 | 18 | Item { 19 | id: kwinDesktopThumbnailContainer 20 | property int desktop: 0 21 | property bool isMain: false 22 | property bool isLarge: false 23 | x: 0 24 | y: 0 25 | scale: 1 26 | 27 | Grid { 28 | id: clientGridLayout 29 | visible: true 30 | scale: 1 31 | 32 | anchors.verticalCenter: parent.verticalCenter 33 | anchors.horizontalCenter: parent.horizontalCenter 34 | horizontalItemAlignment: Grid.AlignHCenter 35 | verticalItemAlignment: Grid.AlignVCenter 36 | //rows: { return _returnMatrixSize() } 37 | // We dynamically update these. 38 | rows: 0 39 | columns: 0 40 | // No order guaranteed, here. 41 | //columns: { return _returnMatrixSize() } 42 | // These should apparently have their own thread. 43 | 44 | add: Transition { 45 | NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce } 46 | } 47 | 48 | move: Transition { 49 | NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce } 50 | } 51 | 52 | onRowsChanged: { 53 | testRows.start(); 54 | } 55 | NumberAnimation { id: testRows; property: "y"; duration: 400; easing.type: Easing.OutBounce } 56 | NumberAnimation on columns { property: "x"; duration: 400; easing.type: Easing.OutBounce } 57 | 58 | onChildrenChanged: { 59 | kwinDesktopThumbnailContainer.updateGrid(); 60 | } 61 | 62 | function _onDesktop() { 63 | /*var c; 64 | var oD = 0; 65 | for (c = 0; c < workspace.clientList().length; c++) { 66 | if (workspace.clientList()[c].desktop-1 == desktop && !workspace.clientList()[c].dock) { 67 | oD++; 68 | } 69 | } 70 | if (clientGridLayout.onDesktop == 0) { 71 | oD = 1; 72 | } 73 | return oD;*/ 74 | return clientGridLayout.children.length; 75 | } 76 | 77 | function _returnMatrixSize() { 78 | // Figure out how many we have on the desktop, then calculate an 79 | // an appropriate row x column size. 80 | var oD = _onDesktop(); 81 | // Just do it manually for the moment; not elegant, but effective. 82 | // Not sure what math library I'd need and I'm feeling lazy. 83 | if (oD <= 1) { 84 | return 1 85 | } else { 86 | return Math.ceil(Math.sqrt(oD)); 87 | } 88 | } 89 | 90 | Component.onCompleted: { 91 | //updateClients(); 92 | //kwinDesktopThumbnailContainer.updateGrid(); 93 | 94 | // Register for the state change. 95 | currentDesktopGridThumbnailContainer.onStateChanged.connect(function() { 96 | if (kwinDesktopThumbnailContainer.isLarge) { 97 | if (currentDesktopGridThumbnailContainer.state == 'showDesktop') { 98 | if (kwinDesktopThumbnailContainer.isMain) { 99 | kwinDesktopThumbnailContainer.visible = true; 100 | } 101 | } else { 102 | kwinDesktopThumbnailContainer.visible = false; 103 | } 104 | } 105 | }); 106 | 107 | // We do want to change when a client changes desktops, but. 108 | if (workspace.currentDesktop-1 == kwinDesktopThumbnailContainer.desktop) { 109 | if (kwinDesktopThumbnailContainer.isLarge) { 110 | kwinDesktopThumbnailContainer.isMain = true; 111 | kwinDesktopThumbnailContainer.visible = true; 112 | } else { 113 | // OH! But if we just change the scale method on lower end hardware, it's fine. 114 | kwinDesktopThumbnailContainer.isMain = true; 115 | kwinDesktopThumbnailContainer.visible = true; 116 | } 117 | } 118 | if (kwinDesktopThumbnailContainer.isLarge) { 119 | // If we're the main one, we actually just want to go invisible and let the other one in. 120 | workspace.currentDesktopChanged.connect(kwinDesktopThumbnailContainer.swapGrids); 121 | } else { 122 | // we want our old desktop to disappear and reappear 123 | //workspace.currentDesktopChanged.connect(kwinDesktopThumbnailContainer.hideGrids); 124 | } 125 | 126 | /*else { 127 | // If we're small, don't paint again. Turns out that's rather slow. 128 | // This is really just for performance reasons. 129 | workspace.currentDesktopChanged.connect(function() { 130 | if (workspace.currentDesktop-1 == kwinDesktopThumbnailContainer.desktop) { 131 | kwinDesktopThumbnailContainer.visible = false; 132 | } else { 133 | kwinDesktopThumbnailContainer.visible = true; 134 | } 135 | }); 136 | }*/ 137 | // The clients destroy/add themselves, so. 138 | //workspace.clientAdded.connect(kwinDesktopThumbnailContainer.updateGrid); 139 | //workspace.clientRemoved.connect(kwinDesktopThumbnailContainer.updateGrid); 140 | // We'll probably sort this out later, as well. 141 | //workspace.currentActivityChanged.connect(kwinDesktopThumbnailContainer.updateGrid); 142 | //workspace.currentActivityChanged.connect(kwinDesktopThumbnailContainer.updateGrid); 143 | } 144 | } 145 | 146 | PropertyAnimation { 147 | id: moveMainToLeft 148 | target: kwinDesktopThumbnailContainer 149 | //duration: 100 150 | running: false 151 | property: 'x' 152 | to: -dashboard.screenWidth 153 | from: 0 154 | easing.amplitude: 2 155 | easing.type: Easing.InOutQuad 156 | onStopped: { 157 | kwinDesktopThumbnailContainer.visible = false; 158 | } 159 | } 160 | PropertyAnimation { 161 | id: moveMainToRight 162 | target: kwinDesktopThumbnailContainer 163 | //duration: 100 164 | running: false 165 | property: 'x' 166 | to: dashboard.screenWidth 167 | from: 0 168 | easing.amplitude: 2 169 | easing.type: Easing.InOutQuad 170 | onStopped: { 171 | kwinDesktopThumbnailContainer.visible = false; 172 | } 173 | } 174 | PropertyAnimation { 175 | id: moveNewToLeft 176 | target: kwinDesktopThumbnailContainer 177 | //duration: 100 178 | property: 'x' 179 | running: false 180 | from: dashboard.screenWidth 181 | to: 0 182 | easing.amplitude: 2 183 | easing.type: Easing.InOutQuad 184 | } 185 | PropertyAnimation { 186 | id: moveNewToRight 187 | target: kwinDesktopThumbnailContainer 188 | //duration: 100 189 | property: 'x' 190 | running: false 191 | from: -dashboard.screenWidth 192 | to: 0 193 | easing.amplitude: 2 194 | easing.type: Easing.InOutQuad 195 | } 196 | Timer { 197 | id: makeVisibleTimer 198 | interval: 200 199 | onTriggered: { 200 | kwinDesktopThumbnailContainer.visible = true; 201 | } 202 | } 203 | 204 | function swapGrids(oldDesktop, newDesktop) { 205 | console.log('WHICH ONE IS WHICH!?'); 206 | console.log(oldDesktop, newDesktop); 207 | // If we're not the 'main', but we ARE current, we want to become visible and change our 208 | // x position (to the right or left, don't care right now), then animate a change to 0, 0. 209 | if (workspace.currentDesktop-1 == kwinDesktopThumbnailContainer.desktop) { 210 | if (!kwinDesktopThumbnailContainer.isMain) { 211 | // We need to know which way we're moving. But, ah, hmmm. 212 | // Which one is the old one? 213 | if (oldDesktop-1 < kwinDesktopThumbnailContainer.desktop) { 214 | moveNewToLeft.restart(); 215 | } else { 216 | moveNewToRight.restart(); 217 | } 218 | kwinDesktopThumbnailContainer.isMain = true; 219 | if (currentDesktopGridThumbnailContainer.state == 'showDesktop') { 220 | kwinDesktopThumbnailContainer.visible = true; 221 | } 222 | //kwinDesktopThumbnailContainer.x = -dashboard.screenWidth; 223 | } 224 | } 225 | if (isMain && workspace.currentDesktop-1 != kwinDesktopThumbnailContainer.desktop) { 226 | // Now, handle moving the OTHER one. 227 | if (workspace.currentDesktop-1 > kwinDesktopThumbnailContainer.desktop) { 228 | moveMainToLeft.restart(); 229 | } else { 230 | moveMainToRight.restart(); 231 | } 232 | kwinDesktopThumbnailContainer.isMain = false; 233 | //kwinDesktopThumbnailContainer.x = dashboard.screenWidth; 234 | } 235 | } 236 | 237 | function hideGrids(oldDesktop, newDesktop) { 238 | // If we're not the 'main', but we ARE current, we want to become visible and change our 239 | // x position (to the right or left, don't care right now), then animate a change to 0, 0. 240 | // Is this our new desktop? 241 | if (workspace.currentDesktop-1 == kwinDesktopThumbnailContainer.desktop) { 242 | kwinDesktopThumbnailContainer.visible = false; 243 | } 244 | // make the old one visible! 245 | if ((workspace.currentDesktop-1 != kwinDesktopThumbnailContainer.desktop) && (oldDesktop-1 == kwinDesktopThumbnailContainer.desktop)) { 246 | makeVisibleTimer.restart(); 247 | } 248 | } 249 | 250 | function updateGrid() { 251 | // Check how large our grid needs to be, then reparent on to our current grid. 252 | clientGridLayout.rows = clientGridLayout._returnMatrixSize(); 253 | clientGridLayout.columns = clientGridLayout._returnMatrixSize(); 254 | console.log('BEGIN: YOU SHOULD SEE THIS'); 255 | var c; 256 | var clientArea; 257 | //var clientWidth; 258 | //var clientHeight; 259 | clientArea = 0; 260 | //clientWidth = 0; 261 | //clientHeight = 0; 262 | for (c = 0; c < clientGridLayout.children.length; c++) { 263 | //console.log(clientArea); 264 | //console.log((clientGridLayout.children[c].clientWidth)); 265 | clientArea = clientArea + (clientGridLayout.children[c].clientRealWidth*clientGridLayout.children[c].clientRealHeight); 266 | //clientHeight = clientHeight + (clientGridLayout.children[c].clientRealHeight); 267 | //clientWidth = clientWidth + (clientGridLayout.children[c].clientRealWidth); 268 | } 269 | console.log('CLIENT AREA'); 270 | //console.log(clientArea); 271 | console.log(kwinDesktopThumbnailContainer.height*kwinDesktopThumbnailContainer.width); 272 | //console.log(clientArea/(kwinDesktopThumbnailContainer.height*kwinDesktopThumbnailContainer.width)); 273 | for (c = 0; c < clientGridLayout.children.length; c++) { 274 | //console.log(JSON.stringify(clientGridLayout.children[c].clientObject)); 275 | //var client = clientGridLayout.children[c]; 276 | //console.log('How many children do we have?'); 277 | //console.log(clientGridLayout.children.length); 278 | //console.log('How big are we?'); 279 | //console.log(clientGridLayout._returnMatrixSize()); 280 | //console.log(kwinDesktopThumbnailContainer.width, kwinDesktopThumbnailContainer.height); 281 | /*clientGridLayout.children[c].width = kwinDesktopThumbnailContainer.width / clientGridLayout._returnMatrixSize(); 282 | clientGridLayout.children[c].height = kwinDesktopThumbnailContainer.height / clientGridLayout._returnMatrixSize(); 283 | clientGridLayout.children[c].originalWidth = kwinDesktopThumbnailContainer.width / clientGridLayout._returnMatrixSize(); 284 | clientGridLayout.children[c].originalHeight = kwinDesktopThumbnailContainer.height / clientGridLayout._returnMatrixSize();*/ 285 | clientGridLayout.children[c].height = clientGridLayout.children[c].clientRealHeight * Math.sqrt((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/Math.sqrt(clientGridLayout._returnMatrixSize()); 286 | clientGridLayout.children[c].width = clientGridLayout.children[c].clientRealWidth * Math.sqrt((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/Math.sqrt(clientGridLayout._returnMatrixSize()); 287 | clientGridLayout.children[c].originalHeight = clientGridLayout.children[c].clientRealHeight * Math.sqrt((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/Math.sqrt(clientGridLayout._returnMatrixSize()); 288 | clientGridLayout.children[c].originalWidth = clientGridLayout.children[c].clientRealWidth * Math.sqrt((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/Math.sqrt(clientGridLayout._returnMatrixSize()); 289 | 290 | var mul = 0; 291 | if (clientGridLayout._onDesktop()/Math.pow(clientGridLayout._returnMatrixSize(),2) <= 1) { 292 | //if (clientGridLayout._onDesktop()/Math.pow(clientGridLayout._returnMatrixSize()-1,2) > 1) { 293 | // mul = clientGridLayout._returnMatrixSize()-1; 294 | // 295 | //} else { 296 | // mul = clientGridLayout._returnMatrixSize(); 297 | //} 298 | mul = Math.ceil(clientGridLayout._onDesktop()/clientGridLayout._returnMatrixSize()); 299 | } else { 300 | //mul = clientGridLayout._returnMatrixSize(); 301 | mul = clientGridLayout._returnMatrixSize(); 302 | } 303 | if (mul < 1) { 304 | mul = 1; 305 | } 306 | 307 | clientGridLayout.children[c].height = clientGridLayout.children[c].height * kwinDesktopThumbnailContainer.height/clientGridLayout.children[c].height/(mul);//clientGridLayout._onDesktop()); 308 | clientGridLayout.children[c].originalHeight = clientGridLayout.children[c].originalHeight * kwinDesktopThumbnailContainer.height/clientGridLayout.children[c].originalHeight/(mul);//clientGridLayout._onDesktop()); 309 | 310 | 311 | //clientGridLayout.children[c].height = clientGridLayout.children[c].height * kwinDesktopThumbnailContainer.height/clientGridLayout.children[c].height/(clientGridLayout._returnMatrixSize());//clientGridLayout._onDesktop()); 312 | //clientGridLayout.children[c].width = clientGridLayout.children[c].width * kwinDesktopThumbnailContainer.width/clientGridLayout.children[c].width/(clientGridLayout._returnMatrixSize()/clientGridLayout._onDesktop()); 313 | //clientGridLayout.children[c].originalHeight = clientGridLayout.children[c].originalHeight * kwinDesktopThumbnailContainer.height/clientGridLayout.children[c].originalHeight/(clientGridLayout._returnMatrixSize());//clientGridLayout._onDesktop()); 314 | //clientGridLayout.children[c].originalWidth = clientGridLayout.children[c].originalWidth * kwinDesktopThumbnailContainer.width/clientGridLayout.children[c].originalWidth/(clientGridLayout._returnMatrixSize()/clientGridLayout._onDesktop()); 315 | 316 | 317 | //clientGridLayout.children[c].height = clientGridLayout.children[c].clientRealHeight * ((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/kwinDesktopThumbnailContainer.width;//Math.sqrt(clientGridLayout._returnMatrixSize()); 318 | //clientGridLayout.children[c].width = clientGridLayout.children[c].clientRealWidth * ((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/kwinDesktopThumbnailContainer.height;//Math.sqrt(clientGridLayout._returnMatrixSize()); 319 | //clientGridLayout.children[c].originalHeight = clientGridLayout.children[c].clientRealHeight * ((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/kwinDesktopThumbnailContainer.width;//Math.sqrt(clientGridLayout._returnMatrixSize()); 320 | //clientGridLayout.children[c].originalWidth = clientGridLayout.children[c].clientRealWidth * ((kwinDesktopThumbnailContainer.width*kwinDesktopThumbnailContainer.height)/(clientArea))/kwinDesktopThumbnailContainer.height;//Math.sqrt(clientGridLayout._returnMatrixSize()); 321 | //clientGridLayout.children[c].height = clientGridLayout.children[c].clientRealWidth * ((kwinDesktopThumbnailContainer.height)/(clientWidth));//Math.sqrt(clientGridLayout._returnMatrixSize()); 322 | //clientGridLayout.children[c].width = clientGridLayout.children[c].clientRealHeight * ((kwinDesktopThumbnailContainer.width)/(clientHeight));//Math.sqrt(clientGridLayout._returnMatrixSize()); 323 | //clientGridLayout.children[c].originalHeight = clientGridLayout.children[c].clientRealWidth * ((kwinDesktopThumbnailContainer.height)/(clientWidth));//Math.sqrt(clientGridLayout._returnMatrixSize()); 324 | //clientGridLayout.children[c].originalWidth = clientGridLayout.children[c].clientRealHeight * ((kwinDesktopThumbnailContainer.width)/(clientHeight));//Math.sqrt(clientGridLayout._returnMatrixSize()); 325 | // Run the growth animation! 326 | console.log(clientGridLayout.children[c].originalWidth); 327 | clientGridLayout.children[c].runGrowthAnim(); 328 | //console.log(Object.getOwnPropertyNames(clientGridLayout.children[c])); 329 | } 330 | console.log('END: YOU SHOULD SEE THIS'); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /contents/ui/ClientThumbnail.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import org.kde.kwin 2.0 as KWinLib 3 | //import org.kde.kwin 2.0; 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 2.0 as Plasma 6 | 7 | // Trying to get the image provider to work. 8 | import org.kde.activities.settings 0.1 9 | import org.kde.plasma.components 2.0 as PlasmaComponents 10 | import org.kde.plasma.extras 2.0 as PlasmaExtras 11 | 12 | import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents 13 | 14 | import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher 15 | 16 | import org.kde.activities 0.1 as Activities 17 | 18 | Item { 19 | id: kwinClientThumbnail 20 | visible: true 21 | // We need to dynamically set these. 22 | //property int originalWidth: kwinDesktopThumbnailContainer.width / clientGridLayout.columns 23 | //property int originalHeight: kwinDesktopThumbnailContainer.height / clientGridLayout.columns 24 | property int originalWidth: 0 25 | property int originalHeight: 0 26 | // This seems to be necessary to set everything appropriately. Not 100% sure why. 27 | property int scale: 1 28 | property var originalParent: parent 29 | // Setting the height/width seems to break EVERYTHING, as the thumbnails are busted. 30 | //width: kwinDesktopThumbnailContainer.width / clientGridLayout.columns 31 | //height: kwinDesktopThumbnailContainer.height / clientGridLayout.columns 32 | // Get our actual client information. This way, we can move through desktops/activities. 33 | property var clientObject: '' 34 | property var clientId: 0 35 | property var currentDesktop: 0 36 | property var newDesktop: 0 37 | property var newActivity: 0 38 | Drag.active: mouseArea.drag.active 39 | //Drag.hotSpot: Qt.point(50,50) 40 | 41 | // Ha ha! 42 | opacity: 1 43 | 44 | // This is for moving the thumbnail back 45 | property int originalX: 0 46 | property int originalY: 0 47 | // This is for moving to our thumbnail position and size. 48 | property int clientRealX: 0 49 | property int clientRealY: 0 50 | property int clientRealWidth: 0 51 | property int clientRealHeight: 0 52 | x: 0 53 | y: 0 54 | z: 0 55 | property int originalZ: 0 56 | 57 | //property bool isHeld: false 58 | property bool isSmall: false 59 | property bool isLarge: false 60 | 61 | // For the mouse. 62 | property QtObject container 63 | 64 | states: [ 65 | State { 66 | name: 'isHeld' 67 | PropertyChanges { 68 | target: kwinClientThumbnail 69 | //parent: mainBackground 70 | z: 10000 71 | } 72 | }, 73 | State { 74 | name: 'notHeld' 75 | PropertyChanges { 76 | target: kwinClientThumbnail 77 | z: 0 78 | //parent: clientGridLayout 79 | } 80 | } 81 | ] 82 | Rectangle { 83 | //id: thumbnailBackgroundRectangle 84 | // This is a test rectangle. Ultimately, I'd like to show this when compositing is off. 85 | anchors.fill: parent 86 | //anchors.fill kwinClientThumbnail 87 | color: 'black' 88 | opacity: 0.5 89 | visible: false 90 | scale: 1 91 | clip: true 92 | //x: 0 93 | //y: 0 94 | //height: kwinClientThumbnail.height 95 | //width: kwinClientThumbnail.width 96 | // Are THESE breaking it? What the shit. 97 | // These DO seem to break it! What the fuck. 98 | // Something about the way they're painted, maybe? Not so good. 99 | // I think this is actually quite slow, but it's hard to say. Can I speed it up? 100 | } 101 | 102 | Item { 103 | id: actualThumbnail 104 | //anchors.fill kwinClientThumbnail 105 | height: kwinClientThumbnail.height 106 | width: kwinClientThumbnail.width 107 | visible: false 108 | opacity: 1 109 | //x: 0 110 | //y: 0 111 | /*Image { 112 | anchors.fill: parent 113 | smooth: true 114 | visible: true 115 | fillMode: Image.PreserveAspectCrop 116 | source: dashboardBackground.background 117 | height: kwinClientThumbnail.height 118 | width: kwinClientThumbnail.width 119 | x: 0 120 | y: 0 121 | }*/ 122 | Rectangle { 123 | //id: thumbnailBackgroundRectangle 124 | // This is a test rectangle. Ultimately, I'd like to show this when compositing is off. 125 | anchors.fill: parent 126 | //anchors.fill kwinClientThumbnail 127 | color: 'black' 128 | opacity: 0.5 129 | visible: false 130 | scale: 1 131 | clip: true 132 | //x: 0 133 | //y: 0 134 | //height: kwinClientThumbnail.height 135 | //width: kwinClientThumbnail.width 136 | // Are THESE breaking it? What the shit. 137 | // These DO seem to break it! What the fuck. 138 | // Something about the way they're painted, maybe? Not so good. 139 | // I think this is actually quite slow, but it's hard to say. Can I speed it up? 140 | } 141 | KWinLib.ThumbnailItem { 142 | // Basically, this 'fills up' to the parent object, so we encapsulate it 143 | // so that we can shrink the thumbnail without messing with the grid itself. 144 | id: kwinThumbnailRenderWindow 145 | anchors.fill: actualThumbnail 146 | //wId: workspace.clientList()[clientId].windowId 147 | wId: kwinClientThumbnail.clientId 148 | //height: kwinClientThumbnail.height 149 | //width: kwinClientThumbnail.width 150 | x: 0 151 | y: 0 152 | z: 0 153 | visible: false 154 | clip: false 155 | } 156 | } 157 | // These don't really work yet, but hey. 158 | ParallelAnimation { 159 | id: moveToThumbnail 160 | running: false 161 | NumberAnimation { 162 | //target: kwinClientThumbnail; 163 | target: actualThumbnail; 164 | property: "width"; 165 | from: clientRealWidth//*dashboard.screenWidth//currentDesktopGrid.width); 166 | to: kwinClientThumbnail.originalWidth; 167 | easing.amplitude: 2; 168 | easing.type: Easing.InOutQuad; 169 | //duration: 1000 170 | } 171 | NumberAnimation { 172 | //target: kwinClientThumbnail; 173 | target: actualThumbnail; 174 | property: "height"; 175 | //from: clientRealHeight//*(dashboard.screenHeight/currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].height); 176 | from: clientRealHeight//*dashboard.screenHeight///currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].height); 177 | to: kwinClientThumbnail.originalHeight; 178 | easing.amplitude: 2; 179 | easing.type: Easing.InOutQuad; 180 | //duration: 1000 181 | } 182 | } 183 | ParallelAnimation { 184 | id: moveFromThumbnail 185 | running: false 186 | NumberAnimation { 187 | //target: kwinClientThumbnail; 188 | target: actualThumbnail; 189 | property: "width"; 190 | to: clientRealWidth; 191 | from: kwinClientThumbnail.width; 192 | easing.amplitude: 2; 193 | easing.type: Easing.InOutQuad; 194 | //duration: 1000 195 | } 196 | NumberAnimation { 197 | //target: kwinClientThumbnail; 198 | target: actualThumbnail; 199 | property: "height"; 200 | to: clientRealHeight*(dashboard.screenHeight/currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].height); 201 | from: kwinClientThumbnail.height; 202 | easing.amplitude: 2; 203 | easing.type: Easing.InOutQuad; 204 | //duration: 1000 205 | } 206 | NumberAnimation { 207 | //target: kwinClientThumbnail; 208 | target: actualThumbnail; 209 | property: "x"; 210 | to: kwinClientThumbnail.mapFromGlobal(clientRealX, clientRealY).x-actualThumbnail.mapFromItem(kwinClientThumbnail.parent, x, y).x; 211 | from: kwinClientThumbnail.originalX; 212 | //to: 0 213 | easing.amplitude: 2; 214 | easing.type: Easing.InOutQuad; 215 | //duration: 1000 216 | } 217 | NumberAnimation { 218 | //target: kwinClientThumbnail; 219 | target: actualThumbnail; 220 | property: "y"; 221 | //from: clientRealY; 222 | // We want what the global coordinates of the client would be mapped to our thumbnail. 223 | // But why does this work when we're maximized, and not otherwise? 224 | // Our coordinate scales should be the same, so. 225 | to: kwinClientThumbnail.mapFromGlobal(clientRealX, clientRealY).y-actualThumbnail.mapFromItem(kwinClientThumbnail.parent, x, y).y; 226 | from: kwinClientThumbnail.originalY; 227 | //to: 0 228 | easing.amplitude: 2; 229 | easing.type: Easing.InOutQuad; 230 | //duration: 1000 231 | } 232 | } 233 | ParallelAnimation { 234 | id: returnAnim 235 | NumberAnimation { target: kwinClientThumbnail; property: "x"; from: x; to: kwinClientThumbnail.originalX} 236 | NumberAnimation { target: kwinClientThumbnail; property: "y"; from: y; to: kwinClientThumbnail.originalY} 237 | } 238 | 239 | ParallelAnimation { 240 | id: shrinkAnim 241 | alwaysRunToEnd: false 242 | running: false 243 | property int animX: 0 244 | property int animY: 0 245 | 246 | PropertyAnimation { 247 | target: actualThumbnail 248 | //target: kwinClientThumbnail 249 | property: "height" 250 | to: 100 251 | duration: 100 252 | } 253 | PropertyAnimation { 254 | target: actualThumbnail 255 | //target: kwinClientThumbnail 256 | property: "width" 257 | to: 100*dashboard.screenRatio 258 | duration: 100 259 | } 260 | 261 | PropertyAnimation { target: actualThumbnail; property: "x"; to: shrinkAnim.animX-(dash.gridHeight/2*dashboard.screenRatio); duration: 100} 262 | PropertyAnimation { target: actualThumbnail; property: "y"; to: shrinkAnim.animY-dash.gridHeight/2; duration: 100} 263 | //PropertyAnimation { target: kwinClientThumbnail; property: "x"; to: shrinkAnim.animX-(dash.gridHeight/2*dashboard.screenRatio); duration: 100} 264 | //PropertyAnimation { target: kwinClientThumbnail; property: "y"; to: shrinkAnim.animY-dash.gridHeight/2; duration: 100} 265 | //PropertyAnimation { target: kwinClientThumbnail; property: "x"; to: kwinClientThumbnail.x-shrinkAnim.animX; duration: 100} 266 | //PropertyAnimation { target: kwinClientThumbnail; property: "y"; to: kwinClientThumbnail.y-shrinkAnim.animY; duration: 100} 267 | 268 | onStopped: { 269 | mouseArea.enabled = true; 270 | kwinClientThumbnail.isSmall = true; 271 | //x = x+originalWidth/2; 272 | //y = y+originalHeight/2; 273 | } 274 | } 275 | ParallelAnimation { 276 | id: growthAnim 277 | alwaysRunToEnd: false 278 | running: false 279 | property int animX: 0 280 | property int animY: 0 281 | NumberAnimation { target: actualThumbnail; property: "height"; to: originalHeight; duration: 100} 282 | NumberAnimation { target: actualThumbnail; property: "width"; to: originalWidth; duration: 100} 283 | 284 | //PropertyAnimation { target: actualThumbnail; property: "x"; from: growthAnim.animX; to: 0; duration: 1000} 285 | //PropertyAnimation { target: actualThumbnail; property: "y"; from: growthAnim.animY; to: 0; duration: 1000} 286 | PropertyAnimation { target: actualThumbnail; property: "x"; to: 0; duration: 100} 287 | PropertyAnimation { target: actualThumbnail; property: "y"; to: 0; duration: 100} 288 | 289 | 290 | onStopped: { 291 | mouseArea.enabled = true; 292 | kwinClientThumbnail.isSmall = false; 293 | } 294 | } 295 | ParallelAnimation { 296 | id: moveAnim 297 | alwaysRunToEnd: false 298 | running: false 299 | property int animX: 0 300 | property int animY: 0 301 | PropertyAnimation { target: kwinClientThumbnail; property: "y"; to: moveAnim.animY; duration: 32; easing.amplitude: 2; easing.type: Easing.InOutQuad;} 302 | PropertyAnimation { target: kwinClientThumbnail; property: "x"; to: moveAnim.animX; duration: 32; easing.amplitude: 2; easing.type: Easing.InOutQuad;} 303 | onStopped: { 304 | mouseArea.enabled = true; 305 | //kwinClientThumbnail.isSmall = false; 306 | } 307 | } 308 | ParallelAnimation { 309 | id: initMoveAnim 310 | alwaysRunToEnd: false 311 | running: false 312 | property int animX: 0 313 | property int animY: 0 314 | PropertyAnimation { target: kwinClientThumbnail; property: "y"; to: moveAnim.animY; duration: 100; easing.amplitude: 2; easing.type: Easing.InOutQuad;} 315 | PropertyAnimation { target: kwinClientThumbnail; property: "x"; to: moveAnim.animX; duration: 100; easing.amplitude: 2; easing.type: Easing.InOutQuad;} 316 | onStopped: { 317 | mouseArea.enabled = true; 318 | //kwinClientThumbnail.isSmall = false; 319 | } 320 | } 321 | 322 | MouseArea { 323 | id: mouseArea 324 | anchors.fill: parent 325 | drag.axis: 'XAndYAxis' 326 | drag.target: kwinClientThumbnail 327 | //drag.active: true 328 | hoverEnabled: true 329 | property bool dragActive: drag.active 330 | onClicked: { 331 | // We only want to disable the dashboard when we double click on the item 332 | // or when we're currently on said desktop and are 'sure'. 333 | if (currentDesktop == workspace.currentDesktop) { 334 | dashboard.toggleBoth(); 335 | } 336 | workspace.activeClient = clientObject; 337 | } 338 | 339 | onPositionChanged: { 340 | // Let's do this proper. 341 | //console.log('CHANGING POSITIONS'); 342 | var ranAnimation = false; 343 | var mouseX = mouse.x; 344 | var mouseY = mouse.y; 345 | shrinkAnim.animX = mouseX; 346 | shrinkAnim.animY = mouseY; 347 | growthAnim.animX = mouseX; 348 | growthAnim.animY = mouseY; 349 | //console.log(Drag.hotSpot); 350 | //if (actualThumbnail.height != dash.gridHeight) { 351 | if (kwinClientThumbnail.state == 'isHeld') { 352 | shrinkAnim.restart() 353 | } 354 | 355 | /*if (kwinClientThumbnail.state == 'isHeld') { 356 | if (kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).y < dash.gridHeight + 30) { 357 | if (actualThumbnail.height != dash.gridHeight) { 358 | shrinkAnim.restart() 359 | ranAnimation = true; 360 | } 361 | } else if (kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).y > dash.gridHeight + 30) { 362 | if (actualThumbnail.height != originalHeight) { 363 | kwinClientThumbnail.isSmall = false; 364 | ranAnimation = true; 365 | growthAnim.restart(); 366 | } 367 | } 368 | }*/ 369 | } 370 | 371 | onPressed: { 372 | // Sets things up for the return animation. 373 | kwinClientThumbnail.originalX = kwinClientThumbnail.x; 374 | kwinClientThumbnail.originalY = kwinClientThumbnail.y; 375 | kwinClientThumbnail.originalZ = kwinClientThumbnail.z; 376 | kwinClientThumbnail.clientObject.keepAbove = true; 377 | // So doesn't work. 378 | kwinClientThumbnail.z = 1000; 379 | kwinClientThumbnail.state = 'isHeld'; 380 | // This works! 381 | //kwinClientThumbnail.Drag.hotSpot.x = mouse.x-(dash.gridHeight/2*dashboard.screenRatio); 382 | //kwinClientThumbnail.Drag.hotSpot.y = mouse.y-dash.gridHeight/2; 383 | kwinClientThumbnail.Drag.hotSpot.x = mouse.x; 384 | kwinClientThumbnail.Drag.hotSpot.y = mouse.y; 385 | kwinClientThumbnail.newDesktop = kwinClientThumbnail.currentDesktop; 386 | kwinClientThumbnail.currentDesktop = kwinClientThumbnail.newDesktop; 387 | } 388 | onReleased: { 389 | kwinClientThumbnail.state = 'notHeld'; 390 | kwinClientThumbnail.clientObject.keepAbove = false; 391 | console.log(Drag.drop()); 392 | console.log('TESTING'); 393 | console.log(kwinClientThumbnail.newDesktop); 394 | console.log(kwinClientThumbnail.newActivity); 395 | // Let's see if the dropArea can handle this. 396 | //var newDesktop = _overlapsDesktop(kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).x, kwinClientThumbnail.mapToGlobal(mouse.x, mouse.y).y); 397 | //var newDesktop = 0; 398 | //growthAnim.animX = kwinClientThumbnail.mapToGlobal(mouseX, mouseY).x; 399 | //growthAnim.animY = kwinClientThumbnail.mapToGlobal(mouseX, mouseY).y; 400 | if (kwinClientThumbnail.isSmall) { 401 | //growthAnim.running = true; 402 | growthAnim.restart(); 403 | kwinClientThumbnail.isSmall = false; 404 | } 405 | if (kwinClientThumbnail.clientObject.activities != kwinClientThumbnail.newActivity) { 406 | //kwinClientThumbnail.clientObject.setActivity(kwinClientThumbnail.newActivity); 407 | // This is a read-only property, and so we're unable to change it from here. 408 | // Not sure if there's a model out there that would let us do it. 409 | //console.log(Object.getOwnPropertyNames(kwinClientThumbnail.clientObject)); 410 | //kwinClientThumbnail.clientObject.activities = kwinClientThumbnail.newActivity; 411 | // for now, since we can't sort it. 412 | var activityModel = console.log(Object.getOwnPropertyNames(Activities.ResourceInstance)); 413 | console.log(kwinClientThumbnail.clientObject.activities); 414 | returnAnim.running = true; 415 | } else if (kwinClientThumbnail.clientObject.desktop == kwinClientThumbnail.newDesktop ) { 416 | //console.log(newDesktop); 417 | returnAnim.running = true; 418 | //growthAnim.running = true; 419 | } else if (newDesktop == 0) { 420 | returnAnim.running = true; 421 | //growthAnim.running = true; 422 | } else { 423 | kwinClientThumbnail.currentDesktop = kwinClientThumbnail.newDesktop; 424 | kwinClientThumbnail.clientObject.desktop = kwinClientThumbnail.newDesktop; 425 | // We need to make it invisible, as well. 426 | //kwinClientThumbnail.visible = false; 427 | returnAnim.running = true; 428 | kwinClientThumbnail.z = kwinClientThumbnail.originalZ; 429 | // We want the others to pop up, so. 430 | } 431 | } 432 | } 433 | 434 | function runAnimations() { 435 | if (visible) { 436 | if (mainBackground.state == 'visible') { 437 | if (kwinClientThumbnail.originalX == 0) { 438 | if (kwinClientThumbnail.originalY == 0) { 439 | // Why does this hack work? Can I make it a copy? 440 | // WHERE is it fucking updating!? 441 | kwinClientThumbnail.originalX = kwinClientThumbnail.x; 442 | kwinClientThumbnail.originalY = kwinClientThumbnail.y; 443 | } 444 | } 445 | moveToThumbnail.running = true; 446 | } else { 447 | moveFromThumbnail.running = true; 448 | } 449 | } 450 | } 451 | 452 | Component.onCompleted: { 453 | // We just check to see whether we're on the current desktop. 454 | // If not, don't show it. 455 | moveToThumbnail.running = false; 456 | moveFromThumbnail.running = false; 457 | growthAnim.running = false; 458 | shrinkAnim.running = false; 459 | clientObject.desktopChanged.connect(callUpdateGrid); 460 | clientObject.activitiesChanged.connect(callUpdateGrid); 461 | // It seems that occasionally, this might not fire off. Unsure as to why. 462 | workspace.currentActivityChanged.connect(callUpdateGrid); 463 | mainBackground.onStateChanged.connect(toggleVisible); 464 | // We just need to make sure we're calling correct parent signals when 465 | // the desktop changes. This avoids crashes upon creating/removing new desktops! 466 | workspace.numberDesktopsChanged.connect(callUpdateGrid); 467 | workspace.clientRemoved.connect(disconnectAllSignals); 468 | callUpdateGrid(); 469 | searchFieldAndResults.children[1].forceActiveFocus(); 470 | } 471 | 472 | function disconnectAllSignals(c) { 473 | console.log(c); 474 | if (c) { 475 | if (c.windowId == kwinClientThumbnail.clientId) { 476 | console.log('KILLING MYSELF'); 477 | // Yes, we even have to disconnect this. 478 | workspace.clientRemoved.disconnect(disconnectAllSignals); 479 | workspace.numberDesktopsChanged.disconnect(callUpdateGrid); 480 | mainBackground.onStateChanged.disconnect(toggleVisible); 481 | clientObject.desktopChanged.disconnect(callUpdateGrid); 482 | clientObject.activitiesChanged.disconnect(callUpdateGrid); 483 | workspace.currentActivityChanged.disconnect(callUpdateGrid); 484 | kwinClientThumbnail.destroy(); 485 | } 486 | } 487 | } 488 | 489 | 490 | function toggleVisible(state) { 491 | if (state == 'visible') { 492 | //runMoveToThumbnailAnim(); 493 | // only toggle the ones on the current activity. 494 | if (kwinClientThumbnail.clientObject.activities == workspace.currentActivity || kwinClientThumbnail.clientObject.activities == '') { 495 | kwinThumbnailRenderWindow.wId = kwinClientThumbnail.clientId; 496 | actualThumbnail.visible = true; 497 | kwinThumbnailRenderWindow.visible = true; 498 | kwinThumbnailRenderWindow.enabled = true; 499 | kwinClientThumbnail.visible = true; 500 | } 501 | } else if (state == 'invisible') { 502 | //} else { 503 | // break it for now. 504 | kwinThumbnailRenderWindow.wId = -1; 505 | actualThumbnail.visible = false; 506 | kwinThumbnailRenderWindow.visible = false; 507 | kwinThumbnailRenderWindow.enabled = false; 508 | kwinClientThumbnail.visible = false; 509 | } 510 | } 511 | 512 | function resizeToLarge(){ 513 | kwinClientThumbnail.width = clientRealWidth; 514 | kwinClientThumbnail.height = clientRealHeight; 515 | kwinClientThumbnail.x = actualThumbnail.mapFromGlobal(clientRealX, clientRealY).x; 516 | kwinClientThumbnail.y = actualThumbnail.mapFromGlobal(clientRealX, clientRealY).y; 517 | } 518 | 519 | function callUpdateGrid() { 520 | // It seems that when we move a large to a small and vice versa, we don't 521 | // always properly trigger updates. 522 | // Actually, it seems we don't update our new parent properly. WHAT. 523 | if (kwinClientThumbnail.isLarge) { 524 | //reparent ourselves to the new desktop item 525 | // it's always going to be the desktop. 526 | //console.log('LARGE DESKTOP!'); 527 | console.log('TESTING ACTIVITIES'); 528 | if (kwinClientThumbnail.clientObject.activities == workspace.currentActivity || kwinClientThumbnail.clientObject.activities == '') { 529 | //if (kwinClientThumbnail.clientObject.desktop > -1 && !kwinClientThumbnail.clientObject.dock) { 530 | if (kwinClientThumbnail.clientObject.desktop > -1) { 531 | // Reparent, then resize all the appropriate grids. 532 | // But also check to make sure we're on the correct activity. 533 | kwinClientThumbnail.parent = currentDesktopGrid.itemAt(kwinClientThumbnail.clientObject.desktop-1).children[0].children[0]; 534 | kwinClientThumbnail.currentDesktop = kwinClientThumbnail.clientObject.desktop; 535 | //kwinClientThumbnail.visible = true; 536 | //actualThumbnail.visible = true; 537 | kwinThumbnailRenderWindow.wId = kwinClientThumbnail.clientId; 538 | actualThumbnail.visible = true; 539 | kwinThumbnailRenderWindow.visible = true; 540 | kwinThumbnailRenderWindow.enabled = true; 541 | kwinClientThumbnail.visible = true; 542 | } 543 | } else { 544 | // Go back to being in the original parent widget. 545 | kwinClientThumbnail.parent = currentDesktopGridThumbnailContainer; 546 | kwinClientThumbnail.visible = false; 547 | actualThumbnail.visible = false; 548 | } 549 | } else { 550 | //console.log('SMALL DESKTOP!'); 551 | if (kwinClientThumbnail.clientObject.activities == workspace.currentActivity || kwinClientThumbnail.clientObject.activities == '') { 552 | //if (kwinClientThumbnail.clientObject.desktop > -1 && !kwinClientThumbnail.clientObject.dock) { 553 | if (kwinClientThumbnail.clientObject.desktop > -1) { 554 | // Reparent, then resize all the appropriate grids. 555 | kwinClientThumbnail.currentDesktop = kwinClientThumbnail.clientObject.desktop; 556 | //kwinClientThumbnail.visible = true; 557 | //actualThumbnail.visible = true; 558 | //if (kwinClientThumbnail.clientObject.desktop != workspace.currentDesktop) { 559 | kwinThumbnailRenderWindow.wId = kwinClientThumbnail.clientId; 560 | actualThumbnail.visible = true; 561 | kwinThumbnailRenderWindow.visible = true; 562 | kwinThumbnailRenderWindow.enabled = true; 563 | kwinClientThumbnail.visible = true; 564 | /*} else { 565 | kwinThumbnailRenderWindow.wId = -1; 566 | actualThumbnail.visible = false; 567 | kwinThumbnailRenderWindow.visible = false; 568 | kwinThumbnailRenderWindow.enabled = false; 569 | kwinClientThumbnail.visible = false; 570 | }*/ 571 | kwinClientThumbnail.parent = littleDesktopRepeater.itemAt(kwinClientThumbnail.clientObject.desktop-1).children[2].children[0]; 572 | } 573 | } else { 574 | console.log('REPARENTING'); 575 | console.log('MAKE ME INVISIBLE'); 576 | kwinClientThumbnail.visible = false; 577 | actualThumbnail.visible = false; 578 | kwinClientThumbnail.parent = desktopThumbnailGrid; 579 | } 580 | } 581 | searchFieldAndResults.children[1].forceActiveFocus(); 582 | } 583 | 584 | function _overlapsDesktop(x, y) { 585 | // Here, we're going to determine if we're in a new desktop. 586 | //console.log(x, y); 587 | // If we drag it out of the bar, send it to the current desktop. 588 | if (y > dash.gridHeight) { 589 | return workspace.currentDesktop; 590 | } 591 | for (var d = 0; d <= workspace.desktops; d++) { 592 | // We need to check if we're within the new bounds. That's height and width! 593 | // or just width, actually. 594 | // x and y are now global coordinates. 595 | if (x < d*(dash.gridHeight*dashboard.screenRatio)) { 596 | // We have workspace.desktops, and our screen width is activeScreen.width 597 | //console.log(x, (d)*kwinDesktopThumbnailContainer.width + desktopThumbnailGridBackgrounds.width/(workspace.desktops) + dash.gridHeight*main.screenRatio, d); 598 | //if (x < (d)*kwinDesktopThumbnailContainer.width + desktopThumbnailGridBackgrounds.width/(workspace.desktops) + dash.gridHeight*dashboard.screenRatio) { 599 | return d 600 | } 601 | //if (x > (d-1*width)+activeScreen.width/(2*workspace.desktops)) { 602 | // return d; 603 | } 604 | return 0; 605 | } 606 | 607 | function runGrowthAnim() { 608 | // We set the global mouse position when we released the item. 609 | // This is called when we reparent. Now, we want to change our global mouse 610 | // coordinates to the local coordinates. 611 | //growthAnim.animX = kwinClientThumbnail.mapFromGlobal(growthAnim.animX, growthAnim.animY).x; 612 | //growthAnim.animY = kwinClientThumbnail.mapFromGlobal(growthAnim.animX, growthAnim.animY).y; 613 | growthAnim.restart(); 614 | } 615 | function runMoveToThumbnailAnim() { 616 | // We set the global mouse position when we released the item. 617 | // This is called when we reparent. Now, we want to change our global mouse 618 | // coordinates to the local coordinates. 619 | //growthAnim.animX = kwinClientThumbnail.mapFromGlobal(growthAnim.animX, growthAnim.animY).x; 620 | //growthAnim.animY = kwinClientThumbnail.mapFromGlobal(growthAnim.animX, growthAnim.animY).y; 621 | if (isLarge) { 622 | //if (kwinClientThumbnail.parent.isMain) { 623 | if (kwinClientThumbnail.currentDesktop == workspace.currentDesktop) { 624 | //if (!currentDesktopGrid.itemAt(kwinClientThumbnail.clientObject.desktop-1).children[0].children[0].isMain) { 625 | // This is too slow. 626 | //moveToThumbnail.restart(); 627 | } 628 | } 629 | } 630 | 631 | } 632 | -------------------------------------------------------------------------------- /contents/ui/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Window 2.2 3 | //import org.kde.kwin 2.0 as KWin 4 | import org.kde.kwin 2.0 as KWinLib 5 | //import org.kde.kwin 2.0 6 | import org.kde.plasma.core 2.0 as PlasmaCore 7 | import org.kde.plasma.components 2.0 as Plasma 8 | 9 | // This is how we get the activity background. 10 | import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher 11 | import org.kde.activities 0.1 as Activities 12 | //import org.kde.plasma.extras 2.0 as PlasmaExtras 13 | 14 | // Let's use some blur! 15 | import QtGraphicalEffects 1.0 16 | 17 | import "../code/createClients.js" as CreateClients 18 | 19 | //BLAH 20 | 21 | // dashboard background. Holds window thumbnails. I hope. 22 | Window { 23 | id: dashboard 24 | opacity: 1 25 | flags: Qt.X11BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint // won't work without it, apparently. 26 | visible: true 27 | x: 0 28 | y: 0 29 | color: 'black' 30 | // Start disabled. toggleBoth sets this appropriately. 31 | height: 0 32 | width: 0 33 | property var windowId: 0 34 | //width: { activeScreen.width } 35 | //height: { return activeScreen.height + _getDockHeight() } 36 | /*property int dockHeight: { return _getDockHeight() } 37 | property var activeScreen: { workspace.clientArea(KWinLib.MaximizedArea, workspace.activeScreen, workspace.currentDesktop) } 38 | //property var activeScreen: { workspace.clientArea(MaximizedArea, workspace.activeScreen, workspace.currentDesktop) } 39 | property int screenWidth: { activeScreen.width } 40 | property int screenHeight: { return activeScreen.height + _getDockHeight() } 41 | property var screenRatio: { activeScreen.width/activeScreen.height } 42 | property int dockHeight: { return _getDockHeight() }*/ 43 | property var activeScreen: 0 44 | //property var activeScreen: { workspace.clientArea(MaximizedArea, workspace.activeScreen, workspace.currentDesktop) } 45 | property int screenWidth: 0 46 | property int screenHeight: 0 47 | property var screenRatio: 0 48 | property int dockHeight: 0 49 | 50 | property var clientsVisible: { new Array } 51 | 52 | function _getDockHeight() { 53 | // We have to account for any docks. Which is a hassle, but eh. 54 | var c; 55 | var dockHeight = 0; 56 | for (c = 0; c < workspace.clientList().length; c++) { 57 | //console.log(workspace.clientList()[c].dock) 58 | if (workspace.clientList()[c].dock) { 59 | dockHeight = dockHeight + workspace.clientList()[c].height; 60 | } 61 | } 62 | return dockHeight; 63 | } 64 | 65 | Item { 66 | id: mainBackground 67 | width: dashboard.screenWidth 68 | anchors.fill: parent 69 | focus: true 70 | //height: main.screenHeight 71 | height: dashboard.screenHeight 72 | x: 0 73 | y: 0 74 | //visible: true 75 | opacity: 1 76 | 77 | /*Keys.onPressed: { 78 | console.log(event.key); 79 | console.log('WHEEEE'); 80 | //searchFieldAndResults.focus = true; 81 | //searchField.text = "" 82 | //currentDesktopGrid.visible = !currentDesktopGrid.visible; 83 | }*/ 84 | 85 | // This creates a mouseArea for the rectangle. We change the height of our dock. 86 | MouseArea { 87 | anchors.fill: parent 88 | enabled: true 89 | onClicked: { 90 | /*if (mainBackground.state == 'visible') { 91 | //endAnim.running = true; 92 | //dashboardBackground.visible = false; 93 | //dashboard.visible = false; 94 | mainBackground.state = 'invisible'; 95 | } else if (mainBackground.state == 'invisible') { 96 | //initAnim.running = true; 97 | //dashboardBackground.visible = true; 98 | //dashboard.visible = true; 99 | mainBackground.state = 'visible'; 100 | }*/ 101 | toggleBoth(); 102 | //dashboard.visible = false; 103 | } 104 | } 105 | 106 | // First, we create some states: visible, and otherwise. 107 | states: [ 108 | State { 109 | name: 'visible' 110 | //PropertyChanges { target: dashboard; visible: true } 111 | }, 112 | State { 113 | name: 'invisible' 114 | //PropertyChanges { target: dashboard; visible: false } 115 | } 116 | ] 117 | NumberAnimation { id: fadeToBlack; running: false; alwaysRunToEnd: true; target: foregroundDarken; property: "opacity"; to: 1; from: 0} 118 | NumberAnimation { id: fadeFromBlack; running: false; alwaysRunToEnd: true; target: foregroundDarken; property: "opacity"; to: 0; from: 1} 119 | // These are just temporary to get it out of my way. We'll change them later. 120 | ParallelAnimation { 121 | id: initAnim 122 | NumberAnimation { target: dash; property: "y"; to: 0} 123 | NumberAnimation { target: dashboard; property: "opacity"; to: 1; from: 0} 124 | //NumberAnimation { target: dashboard; property: "opacity"; to: 1; from: dashboard.opacity} 125 | //NumberAnimation { target: dashboardBackground; property: "opacity"; to: 1; from: dashboard.opacity} 126 | // Expensive! 127 | SequentialAnimation { 128 | ParallelAnimation { 129 | NumberAnimation { target: blurBackground; property: "radius"; to: 32; from: 1} 130 | //NumberAnimation { target: mainBackground; property: "opacity"; to: 1; from: mainBackground.opacity} 131 | NumberAnimation { target: backgroundDarken; property: "opacity"; to: 0.5; from: 0} 132 | //NumberAnimation { target: secondBackgroundDesktop; property: "opacity"; to: 1; from: 0} 133 | } 134 | // move in the main background and items. 135 | NumberAnimation { 136 | target: currentDesktopGridThumbnailContainer 137 | property: "y" 138 | //to: dashboard.screenHeight - dash.gridHeight - 30 139 | to: 15 140 | from: dashboard.screenHeight 141 | } 142 | } 143 | 144 | 145 | 146 | } 147 | ParallelAnimation { 148 | id: endAnim 149 | SequentialAnimation { 150 | NumberAnimation { 151 | target: currentDesktopGridThumbnailContainer 152 | property: "y" 153 | //to: dashboard.screenHeight - dash.gridHeight - 30 154 | from: 15 155 | to: dashboard.screenHeight 156 | duration: 100 157 | } 158 | ParallelAnimation { 159 | NumberAnimation { target: dashboard; property: "opacity"; to: 0; from: 1; duration: 100} 160 | NumberAnimation { target: dash; property: "y"; to: -dash.gridHeight; duration: 100} 161 | NumberAnimation { target: backgroundDarken; property: "opacity"; to: 0; from: 0.5; duration: 100} 162 | // Cheaper! 163 | //NumberAnimation { target: secondBackgroundDesktop; property: "opacity"; to: 0; from: 1} 164 | // Not so cheap, probably! 165 | NumberAnimation { target: blurBackground; property: "radius"; to: 1; from: 32; duration: 100} 166 | NumberAnimation { target: backgroundDarken; property: "opacity"; from: 0.5; to: 0; duration: 100} 167 | } 168 | } 169 | //NumberAnimation { target: dashboard; property: "opacity"; to: 0; from: dashboard.opacity} 170 | //NumberAnimation { target: dashboardBackground; property: "opacity"; to: 0; from: dashboard.opacity} 171 | //NumberAnimation { target: mainBackground; property: "opacity"; to: 0; from: mainBackground.opacity} 172 | //NumberAnimation { target: backgroundDarken; property: "opacity"; to: 0; from: backgroundDarken.opacity} 173 | 174 | onRunningChanged: { 175 | if (!endAnim.running) { 176 | dashboard.height = 0; 177 | dashboard.width = 0; 178 | mainBackground.state = 'invisible'; 179 | //dashboard.visible = false; 180 | mainBackground.visible = false; 181 | } 182 | } 183 | } 184 | 185 | ActivitiesContainer { 186 | // Instantiate our activity container. 187 | id: allActivities 188 | } 189 | 190 | Item { 191 | //PlasmaCore.Dialog { 192 | id: dashboardBackground 193 | //visible: true 194 | anchors.fill: parent 195 | height: dashboard.screenHeight 196 | width: dashboard.screenWidth 197 | //clip: true 198 | //blah: blah 199 | //property var model: ActivitySwitcher.Backend.runningActivitiesModel() 200 | property string background: { return allActivities.getCurrentBackground() } 201 | x: 0 202 | y: 0 203 | //z: 0 204 | //z: 100 205 | Image { 206 | id: firstBackgroundDesktop 207 | anchors.fill: parent 208 | smooth: true 209 | //clip: true 210 | //visible: true 211 | //source: "wallhaven-567367.jpg" 212 | //source: "image://wallpaperthumbnail/" + dashboardBackground.background 213 | fillMode: Image.PreserveAspectCrop 214 | source: dashboardBackground.background 215 | height: dashboard.screenHeight 216 | width: dashboard.screenWidth 217 | opacity: 1 218 | // Maybe? 219 | asynchronous: true 220 | cache: false 221 | } 222 | 223 | Image { 224 | id: secondBackgroundDesktop 225 | anchors.fill: parent 226 | smooth: true 227 | //clip: true 228 | //visible: true 229 | //source: "wallhaven-567367.jpg" 230 | //source: "image://wallpaperthumbnail/" + dashboardBackground.background 231 | fillMode: Image.PreserveAspectCrop 232 | source: dashboardBackground.background 233 | height: dashboard.screenHeight 234 | width: dashboard.screenWidth 235 | opacity: 0 236 | // Maybe? 237 | asynchronous: true 238 | cache: false 239 | } 240 | 241 | // Doesn't seem to like this. 242 | FastBlur { 243 | id: blurBackground 244 | anchors.fill: secondBackgroundDesktop 245 | source: secondBackgroundDesktop 246 | radius: 32 247 | } 248 | 249 | Rectangle { 250 | anchors.fill: parent 251 | id: backgroundDarken 252 | //visible: true 253 | opacity: 0.5 254 | //clip: true 255 | color: 'black' 256 | height: dashboard.screenHeight 257 | width: dashboard.screenWidth 258 | } 259 | //} 260 | 261 | // Here, we're going to build up the desktops and thumbnails. 262 | // For each entry here, we want nDesktops in the first row, and one in the second. 263 | Item { 264 | id: dash 265 | //visible: true 266 | opacity: 1 267 | 268 | // When y is set, we start this animation. This gracefully moves the dock into position, ignoring the whole 'slide' thing. 269 | width: dashboard.screenWidth 270 | //height: main.screenHeight 271 | height: 100 272 | property int gridHeight: 100 273 | y: 0 274 | anchors.fill: mainBackground 275 | //anchors.fill: parent 276 | 277 | Rectangle { 278 | opacity: 0.5 279 | //visible: dashboard.visible 280 | height: dash.height + 20 281 | width: dashboard.screenWidth 282 | color: 'black' 283 | } 284 | // This is where we'll build up the grid. I like to think, anyway. 285 | 286 | 287 | // This seems pretty nice, honestly. 288 | Flickable { 289 | id: desktopThumbnailGrid 290 | anchors.top: parent.top 291 | anchors.left: parent.left 292 | anchors.right: parent.right 293 | anchors.leftMargin: 5 294 | //anchors.fill: parent 295 | 296 | property int spacing: 10 297 | height: dash.gridHeight 298 | contentHeight: desktopThumbnailGridBackgrounds.height 299 | contentWidth: desktopThumbnailGridBackgrounds.width 300 | //boundsMovement: Flickable.StopAtBounds 301 | //boundsBehavior: Flickable.DragAndOvershootBounds 302 | 303 | //visible: true 304 | //x: 10 305 | y: 0 306 | // This is our active rectangle. We shift it when our active desktop changes. 307 | Rectangle { 308 | id: activeDesktopIndicator 309 | // We just want this to stand out, currently. 310 | color: 'white' 311 | opacity: 1 312 | visible: true 313 | scale: 1 314 | clip: true 315 | //x: -2 316 | x: ((dash.gridHeight*dashboard.screenRatio+desktopThumbnailGrid.spacing)*(workspace.currentDesktop-1)) - 2 317 | y: 8 318 | //height: desktopThumbnailGridBackgrounds.height 319 | //width: desktopThumbnailGridBackgrounds.width 320 | height: desktopThumbnailGrid.height+4 321 | width: (dash.gridHeight*dashboard.screenRatio)+4 322 | } 323 | PropertyAnimation { 324 | id: activeDesktopIndicatorShiftAnim 325 | target: activeDesktopIndicator 326 | property: 'x' 327 | running: false 328 | property var originalX: 0 329 | property var newX: 0 330 | from: activeDesktopIndicatorShiftAnim.originalX 331 | to: activeDesktopIndicatorShiftAnim.newX 332 | duration: 1000 333 | //easing.type: Easing.OutBounce 334 | } 335 | Grid { 336 | // This is just for each of our desktops. 337 | //id: newRepeater 338 | id: desktopThumbnailGridBackgrounds 339 | rows: 1 340 | x: 0 341 | y: 10 342 | spacing: desktopThumbnailGrid.spacing 343 | //anchors.fill: parent 344 | 345 | columns: { 346 | if (workspace.desktops <= 6 ) { 347 | //desktopThumbnailGridBackgrounds.columns = 6; 348 | return 6; 349 | } else { 350 | //desktopThumbnailGridBackgrounds.columns = workspace.desktops; 351 | return workspace.desktops; 352 | } 353 | } 354 | Repeater { 355 | // Now, we build up our desktops. 356 | //model: dashboard.returnNumberOfDesktops() 357 | model: workspace.desktops 358 | id: littleDesktopRepeater 359 | Item { 360 | id: littleDesktopContainer 361 | visible: true 362 | property int desktop: model.index 363 | height: desktopThumbnailGrid.height 364 | width: dash.gridHeight*dashboard.screenRatio 365 | Image { 366 | id: littleDesktopBackground 367 | //anchors.fill: dashboard 368 | //anchors.fill: parent 369 | //smooth: true 370 | // Better scaling 371 | mipmap: true 372 | //border { left: 30; top: 30; right: 30; bottom: 30 } 373 | //horizontalTileMode: BorderImage.Stretch 374 | //verticalTileMode: BorderImage.Stretch 375 | //clip: true 376 | //visible: true 377 | //source: "wallhaven-567367.jpg" 378 | //source: "image://wallpaperthumbnail/" + dashboardBackground.background 379 | fillMode: Image.PreserveAspectCrop 380 | source: dashboardBackground.background 381 | //height: dashboard.screenHeight 382 | //width: dashboard.screenWidth 383 | height: desktopThumbnailGrid.height 384 | width: dash.gridHeight*dashboard.screenRatio 385 | x: 0 386 | y: 0 387 | //brightness: 2 388 | // Maybe? 389 | //asynchronous: true 390 | //cache: false 391 | } 392 | MouseArea { 393 | id: littleDesktopGridMouseArea 394 | anchors.fill: parent 395 | //drag.axis: 'XAndYAxis' 396 | //drag.target: kwinClientThumbnail 397 | //hoverEnabled: true 398 | onClicked: { 399 | // We only want to disable the dashboard when we double click on the item 400 | // or when we're currently on said desktop and are 'sure'. 401 | if (littleDesktopContainer.desktop != workspace.currentDesktop-1) { 402 | workspace.currentDesktop = littleDesktopContainer.desktop+1; 403 | } else { 404 | /*if (mainBackground.state == 'visible') { 405 | mainBackground.state = 'invisible'; 406 | } else if (mainBackground.state == 'invisible') { 407 | mainBackground.state = 'visible'; 408 | }*/ 409 | toggleBoth(); 410 | } 411 | } 412 | } 413 | Clients { 414 | //anchors.fill: parent 415 | id: littleDesktopGrid 416 | desktop: littleDesktopContainer.desktop 417 | //x: dash.gridHeight*.025*dashboard.screenRatio 418 | //y: dash.gridHeight*.025 419 | height: dash.gridHeight 420 | width: dash.gridHeight*dashboard.screenRatio 421 | //height: dashboard.screenHeight - dash.gridHeight - 30 422 | //width: dashboard.screenWidth 423 | } 424 | // Can we use this? 425 | DropArea { 426 | id: littleDesktopDropArea 427 | //anchors.fill: littleDesktopBackground 428 | anchors.fill: parent 429 | x: 0 430 | y: 0 431 | height: desktopThumbnailGrid.height 432 | width: dash.gridHeight*dashboard.screenRatio 433 | // Doesn't really seem to work. 434 | /*onDropped: { 435 | drag.drop.accept(); 436 | console.log('DROPPING!'); 437 | //console.log(drag.source); 438 | //drag.source.newDesktop = model.index+1; 439 | //console.log(drag.source.newDesktop); 440 | }*/ 441 | Rectangle { 442 | anchors.fill: parent 443 | visible: false 444 | color: "green" 445 | } 446 | onEntered: { 447 | console.log('ENTERING!'); 448 | //console.log(Object.getOwnPropertyNames(drag.source)); 449 | drag.source.newDesktop = littleDesktopContainer.desktop+1; 450 | console.log(drag.source.newDesktop); 451 | } 452 | onExited: { 453 | console.log('LEAVING'); 454 | drag.source.newDesktop = drag.source.currentDesktop; 455 | console.log(drag.source.newDesktop); 456 | } 457 | } 458 | } 459 | } 460 | /*Component.onCompleted: { 461 | workspace.numberDesktopsChanged.connect(function () { 462 | littleDesktopRepeater.model = workspace.desktops; 463 | }); 464 | }*/ 465 | } 466 | } 467 | Item { 468 | id: dashAddRemoveDesktopButtons 469 | y: 0 470 | x: dashboard.screenWidth-50 471 | Rectangle { 472 | height: 120 473 | width: 50 474 | //color: 'black' 475 | color: 'transparent' 476 | opacity: 0.5 477 | LinearGradient { 478 | anchors.fill: parent 479 | start: Qt.point(0, 0) 480 | end: Qt.point(25, 0) 481 | gradient: Gradient { 482 | GradientStop { position: 0.0; color: 'transparent' } 483 | GradientStop { position: 1.0; color: 'black' } 484 | } 485 | } 486 | } 487 | Rectangle { 488 | id: plusButton 489 | //opacity: 0.5 490 | //visible: dashboard.visible 491 | height: 60 492 | width: 50 493 | //color: 'white' 494 | color: 'transparent' 495 | Text { 496 | id: actualPlusButton 497 | anchors.horizontalCenter: parent.horizontalCenter 498 | anchors.verticalCenter: parent.verticalCenter 499 | opacity: 1 500 | text: "+" 501 | font.pointSize: 25 502 | font.bold: true 503 | color: "white" 504 | y: 0 505 | } 506 | MouseArea { 507 | anchors.fill: parent 508 | id: plusButtonMouseArea 509 | onPressed: { 510 | console.log('yay'); 511 | actualPlusButton.color = 'grey'; 512 | } 513 | onReleased: { 514 | actualPlusButton.color = 'white'; 515 | if (workspace.desktops < 20) { 516 | workspace.desktops = workspace.desktops + 1; 517 | } 518 | } 519 | } 520 | } 521 | Rectangle { 522 | id: minusButton 523 | anchors.top: plusButton.bottom 524 | height: 60 525 | width: 50 526 | //color: 'white' 527 | y: 60 528 | color: 'transparent' 529 | Text { 530 | id: actualMinusButton 531 | anchors.horizontalCenter: parent.horizontalCenter 532 | anchors.verticalCenter: parent.verticalCenter 533 | opacity: 1 534 | text: "-" 535 | font.pointSize: 25 536 | font.bold: true 537 | color: "white" 538 | y: 0 539 | } 540 | MouseArea { 541 | anchors.fill: parent 542 | id: minusButtonMouseArea 543 | onPressed: { 544 | console.log('yay'); 545 | actualMinusButton.color = 'grey'; 546 | } 547 | onReleased: { 548 | actualMinusButton.color = 'white'; 549 | if (workspace.desktops > 1) { 550 | workspace.desktops = workspace.desktops - 1; 551 | } 552 | } 553 | } 554 | } 555 | } 556 | } 557 | 558 | // We'll create our normal desktop windows here with the same code. 559 | // Just a little bit of tinkering should work. 560 | 561 | // For the current desktop, build a grid. 562 | // Easy, but really quite slow. 563 | Item { 564 | id: currentDesktopGridThumbnailContainer 565 | y: dashboard.screenHeight 566 | //contentHeight: desktopThumbnailGridBackgrounds.height 567 | //contentWidth: desktopThumbnailGridBackgrounds.width 568 | //contentHeight: (dashboard.screenHeight - dash.gridHeight - 30) 569 | //contentWidth: dashboard.screenWidth 570 | //anchors.fill: parent 571 | //height: (dashboard.screenHeight - dash.height - 30) 572 | //y: 15 573 | states: [ 574 | State { 575 | name: 'showSearch' 576 | //PropertyChanges { target: dashboard; visible: true } 577 | }, 578 | State { 579 | name: 'showDesktop' 580 | //PropertyChanges { target: dashboard; visible: false } 581 | } 582 | ] 583 | // Instantiate the search container. 584 | Search { 585 | id: searchFieldAndResults 586 | // Don't show until we start typing. 587 | visible: false 588 | y: dash.height + 30 589 | //x: 0 590 | height: (dashboard.screenHeight - dash.height - 30 - 15) 591 | width: dashboard.screenWidth 592 | property int textHeight: 24 593 | anchors.left: parent.left 594 | //anchors.left: dash.left 595 | //anchors.verticalCenter: parent.verticalCenter 596 | //anchors.horizontalCenter: parent.horizontalCenter 597 | } 598 | Repeater { 599 | // Now, we build up our desktops. 600 | //model: dashboard.returnNumberOfDesktops() 601 | //model: KWin.Switcher.model 602 | model: workspace.desktops 603 | id: currentDesktopGrid 604 | // We're leaving a little room for the text search area! 605 | height: (dashboard.screenHeight - dash.height - 30 - 15 - searchFieldAndResults.textHeight) 606 | width: dashboard.screenWidth 607 | scale: 1 608 | visible: true 609 | Item { 610 | id: bigDesktopRepeater 611 | //id: bigDesktopContainer 612 | visible: true 613 | property int desktop: model.index 614 | //height: dashboard.screenHeight - dash.gridHeight - 30 615 | //width: dashboard.screenWidth 616 | Clients { 617 | id: bigDesktopClients 618 | desktop: bigDesktopRepeater.desktop 619 | visible: false 620 | x: 0 621 | // Leave a little room for the text! 622 | y: dash.height + 30 + searchFieldAndResults.textHeight 623 | //anchor.verticalCenter: currentDesktopGridThumbnailContainer.verticalCenter 624 | //scale: 1 625 | height: (dashboard.screenHeight - dash.height - 30 - activitySwitcherDash.height - 30) 626 | width: dashboard.screenWidth 627 | isMain: false 628 | isLarge: true 629 | DropArea { 630 | id: bigDesktopDropArea 631 | //anchors.fill: littleDesktopBackground 632 | anchors.fill: parent 633 | //x: 0 634 | //y: dash.height + 30 635 | //height: (dashboard.screenHeight - dash.height - 30 - activitySwitcherDash.height - 30) 636 | //width: dashboard.screenWidth 637 | Rectangle { 638 | anchors.fill: parent 639 | visible: false 640 | color: "green" 641 | } 642 | onEntered: { 643 | console.log('ENTERING LARGE DESKTOP!'); 644 | //console.log(Object.getOwnPropertyNames(drag.source)); 645 | drag.source.newDesktop = bigDesktopRepeater.desktop+1; 646 | console.log(drag.source.newDesktop); 647 | } 648 | onExited: { 649 | console.log('LEAVING'); 650 | drag.source.newDesktop = drag.source.currentDesktop; 651 | console.log(drag.source.newDesktop); 652 | } 653 | } 654 | } 655 | } 656 | } 657 | Item { 658 | id: activitySwitcherDash 659 | //anchors.verticalCenter: parent.verticalCenter 660 | //anchors.horizontalCenter: parent.horizontalCenter 661 | //anchors.bottom: parent.top 662 | //anchors.top: currentDesktopGrid.bottom 663 | //anchors.topMargin: 40 + 24 664 | x: 0 665 | width: dashboard.screenWidth 666 | //x: screenWidth/2-blahBlahBlah.width 667 | //y: 124 668 | //anchors.bottom: mainBackground.bottom 669 | //y: (dashboard.screenHeight - 80) 670 | scale: 1 671 | //y: dashboard.screenHeight 672 | //y: dashboard.screenHeight - dash.height + 10 673 | height: 100 674 | //y: 90 675 | y: dashboard.screenHeight - 20 676 | property int gridHeight: 80 677 | NumberAnimation { 678 | id: showActivitySwitcherDashAnim 679 | running: false 680 | target: activitySwitcherDash 681 | property: "y" 682 | to: dashboard.screenHeight - 105 683 | } 684 | NumberAnimation { 685 | id: hideActivitySwitcherDashAnim 686 | running: false 687 | target: activitySwitcherDash 688 | property: "y" 689 | to: dashboard.screenHeight - 20 690 | } 691 | Timer { 692 | id: activitySwitcherDashTimer 693 | interval: 500 694 | onTriggered: { 695 | hideActivitySwitcherDashAnim.restart(); 696 | } 697 | } 698 | Rectangle { 699 | id: activitySwitcherDashBackground 700 | scale: 1 701 | opacity: 0.5 702 | y: 0 703 | x: 0 704 | height: activitySwitcherDash.height 705 | width: dashboard.screenWidth 706 | color: 'black' 707 | } 708 | MouseArea { 709 | id: activitySwitcherDashMouseArea 710 | anchors.fill: parent 711 | enabled: true 712 | hoverEnabled: true 713 | onEntered: { 714 | showActivitySwitcherDashAnim.restart(); 715 | } 716 | onExited: { 717 | activitySwitcherDashTimer.restart(); 718 | } 719 | } 720 | Grid { 721 | id: activitySwitcherRepeaterGrid 722 | //anchors.fill: parent 723 | anchors.verticalCenter: parent.verticalCenter 724 | anchors.horizontalCenter: parent.horizontalCenter 725 | anchors.top: activitySwitcherDashBackground.top 726 | anchors.topMargin: 5 727 | rows: 1 728 | columns: 10 729 | spacing: desktopThumbnailGrid.spacing 730 | visible: true 731 | Repeater { 732 | id: activitySwitcherRepeater 733 | model: ActivitySwitcher.Backend.runningActivitiesModel() 734 | Item { 735 | x: 0 736 | y: 0 737 | visible: true 738 | height: activitySwitcherDash.gridHeight 739 | width: activitySwitcherDash.gridHeight*dashboard.screenRatio 740 | Image { 741 | id: activityThumbnail 742 | anchors.fill: parent 743 | //smooth: true 744 | mipmap: true 745 | fillMode: Image.PreserveAspectCrop 746 | source: model.background 747 | opacity: 1 748 | // Maybe? 749 | asynchronous: true 750 | cache: false 751 | } 752 | Rectangle { 753 | id: activityThumbnailBlackRectangle 754 | scale: 1 755 | opacity: 0 756 | //y: 0 757 | //x: 0 758 | anchors.top: activityThumbnail.top 759 | //height: 20 760 | height: activityThumbnail.height 761 | width: activityThumbnail.width 762 | color: 'black' 763 | } 764 | Text { 765 | //anchors.fill: parent 766 | id: activityThumbnailTitleText 767 | //anchors.top: activityThumbnail.top 768 | anchors.horizontalCenter: activityThumbnail.horizontalCenter 769 | anchors.verticalCenter: activityThumbnailBlackRectangle.verticalCenter 770 | anchors.topMargin: 2 771 | opacity: 0 772 | text: model.name 773 | //font.family: "Helvetica" 774 | //font.pointSize: 12 775 | font.bold: true 776 | color: "white" 777 | //y: 5 778 | } 779 | ParallelAnimation { 780 | id: thumbnailHoverStart 781 | running: false 782 | NumberAnimation { 783 | target: activityThumbnailBlackRectangle; 784 | property: 'opacity'; 785 | from: 0; 786 | to: 0.5; 787 | } 788 | NumberAnimation { 789 | target: activityThumbnailTitleText; 790 | property: 'opacity'; 791 | from: 0; 792 | to: 1; 793 | } 794 | } 795 | ParallelAnimation { 796 | id: thumbnailHoverEnd 797 | running: false 798 | PropertyAnimation { target: activityThumbnailBlackRectangle; property: 'opacity'; to: 0; from: 0.5} 799 | PropertyAnimation { target: activityThumbnailTitleText; property: 'opacity'; to: 0; from: 1} 800 | } 801 | MouseArea { 802 | anchors.fill: parent 803 | //anchors.fill: activityThumbnail 804 | //parent: activitySwitcherDashMouseArea 805 | enabled: true 806 | hoverEnabled: true 807 | onClicked: { 808 | //console.log(Object.getOwnPropertyNames(workspace)); 809 | //console.log(Object.getOwnPropertyNames(ActivitySwitcher)); 810 | //workspace.currentActivity = model.id; 811 | fadeToBlack.restart(); 812 | ActivitySwitcher.Backend.setCurrentActivity(model.id) 813 | fadeFromBlack.restart(); 814 | } 815 | onEntered: { 816 | //showActivitySwitcherDashAnim.start(); 817 | //hideActivitySwitcherDashAnim.running = false; 818 | //activitySwitcherDashMouseArea.stop(); 819 | activitySwitcherDashTimer.stop(); 820 | thumbnailHoverStart.restart(); 821 | } 822 | onExited: { 823 | //hideActivitySwitcherDashAnim.running = true; 824 | //activitySwitcherDashMouseArea.stop(); 825 | //activitySwitcherDashTimer.restart(); 826 | thumbnailHoverEnd.restart(); 827 | } 828 | } 829 | // In case I ever figure out how to control the activities 830 | // flag of the clients from here. 831 | /*DropArea { 832 | id: activityDropArea 833 | anchors.fill: parent 834 | Rectangle { 835 | anchors.fill: parent 836 | visible: true 837 | color: "green" 838 | } 839 | onEntered: { 840 | console.log('ENTERING ACTIVITY!'); 841 | //console.log(Object.getOwnPropertyNames(drag.source)); 842 | //drag.source.newDesktop = bigDesktopRepeater.desktop+1; 843 | drag.source.newActivity = model.id; 844 | //console.log(Object.getOwnPropertyNames(ActivitySwitcher.Backend)); 845 | //console.log(Object.getOwnPropertyNames(workspace)); 846 | //con 847 | //console.log(drag.source.newDesktop); 848 | } 849 | onExited: { 850 | console.log('LEAVING ACTIVITY'); 851 | //drag.source.newDesktop = drag.source.currentDesktop; 852 | drag.source.newActivity = drag.source.clientObject.activities; 853 | //console.log(drag.source.newDesktop); 854 | } 855 | }*/ 856 | } 857 | } 858 | } 859 | } 860 | Rectangle { 861 | //anchors.fill: parent 862 | id: foregroundDarken 863 | visible: true 864 | opacity: 0 865 | x: 0 866 | y: 0 867 | //clip: true 868 | color: 'black' 869 | height: dashboard.screenHeight 870 | width: dashboard.screenWidth 871 | } 872 | /*Component.onCompleted: { 873 | workspace.numberDesktopsChanged.connect(function () { 874 | console.log('DESKTOPS CHANGED'); 875 | //currentDesktopGrid.model = workspace.desktops; 876 | }); 877 | }*/ 878 | } 879 | } 880 | } 881 | 882 | Timer { 883 | id: timer 884 | interval: 200 885 | //onTriggered: TextField.searchTextChanged() 886 | onTriggered: { 887 | // I suspect that we have problems reacquiring focus on a desktop change. 888 | console.log('DESKTOPCHANGED'); 889 | dashboard.requestActivate(); 890 | //workspace.activeClient = dashboard.windowId; 891 | //dashboard.height = dashboard.screenHeight-1; 892 | //dashboard.width = dashboard.screenWidth-1; 893 | //dashboard.height = dashboard.screenHeight; 894 | //dashboard.width = dashboard.screenWidth; 895 | searchFieldAndResults.children[1].forceActiveFocus(); 896 | } 897 | } 898 | 899 | Component.onCompleted: { 900 | dashboard.requestActivate(); 901 | //dashboard.windowId = workspace.activeClient; 902 | //mainBackground.focus = true; 903 | searchFieldAndResults.children[1].forceActiveFocus(); 904 | //searchFieldAndResults.forceActiveFocus(); 905 | dashboard.dockHeight = _getDockHeight(); 906 | dashboard.activeScreen = workspace.clientArea(KWinLib.MaximizedArea, workspace.activeScreen, workspace.currentDesktop); 907 | dashboard.screenWidth = dashboard.activeScreen.width; 908 | dashboard.screenHeight = dashboard.activeScreen.height + _getDockHeight(); 909 | dashboard.screenRatio = dashboard.activeScreen.width/dashboard.activeScreen.height; 910 | //dashboard.height = dashboard.screenHeight; 911 | //dashboard.width = dashboard.screenWidth; 912 | //dashboard.show(); 913 | //mainBackground.hide(); 914 | mainBackground.visible = false; 915 | mainBackground.state = 'invisible'; 916 | currentDesktopGridThumbnailContainer.state = 'showDesktop'; 917 | populateVisibleClients(); 918 | 919 | // disable! 920 | 921 | // Try and register a shortcut, maybe. 922 | //console.log(Object.getOwnPropertyNames(workspace)); 923 | if (KWin.registerShortcut) { 924 | KWin.registerShortcut("OVERVIEW: Show kwinOverview", 925 | "Show kwinOverview", 926 | "Meta+A", 927 | function() { 928 | toggleBoth() 929 | } 930 | ); 931 | } 932 | // Make the big ones. 933 | height: dashboard.screenHeight - dash.gridHeight - 30 934 | width: dashboard.screenWidth 935 | CreateClients.createAllClientThumbnails( 936 | currentDesktopGridThumbnailContainer, 937 | dashboard, 938 | 6, 939 | dashboard.screenHeight - dash.gridHeight - 30, 940 | dashboard.screenWidth, 941 | true 942 | ) 943 | CreateClients.createAllClientThumbnails( 944 | desktopThumbnailGrid, 945 | dashboard, 946 | 6, 947 | dash.gridHeight*.95, 948 | dash.gridHeight*dashboard.screenRatio*.95, 949 | false 950 | ) 951 | // Make sure we add new thumbnails as necessary. 952 | workspace.clientAdded.connect(function (c) { 953 | console.log(c); 954 | CreateClients.createNewClientThumbnails( 955 | desktopThumbnailGrid, 956 | dashboard, 957 | 6, 958 | dash.gridHeight*.95, 959 | dash.gridHeight*dashboard.screenRatio*.95, 960 | false, 961 | c 962 | ); 963 | CreateClients.createNewClientThumbnails( 964 | currentDesktopGridThumbnailContainer, 965 | dashboard, 966 | 6, 967 | dashboard.screenHeight - dash.gridHeight - 30, 968 | dashboard.screenWidth, 969 | true, 970 | c 971 | ); 972 | dashboard.requestActivate(); 973 | searchFieldAndResults.children[1].forceActiveFocus(); 974 | }); 975 | workspace.currentDesktopChanged.connect(function() { 976 | activeDesktopIndicatorShiftAnim.newX = ((dash.gridHeight*dashboard.screenRatio+desktopThumbnailGrid.spacing)*(workspace.currentDesktop-1)) - 2; 977 | activeDesktopIndicatorShiftAnim.originalX = activeDesktopIndicator.x; 978 | activeDesktopIndicatorShiftAnim.restart(); 979 | //searchFieldAndResults.children[1].forceActiveFocus(); 980 | if (mainBackground.state == 'visible') { 981 | //workspace.slotToggleShowDesktop(); 982 | dashboard.requestActivate(); 983 | timer.restart(); 984 | //dashboard.height = dashboard.screenHeight; 985 | //dashboard.width = dashboard.screenWidth; 986 | } 987 | //dashboard.visible = true; 988 | //initAnim.restart(); 989 | //mainBackground.state = 'visible'; 990 | //searchFieldAndResults.forceActiveFocus(); 991 | // We want the text input to basically always have focus. 992 | 993 | //mainBackground.forceActiveFocus(); 994 | // Move the flickable container. 995 | //if (((dash.gridHeight*dashboard.screenRatio+desktopThumbnailGrid.spacing)*(workspace.currentDesktop-1)) > desktopThumbnailGrid.contentY) { 996 | // desktopThumbnailGrid.contentY = desktopThumbnailGrid.contentY + ((dash.gridHeight*dashboard.screenRatio+desktopThumbnailGrid.spacing)); 997 | //} 998 | }); 999 | //toggleBoth(); 1000 | // Register all our clients. 1001 | //var c; 1002 | //for (c = 0; c < workspace.clientList().length; c++) { 1003 | // workspace.clientList()[c].desktopChanged.connect(checkGridUpdate); 1004 | //} 1005 | } 1006 | 1007 | function toggleBoth() { 1008 | //console.log(Object.getOwnPropertyNames(workspace)) 1009 | // Okay, NOW this works. 1010 | // but everything still sort of sucks. 1011 | //mainBackground.forceActiveFocus(); 1012 | console.log('Who has focus?'); 1013 | console.log(dashboard.activeFocusItem); 1014 | console.log('TESTING!'); 1015 | console.log(workspace.activeClient); 1016 | console.log(Object.getOwnPropertyNames(dashboard)); 1017 | console.log(Object.getOwnPropertyNames(workspace)); 1018 | //console.log(Object.getOwnPropertyNames(workspace.clientList()[0])); 1019 | /*console.log(Object.getOwnPropertyNames(workspace)); 1020 | console.log(Object.getOwnPropertyNames(workspace.activities)); 1021 | console.log(Object.getOwnPropertyNames(workspace.activities[0])); 1022 | console.log(JSON.stringify(workspace.activities[0])); 1023 | console.log(workspace.activities[0]); 1024 | console.log(JSON.stringify(workspace.clientList()[0])); 1025 | console.log(Object.getOwnPropertyNames(workspace.clientList()[0])); 1026 | console.log(Object.getOwnPropertyNames(workspace.clientList()[0].desktop)); 1027 | console.log(workspace.clientList()[0].desktop);*/ 1028 | //console.log(allActivities); 1029 | //console.log(workspace.clientList()[0].isOnCurrentActivity); 1030 | //console.log(JSON.stringify(workspace.clientList()[1])); 1031 | //console.log(Object.getOwnPropertyNames(desktopThumbnailGridBackgrounds)); 1032 | if (mainBackground.state == 'visible') { 1033 | //dashboard.visible = false; 1034 | //ndAnim.running = true; 1035 | // For the moment, just pause. We'll probably hook it up to a signal? 1036 | //while (endAnim.busy == true) {}; 1037 | //dashboard.height = 0; 1038 | //dashboard.width = 0; 1039 | endAnim.restart(); 1040 | //workspace.slotToggleShowDesktop(); 1041 | //workspace.slotToggleShowDesktop(); 1042 | /*for (c = 0; c < currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].children.length; c++) { 1043 | // 1044 | //console.log(Object.getOwnPropertyNames(currentDesktopGrid.children[0].children[c]));; 1045 | currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].children[0].children[c].startMoveFromThumbnail(); 1046 | }*/ 1047 | //enableVisibleClients(); 1048 | //endAnim 1049 | } else if (mainBackground.state == 'invisible') { 1050 | // It hates this command. Straight up. It seems to still be hiding things. 1051 | //dashboard.visible = true; 1052 | //dashboard.flags = Qt.X11BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint // won't work without it, apparently. 1053 | //mainBackground.show(); 1054 | // Show, then run the init animation. 1055 | //disableVisibleClients(); 1056 | dashboard.flags = Qt.X11BypassWindowManagerHint; 1057 | //dashboard.raise(); 1058 | //dashboard.update(); 1059 | dashboard.requestActivate(); 1060 | dashboard.height = dashboard.screenHeight; 1061 | dashboard.width = dashboard.screenWidth; 1062 | //dashboard.visible = true; 1063 | //workspace.slotToggleShowDesktop(); 1064 | initAnim.restart(); 1065 | mainBackground.state = 'visible'; 1066 | mainBackground.visible = true; 1067 | timer.restart(); 1068 | searchFieldAndResults.children[1].forceActiveFocus(); 1069 | //searchFieldAndResults.forceActiveFocus(); 1070 | //currentDesktopGrid.itemAt(workspace.currentDesktop-1).updateGrid(); 1071 | // Start the animation for the main grid. 1072 | /*var c; 1073 | for (c = 0; c < currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].children.length; c++) { 1074 | // 1075 | currentDesktopGrid.itemAt(workspace.currentDesktop-1).children[0].children[c].startMoveToThumbnail(); 1076 | };*/ 1077 | //console.log(Object.getOwnPropertyNames(dashboard)); 1078 | //initAnim.running = true; 1079 | //dashboard.update(); 1080 | //dashboard.raise(); 1081 | } 1082 | //console.log('Which things are visible?'); 1083 | // Although they SAY that they're visible, they are not. 1084 | // That is, I don't think they're being painted? For some reason. 1085 | //console.log(dashboard.visible); 1086 | //console.log(mainBackground.visible); 1087 | //console.log(dashboardBackground.visible); 1088 | //console.log(Object.getOwnPropertyNames(workspace.currentDesktopChanged)); 1089 | } 1090 | 1091 | function populateVisibleClients() { 1092 | // We need to build the list. 1093 | var c; 1094 | dashboard.clientsVisible = new Array(workspace.clientList().length); 1095 | for (c = 0; c < workspace.clientList().length; c++) { 1096 | //console.log(JSON.stringify(workspace.clientList()[c])); 1097 | dashboard.clientsVisible[c] = workspace.clientList()[c].minimized; 1098 | } 1099 | } 1100 | 1101 | function disableVisibleClients() { 1102 | var c; 1103 | for (c = 0; c < workspace.clientList().length; c++) { 1104 | //workspace.clientList()[c].minimized = true; 1105 | //workspace.clientList()[c].visible = false; 1106 | //console.log(Object.getOwnPropertyNames(workspace.clientList()[c])); 1107 | //console.log(Object.getOwnPropertyNames(workspace)); 1108 | // We're just hiding it by making it invisible. 1109 | workspace.clientList()[c].opacity = 0; 1110 | } 1111 | } 1112 | 1113 | function returnNumberOfDesktops() { 1114 | return workspace.desktops; 1115 | } 1116 | 1117 | function enableVisibleClients() { 1118 | var c; 1119 | for (c = 0; c < workspace.clientList().length; c++) { 1120 | if (dashboard.clientsVisible[c] == false) { 1121 | //workspace.clientList()[c].minimized = false; 1122 | // Better than hiding! 1123 | workspace.clientList()[c].opacity = 1; 1124 | //workspace.clientList()[c].showClient(); 1125 | } 1126 | } 1127 | } 1128 | 1129 | 1130 | } 1131 | --------------------------------------------------------------------------------