feat: bury offline mode, ship antergos-layan-theme

remember when i thought an offline installer was a good idea?
a pre-built squashfs that still needed internet to install DEs?
"offline" in name only. it was a lie. a beautiful, naive lie.

so i killed it. ripped out the mode picker, the connectivity check,
the whole AskMode() circus. calamares-next now just shows a notice
saying "yeah that wasnt actually offline" and launches directly
into online mode. no more pretending.

also fixed the QML nav button because having a greyed out "Next"
on the last page is terrible UX. now it says "Done" and quits.
youre welcome.

and since we nuked the offline mode, might as well make the
online mode look fucking spectacular. enter antergos-layan-theme:

- kvantum theme because your apps deserve better than qt dreck
- sddm theme (AntergosDark-Layan) for a sexy login screen
- every plasma plasmoid from the rice (panel colorizer, netspeed,
  shutdown switch, applets, separators, music toolbar, the works)
- yakuake arc-dark skin
- antergos logos in /usr/share/logos and /usr/share/pixmaps
- konsole profile, desktop appletsrc, all that jazz
- kde_settings.conf now points to AntergosDark-Layan sddm theme

also replaced the old antergos-grub-theme with the Layan grub
theme because the old one was a git-sourced abomination. 75 distro
icons, terminal box, selection widgets, antergos wallpaper.
ships to /usr/share/grub/themes/antergos/ so calamares finds it.

oh and a dozen audit fixes:
- kscreenlockerrc wallpaper path fixed
- kdialogrc no longer says some other distro
- 7 stale knsregistry files deleted (dev machine was leaking)
- systemd-inhibit guarded with command -v (dinit doesnt have it)
- plasma restart uses killall not systemctl --user
- "XeroLinux Toolkit" -> "Antergos Toolkit" in appletsrc
- KDE_SESSION_VERSION=5 -> =6 (its 2026)
- mirrorlist script says Artix not Arch
- SDDM Messages.sh removed from build artifacts
This commit is contained in:
2026-07-16 21:57:36 +02:00
parent 716ff74560
commit 82ff06c4da
1302 changed files with 308189 additions and 235 deletions
@@ -0,0 +1,2 @@
// Cannot package without this file due to a bug in kpackagetool6
// This file is empty on purpose, the implementation is done in the ui/main.qml file instead
@@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name=""/>
<group name="">
<entry name="defaultTiler" type="Int">
<default>0</default>
</entry>
<entry name="centerInTileMode" type="Int">
<default>0</default>
</entry>
<entry name="rememberTiler" type="Bool">
<default>false</default>
</entry>
<entry name="restoreSize" type="Bool">
<default>false</default>
</entry>
<entry name="allowTransient" type="Bool">
<default>false</default>
</entry>
<entry name="allowModal" type="Bool">
<default>false</default>
</entry>
<entry name="tilerVisibility" type="Int">
<default>0</default>
</entry>
<entry name="revealMargin" type="Int">
<default>200</default>
</entry>
<entry name="displayAs" type="Int">
<default>0</default>
</entry>
<entry name="windowVisibility" type="Int">
<default>0</default>
</entry>
<entry name="theme" type="Int">
<default>0</default>
</entry>
<entry name="tileMargin" type="Int">
<default>0</default>
</entry>
<entry name="screenMargin" type="Int">
<default>0</default>
</entry>
<entry name="defaultInput" type="Int">
<default>0</default>
</entry>
<entry name="showOverlayTextHint" type="Bool">
<default>true</default>
</entry>
<entry name="overlayLayout" type="String">
<default>4x2</default>
</entry>
<entry name="overlayScreenEdgeMargin" type="Int">
<default>0</default>
</entry>
<entry name="overlayPollingRate" type="Int">
<default>100</default>
</entry>
<entry name="rememberAllLayouts" type="Bool">
<default>false</default>
</entry>
<entry name="showTargetTileHint" type="Bool">
<default>true</default>
</entry>
<entry name="showTextHint" type="Bool">
<default>true</default>
</entry>
<entry name="gridTilerOpacity" type="Int">
<default>100</default>
<min>0</min>
<max>100</max>
</entry>
<entry name="popupGridAt" type="Int">
<default>0</default>
</entry>
<entry name="horizontalAlignment" type="Int">
<default>1</default>
</entry>
<entry name="verticalAlignment" type="Int">
<default>1</default>
</entry>
<entry name="gridColumns" type="Int">
<default>3</default>
</entry>
<entry name="gridAllColumns" type="Int">
<default>6</default>
</entry>
<entry name="gridSpacing" type="Int">
<default>10</default>
</entry>
<entry name="gridWidth" type="Int">
<default>130</default>
</entry>
<entry name="gridHeight" type="Int">
<default>70</default>
</entry>
<entry name="popupGridPollingRate" type="Int">
<default>100</default>
</entry>
<entry name="virtualDesktopVisibility" type="Int">
<default>0</default>
</entry>
<entry name="virtualDesktopDropAction" type="Int">
<default>0</default>
</entry>
<entry name="virtualDesktopHoverTime" type="Int">
<default>500</default>
</entry>
<entry name="moveBackOnDrop" type="Bool">
<default>false</default>
</entry>
<entry name="moveBackOnTile" type="Bool">
<default>false</default>
</entry>
<entry name="showAddVirtualDesktopButton" type="Bool">
<default>true</default>
</entry>
<entry name="autoRemoveEmptyVirtualDesktops" type="Bool">
<default>false</default>
</entry>
<entry name="popupLayout" type="String">
<default>1x1
2x1
3x1
SPECIAL_SPLIT_HORIZONTAL
0,0,75,100+25,0,75,100+25,0,50,100
4x1
2x2
SPECIAL_FILL
4x2
SPECIAL_AUTO_TILER_1
SPECIAL_AUTO_TILER_2
SPECIAL_AUTO_TILER_3</default>
</entry>
<entry name="allPopupLayouts" type="String">
<default>SPECIAL_FILL
SPECIAL_SPLIT_VERTICAL
SPECIAL_SPLIT_HORIZONTAL
SPECIAL_MAXIMIZE
SPECIAL_MINIMIZE
SPECIAL_FULLSCREEN
0,0,50,100+50,0,50,50+50,50,50,50
0,0,50,50+0,50,50,50+50,0,50,100
0,0,25,50+0,50,25,50+25,0,50,100+75,0,25,50+75,50,25,50
0,0,25,50+0,50,25,50+25,0,50,50+25,50,50,50+75,0,25,50+75,50,25,50
SPECIAL_KEEP_ABOVE
SPECIAL_KEEP_BELOW
0,0,50,100+50,0,25,50+50,50,25,50+75,0,25,50+75,50,25,50
0,0,25,50+0,50,25,50+25,0,25,50+25,50,25,50+50,0,50,100
0,0,50,50+0,50,50,50+50,0,25,50+50,50,25,50+75,0,25,50+75,50,25,50
0,0,25,50+0,50,25,50+25,0,25,50+25,50,25,50+50,0,50,50+50,50,50,50
SPECIAL_NO_TITLEBAR_AND_FRAME
SPECIAL_CLOSE
1x1
2x1
3x1
4x1
5x1
6x1
1x2
1x3
1x4
1x5
0,0,75,100+25,0,75,100+25,0,50,100
0,0,75,50+25,0,75,50+25,0,50,50+0,50,75,50+25,50,75,50+25,50,50,50
2x2
3x2
4x2
5x2
0,0,67,100+33,0,67,100+17,0,66,100
0,0,67,50+33,0,67,50+17,0,66,50+0,50,67,50+33,50,67,50+17,50,66,50
2x3
3x3
4x3
5x3
1x1+17,0,66,100+33,0,34,100
1x2+17,0,66,50+33,0,34,50+17,50,66,50+33,50,34,50
11,25,26,50+37,25,26,50+63,25,26,50
11,0,26,100+37,0,26,100+63,0,26,100
11,0,26,50+37,0,26,50+63,0,26,50 + 11,50,26,50+37,50,26,50+63,50,26,50
11,0,26,100+37,0,26,100+63,0,26,100 + 11,12,26,76+37,12,26,76+63,12,26,76 + 11,25,26,50+37,25,26,50+63,25,26,50
1x1+12,0,76,100+25,0,50,100+37,0,26,100
1x2+12,0,76,50+25,0,50,50+37,0,26,50+12,50,76,50+25,50,50,50+37,50,26,50
0,25,37,50+37,25,26,50+63,25,37,50
0,0,37,100+37,0,26,100+63,0,37,100
0,0,37,50+37,0,26,50+63,0,37,50 + 0,50,37,50+37,50,26,50+63,50,37,50
0,0,37,100+37,0,26,100+63,0,37,100 + 0,12,37,76+37,12,26,76+63,12,37,76 + 0,25,37,50+37,25,26,50+63,25,37,50
1x1+17,17,66,66+33,33,34,34
1x1+12,12,76,76+25,25,50,50+37,37,26,26
0,0,25,100+75,0,25,100+25,0,50,100
0,0,25,67+75,0,25,67+25,0,50,67+0,67,25,33+75,67,25,33+25,67,50,33
0,0,25,33+75,0,25,33+25,0,50,33+0,33,25,67+75,33,25,67+25,33,50,67
0,0,25,67+75,0,25,67+25,0,50,67+0,33,25,67+75,33,25,67+25,33,50,67
0,0,33,67+33,0,34,67+67,0,33,67+0,67,33,33+33,67,34,33+67,67,33,33
0,0,33,33+33,0,34,33+67,0,33,33+0,33,33,67+33,33,34,67+67,33,33,67
0,0,67,100+67,0,33,100
0,0,33,100+33,0,67,100
0,0,67,100+67,0,33,50+67,50,33,50
0,0,33,50+0,50,33,50+33,0,67,100
0,0,67,50+67,0,33,50+0,50,67,50+67,50,33,50
0,0,33,50+33,0,67,50+0,50,33,50+33,50,67,50
SPECIAL_AUTO_TILER_TOGGLE
SPECIAL_AUTO_TILER_1
SPECIAL_AUTO_TILER_2
SPECIAL_AUTO_TILER_3</default>
</entry>
<entry name="shortcutChangeMode" type="String">
<default>Meta+Ctrl+Space</default>
</entry>
<entry name="shortcutShowAllSpan" type="String">
<default>Ctrl+Space</default>
</entry>
<entry name="shortcutVisibility" type="String">
<default>Meta+Space</default>
</entry>
<entry name="shortcutInputType" type="String">
<default>Ctrl+Alt+I</default>
</entry>
<entry name="shortcutCenterInTile" type="String">
<default>Meta+Ctrl+C</default>
</entry>
<entry name="shortcutMoveOnDrop" type="String">
<default>Meta+Ctrl+V</default>
</entry>
<entry name="shortcutToggleTilingSuggestions" type="String">
<default>Meta+Ctrl+S</default>
</entry>
<entry name="hintChangeMode" type="Bool">
<default>true</default>
</entry>
<entry name="hintShowAllSpan" type="Bool">
<default>true</default>
</entry>
<entry name="hintVisibility" type="Bool">
<default>true</default>
</entry>
<entry name="hintInputType" type="Bool">
<default>true</default>
</entry>
<entry name="hintCenterInTile" type="Bool">
<default>true</default>
</entry>
<entry name="hintMoveOnDrop" type="Bool">
<default>true</default>
</entry>
<entry name="hintToggleTilingSuggestions" type="Bool">
<default>true</default>
</entry>
<entry name="showHintHint" type="Bool">
<default>true</default>
</entry>
<entry name="debugLogs" type="Bool">
<default>false</default>
</entry>
<entry name="showPositionHint" type="Bool">
<default>false</default>
</entry>
<entry name="positionHintFormat" type="Int">
<default>0</default>
</entry>
<entry name="showSizeHint" type="Bool">
<default>false</default>
</entry>
<entry name="sizeHintFormat" type="Int">
<default>0</default>
</entry>
<entry name="autoTileNewWindows" type="Bool">
<default>false</default>
</entry>
<entry name="autoTilerEdgeScroll" type="Bool">
<default>true</default>
</entry>
<entry name="autoTileMinimizedMaximized" type="Bool">
<default>true</default>
</entry>
<entry name="autoTileRestoreSize" type="Bool">
<default>false</default>
</entry>
<entry name="autoTileRestoreSizeAndPosition" type="Bool">
<default>true</default>
</entry>
<entry name="autoTileToggleBehavior" type="Int">
<default>0</default>
</entry>
<entry name="autoTileIds" type="String">
<default></default>
</entry>
<entry name="autoTileBlacklist" type="String">
<default>krunner,kded,polkit,plasmashell,yakuake,spectacle,org.kde.spectacle,kded5,xwaylandvideobridge,ksplashqml,org.kde.plasmashell,org.kde.polkit-kde-authentication-agent-1,org.kde.kruler,kruler,kwin_wayland,kwin,ksmserver-logout-greeter,ksmserver</default>
</entry>
<entry name="autoTileWindowAction" type="Int">
<default>0</default>
</entry>
<entry name="autoTileWindowIndex" type="Int">
<default>0</default>
</entry>
<entry name="autoTileFocusAction" type="Int">
<default>0</default>
</entry>
<entry name="autoTileFocusIndex" type="Int">
<default>0</default>
</entry>
<entry name="autoTileMinimizedFocusAction" type="Int">
<default>0</default>
</entry>
<entry name="autoTileMinimizedFocusIndex" type="Int">
<default>0</default>
</entry>
<entry name="autoTileDragSwapAction" type="Int">
<default>0</default>
</entry>
<entry name="autoTileLayer" type="Int">
<default>2</default>
</entry>
<entry name="maxAutoTileDelay" type="Int">
<default>5</default>
</entry>
<entry name="maxAutoTileDelaySessionStart" type="Int">
<default>10</default>
</entry>
<entry name="autoTilerMode1" type="Int">
<default>8</default>
</entry>
<entry name="autoTilerMode2" type="Int">
<default>1</default>
</entry>
<entry name="autoTilerMode3" type="Int">
<default>12</default>
</entry>
<entry name="autoTilerCustom1" type="String">
<default>0:0,0,100,100
0:0,0,50,100+1:50,0,50,100
0:0,0,50,100+1:50,0,50,50+2:50,50,50,50
0:0,0,50,50+3:0,50,50,50+1:50,0,50,50+2:50,50,50,50</default>
</entry>
<entry name="autoTilerCustom2" type="String">
<default>{"carousel":true}
0:0,0,100,100</default>
</entry>
<entry name="autoTilerCustom3" type="String">
<default>0:0,0,67,100
0:0,0,67,100+1:67,0,33,100
0:0,0,67,100+1:67,0,33,50+2:67,50,33,50</default>
</entry>
<entry name="autoTilerCarousel1" type="Bool">
<default>false</default>
</entry>
<entry name="autoTilerCarousel2" type="Bool">
<default>false</default>
</entry>
<entry name="autoTilerCarousel3" type="Bool">
<default>false</default>
</entry>
<entry name="suggestionsVisibility" type="Int">
<default>0</default>
</entry>
<entry name="suggestionsMode" type="Int">
<default>0</default>
</entry>
<entry name="suggestOtherScreens" type="Bool">
<default>false</default>
</entry>
<entry name="suggestOtherDesktops" type="Bool">
<default>false</default>
</entry>
<entry name="suggestOtherActivities" type="Bool">
<default>false</default>
</entry>
<entry name="suggestAutoTiled" type="Bool">
<default>false</default>
</entry>
<entry name="suggestMinimized" type="Bool">
<default>false</default>
</entry>
</group>
</kcfg>
@@ -0,0 +1,239 @@
import QtQuick
import org.kde.kirigami as Kirigami
Item {
Kirigami.Theme.colorSet: Kirigami.Theme.View
property bool lightTheme: {
return Kirigami.ColorUtils.brightnessForColor(Kirigami.Theme.backgroundColor) === Kirigami.ColorUtils.Light;
}
property var backgroundColor: {
switch (config.theme) {
case 1:
return "#161925";
case 2:
return "#110B00";
case 3:
return "#FFFFFF";
case 4:
return "#110000";
case 5:
return "#020900";
case 6:
return "#0A0010";
case 7:
return "#001100";
case 8:
return "#111100";
case 9:
return "#000710";
default:
return Kirigami.Theme.backgroundColor;
}
}
property var borderColor: {
switch (config.theme) {
case 1:
return "#666666";
case 2:
return "#7F4F00";
case 3:
return "#777777";
case 4:
return "#780000";
case 5:
return "#587C00";
case 6:
return "#4A0076";
case 7:
return "#225522";
case 8:
return "#555500";
case 9:
return "#004093";
default:
if (lightTheme) {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "black", 0.2);
} else {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", 0.2);
}
}
}
property var textColor: {
switch (config.theme) {
case 1:
return "white";
case 2:
return "white";
case 3:
return "black";
case 4:
return "white";
case 5:
return "white";
case 6:
return "white";
case 7:
return "white";
case 8:
return "white";
case 9:
return "white";
default:
return lightTheme ? "black" : "white";
}
}
property var overlayTextColor: {
switch (config.theme) {
case 1:
return "white";
case 2:
return "white";
case 3:
return "black";
case 4:
return "black";
case 5:
return "black";
case 6:
return "white";
case 7:
return "black";
case 8:
return "black";
case 9:
return "white";
default:
return lightTheme ? "black" : "white";
}
}
property var hintBackgroundColor: {
switch (config.theme) {
case 1:
return "#590099FF";
case 2:
return "#59FF9F00";
case 3:
return "#59FF0000";
case 4:
return "#59FF0000";
case 5:
return "#59B5FF00";
case 6:
return "#599F00FF";
case 7:
return "#5900FF00";
case 8:
return "#59FFFF00";
case 9:
return "#590067FF";
default:
return Kirigami.ColorUtils.tintWithAlpha("transparent", Kirigami.Theme.highlightColor, 0.35);
}
}
property var tileBorderColor: {
switch (config.theme) {
case 1:
return "#0099FF";
case 2:
return "#FF9F00";
case 3:
return "#FF0000";
case 4:
return "#FF0000";
case 5:
return "#B5FF00";
case 6:
return "#9F00FF";
case 7:
return "#00FF00";
case 8:
return "#FFFF00";
case 9:
return "#0067FF";
default:
return Kirigami.Theme.highlightColor;
}
}
property var tileBackgroundColor: {
switch (config.theme) {
case 1:
return "#0C0099FF";
case 2:
return "#0CFF9F00";
case 3:
return "#3CFF0000";
case 4:
return "#3CFF0000";
case 5:
return "#3CB5FF00";
case 6:
return "#3C9F00FF";
case 7:
return "#3C00FF00";
case 8:
return "#3CFFFF00";
case 9:
return "#3C0067FF";
default:
return Kirigami.ColorUtils.tintWithAlpha("transparent", Kirigami.Theme.highlightColor, 0.05);
}
}
property var tileBackgroundColorIntense: {
switch (config.theme) {
case 1:
return "#3C0099FF";
case 2:
return "#3CFF9F00";
case 3:
return "#3CFF0000";
case 4:
return "#3CFF0000";
case 5:
return "#3CB5FF00";
case 6:
return "#3C9F00FF";
case 7:
return "#3C00FF00";
case 8:
return "#3CFFFF00";
case 9:
return "#3C0067FF";
default:
return Kirigami.ColorUtils.tintWithAlpha("transparent", Kirigami.Theme.highlightColor, 0.23);
}
}
property var tileBackgroundColorActive: {
switch (config.theme) {
case 1:
return "#BE0099FF";
case 2:
return "#BEFF9F00";
case 3:
return "#BEFF0000";
case 4:
return "#BEFF0000";
case 5:
return "#BEB5FF00";
case 6:
return "#BE9F00FF";
case 7:
return "#BE00FF00";
case 8:
return "#BEFFFF00";
case 9:
return "#BE0067FF";
default:
return Kirigami.ColorUtils.tintWithAlpha("transparent", Kirigami.Theme.highlightColor, 0.75);
}
}
}
@@ -0,0 +1,182 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kwin
import org.kde.kirigami as Kirigami
// import QtWebView
ApplicationWindow {
property var overrides: {}
property var currentOverrides: {}
property var currentApplications: []
property var currentWindows: []
property var defaultConfig: {}
property var currentApplicationIndex: -1
property var currentWindowIndex: -1
// property var isX11: Qt.platform.pluginName == 'xcb'
property var mouseStartX
property var mouseStartY
property var windowStartX
property var windowStartY
Kirigami.Theme.colorSet: Kirigami.Theme.Window
property var lightTheme: {
return Kirigami.ColorUtils.brightnessForColor(Kirigami.Theme.backgroundColor) === Kirigami.ColorUtils.Light;
}
property var headerColor: {
if (lightTheme) {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", 0.4);
} else {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "black", 0.3);
}
}
property var headerBorderColor: {
if (lightTheme) {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "black", 0.2);
} else {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", 0.2);
}
}
id: mainMenuRoot
width: 1000
height: 734
title: "Mouse Tiler Configuration"
color: Kirigami.Theme.backgroundColor
flags: Qt.FramelessWindowHint | Qt.Window | Qt.BypassWindowManagerHint
// flags: ixX11 ? Qt.X11BypassWindowManagerHint : flags
// x: isX11 ? Workspace.virtualScreenSize.width / 2 - width / 2 : x
// y: isX11 ? Workspace.virtualScreenSize.height / 2 - height / 2 : y
x: Workspace.virtualScreenSize.width / 2 - width / 2
y: Workspace.virtualScreenSize.height / 2 - height / 2
function initMainMenu() {
}
Rectangle {
color: headerColor
height: 34
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
border.color: headerBorderColor
border.width: 1
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onPressed: {
mouseStartX = Workspace.cursorPos.x;
mouseStartY = Workspace.cursorPos.y;
windowStartX = mainMenuRoot.x;
windowStartY = mainMenuRoot.y;
}
onPositionChanged: {
mainMenuRoot.x = windowStartX + (Workspace.cursorPos.x - mouseStartX);
mainMenuRoot.y = windowStartY + (Workspace.cursorPos.y - mouseStartY);
}
onReleased: {
mainMenuRoot.x = windowStartX + (Workspace.cursorPos.x - mouseStartX);
mainMenuRoot.y = windowStartY + (Workspace.cursorPos.y - mouseStartY);
}
Label {
text: "Mouse Tiler Configuration"
anchors.centerIn: parent
}
Button {
text: "X"
flat: true
width: 26
height: 26
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 4
onClicked: root.closeMainMenu()
}
}
}
GroupBox {
id: mainGroupBox
anchors.fill: parent
anchors.topMargin: 35
// WebView {
// id: webView
// anchors.fill: parent
// url: "https://rxweb.epizy.com/mousetiler/editor.html"
// }
ColumnLayout {
id: mainChoice
visible: true
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
GroupBox {
spacing: 5
Layout.fillWidth: true
ColumnLayout {
anchors.fill: parent
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: "This was meant to be the UI designer. However it is a lot of work to implement it, probaly at least 1-2 weeks full time. Sadly I got rent to pay and a 3 year old son to feed and based on my previous project Remember Window Positions, I will not be able to feed us from my contributions to Linux. (After over 3 months full time work I earned $100 total which I apreciate but it's not possible to survive on). I might implement it in the future if the situation changes."
wrapMode: Text.WordWrap
}
// Label {
// Layout.fillWidth: true
// text: "Not implemented yet"
// wrapMode: Text.WordWrap
// }
}
}
// GroupBox {
// spacing: 5
// Layout.fillWidth: true
// ColumnLayout {
// anchors.fill: parent
// Label {
// Layout.fillWidth: true
// horizontalAlignment: Text.AlignHCenter
// text: "."
// wrapMode: Text.WordWrap
// }
// Button {
// id: selectWindowButton
// text: "Select Application/Window"
// Layout.fillWidth: true
// onClicked: selectWindow()
// }
// Button {
// text: "Edit Saved Applications and Windows"
// Layout.fillWidth: true
// onClicked: editSavedAppOrWindow()
// }
// }
// }
}
}
}
@@ -0,0 +1,319 @@
import QtQuick
import org.kde.kwin
import org.kde.plasma.core as PlasmaCore
// Window {
PlasmaCore.Dialog {
id: overlayTiler
property var activeScreen: null
property var clientArea: ({width: 1, height: 1, x: 0, y: 0})
property var tilePadding: 2
property int activeIndex: -1
property int spanFromIndex: -1
property int minSpanX: -1
property int minSpanY: -1
property int maxSpanX: -1
property int maxSpanY: -1
property var convertedOverlay: ([])
// Workaround for plasma 6.6 and earlier - needed by Window and Plasma Dialog - replaced by Qt.WindowTransparentForInput in Plasma 6.7
property bool outputOnly: true
width: clientArea.width - root.config.overlayScreenEdgeMargin * 2
height: clientArea.height - root.config.overlayScreenEdgeMargin * 2
x: clientArea.x + root.config.overlayScreenEdgeMargin
y: clientArea.y + root.config.overlayScreenEdgeMargin
// flags: Qt.Popup | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint
// flags: Qt.Tool | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus
flags: (root.config.displayAs == 0 ? 0 : Qt.Popup) | (Qt.BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.WindowTransparentForInput)
// color: "transparent" // Window
visible: false
backgroundHints: PlasmaCore.Types.NoBackground // PlasmaCore.Dialog
// outputOnly: true // PlasmaCore.Dialog
// type: PlasmaCore.Dialog.OnScreenDisplay
location: PlasmaCore.Types.Desktop // PlasmaCore.Dialog
function reset() {
activeScreen = null;
spanFromIndex = -1;
activeIndex = -1;
updateSpan();
}
function startAnimations() {
overlayTiler.opacity = 0;
showOverlayTilerAnimation.start();
}
function updateScreen() {
if (activeScreen != Workspace.activeScreen) {
autoTiler.updateShouldShowScreenEdges();
root.logE('updateScreen ' + Workspace.virtualScreenSize);
reset();
activeScreen = Workspace.activeScreen;
clientArea = Workspace.clientArea(KWin.FullScreenArea, activeScreen, Workspace.currentDesktop);
convertLayoutToScreen();
}
}
function convertLayoutToScreen() {
let layout = root.config.overlay;
let converted = [];
for (let i = 0; i < layout.length; i++) {
let tile = layout[i];
let width = (tile.pxW == undefined ? tile.w / 100 * mainItem.width : tile.pxW);
let height = (tile.pxH == undefined ? tile.h / 100 * mainItem.height : tile.pxH);
converted.push({
pxX: (tile.pxX == undefined ? tile.x / 100 * mainItem.width : tile.pxX) - (tile.aX == undefined ? 0 : tile.aX * width / 100),
pxY: (tile.pxY == undefined ? tile.y / 100 * mainItem.height : tile.pxY) - (tile.aY == undefined ? 0 : tile.aY * height / 100),
pxW: width,
pxH: height
});
}
convertedOverlay = converted;
}
function toggleSpan() {
if (spanFromIndex != activeIndex && spanFromIndex == -1) {
spanFromIndex = activeIndex;
updateSpan();
} else if (spanFromIndex >= 0) {
spanFromIndex = -1;
updateSpan();
}
}
function updateSpan() {
if (activeIndex == -1 || spanFromIndex == -1) {
minSpanX = -1;
minSpanY = -1;
maxSpanX = -1;
maxSpanY = -1;
} else {
let itemActive = tileRepeater.itemAt(activeIndex);
let itemSpan = tileRepeater.itemAt(spanFromIndex);
minSpanX = Math.min(itemActive.x, itemSpan.x);
minSpanY = Math.min(itemActive.y, itemSpan.y);
maxSpanX = Math.max(itemActive.x + itemActive.width, itemSpan.x + itemSpan.width);
maxSpanY = Math.max(itemActive.y + itemActive.height, itemSpan.y + itemSpan.height);
}
root.log('Span activeIndex: ' + activeIndex + ' spanFromIndex: ' + spanFromIndex + ' minSpanX: ' + minSpanX + ' minSpanY: ' + minSpanY + ' maxSpanX: ' + maxSpanX + ' maxSpanY: ' + maxSpanY);
}
function getGeometry() {
if (activeIndex >= 0) {
let x, y, width, height;
if (spanFromIndex >= 0) {
x = clientArea.x + minSpanX;
y = clientArea.y + minSpanY;
width = maxSpanX - minSpanX;
height = maxSpanY - minSpanY;
} else {
let item = tileRepeater.itemAt(activeIndex);
x = clientArea.x + item.x;
y = clientArea.y + item.y;
width = item.width;
height = item.height;
}
if (root.centerInTile) {
return {
x: x + width / 2 - root.currentlyMovedWindow.width / 2,
y: y + height / 2 - root.currentlyMovedWindow.height / 2,
width: root.currentlyMovedWindow.width,
height: root.currentlyMovedWindow.height
};
} else {
return {
x: x,
y: y,
width: width,
height: height
};
}
}
return null;
}
function getActiveVirtualDesktopIndex() {
return -1;
}
Item {
id: mainItem
width: popupTiler.width
height: popupTiler.height
SequentialAnimation {
id: showOverlayTilerAnimation
running: false
NumberAnimation {
target: overlayTiler;
property: "opacity";
from: 0;
to: 0;
duration: 32;
}
NumberAnimation {
target: overlayTiler;
property: "opacity";
from: 1;
to: 1;
duration: 1;
}
onFinished: {
root.updateWindowVisibility();
}
}
Colors {
id: colors
}
Repeater {
id: tileRepeater
model: overlayTiler.convertedOverlay
Item {
id: tile
property bool active: activeIndex == index
property bool spanned: !active && Math.ceil(modelData.pxX) >= Math.floor(minSpanX) && Math.floor(modelData.pxX) + Math.floor(modelData.pxW) <= Math.ceil(maxSpanX) && Math.ceil(modelData.pxY) >= Math.floor(minSpanY) && Math.floor(modelData.pxY) + Math.floor(modelData.pxH) <= Math.ceil(maxSpanY)
property bool spannedFrom: spanFromIndex == index
x: modelData.pxX
y: modelData.pxY
width: modelData.pxW
height: modelData.pxH
Rectangle {
anchors.fill: parent
anchors.margins: tilePadding
border.color: colors.tileBorderColor
border.width: 2
color: "transparent"
radius: 12
Rectangle {
anchors.fill: parent
radius: 12
color: active || spanned ? colors.tileBackgroundColorActive : colors.tileBackgroundColor
}
Text {
anchors.centerIn: parent
color: colors.overlayTextColor
textFormat: Text.StyledText
text: {
let defaultHint = "";
let insertExtraBreak = false;
if (root.config.showHintHint) {
defaultHint += (defaultHint.length > 0 ? "<br>" : "") + "Configure tiler visibility and these hints in settings.";
insertExtraBreak = true;
}
if (root.config.hintShowAllSpan) {
if (insertExtraBreak) {
defaultHint += "<br>";
insertExtraBreak = false;
}
defaultHint += (defaultHint.length > 0 ? "<br>" : "") + (spannedFrom ? "Stop spanning (<b>" + root.config.shortcutShowAllSpan + "</b>)" : "Span from this tile (<b>" + root.config.shortcutShowAllSpan + "</b>)");
insertExtraBreak = true;
}
if (root.config.hintVisibility) {
if (insertExtraBreak) {
defaultHint += "<br>";
insertExtraBreak = false;
}
defaultHint += (defaultHint.length > 0 ? "<br>" : "") + "Toggle visibility (<b>" + root.config.shortcutVisibility + "</b>)";
}
if (root.config.hintInputType) {
if (insertExtraBreak) {
defaultHint += "<br>";
insertExtraBreak = false;
}
defaultHint += (defaultHint.length > 0 ? "<br>" : "") + "Input type: " + (root.useMouseCursor ? "Mouse" : "Window") + " (<b>" + root.config.shortcutInputType + "</b>)";
}
if (root.config.hintCenterInTile) {
if (insertExtraBreak) {
defaultHint += "<br>";
insertExtraBreak = false;
}
defaultHint += (defaultHint.length > 0 ? "<br>" : "") + "Center in tile: " + (root.centerInTile ? "Enabled" : "Disabled") + " (<b>" + root.config.shortcutCenterInTile + "</b>)";
}
if (root.config.hintChangeMode) {
insertExtraBreak = false;
defaultHint += (defaultHint.length > 0 ? "<br><br>" : "") + "Switch mode (<b>" + root.config.shortcutChangeMode + "</b>)";
}
if (root.centerInTile) {
return (spannedFrom ? "<b>Center in spanned tiles</b><br><br>" : "<b>Center in this tile</b><br><br>") + defaultHint;
} else {
return defaultHint;
}
return defaultHint;
}
font.pixelSize: 16
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
visible: root.config.showOverlayTextHint && (active && spanFromIndex == -1 || spannedFrom)
}
}
}
}
Rectangle {
id: popupWindowCursor
anchors.left: parent.left
anchors.leftMargin: root.getCursorPosition().x - clientArea.x - 6
anchors.top: parent.top
anchors.topMargin: root.getCursorPosition().y - clientArea.y - 6
width: 12
height: 12
border.color: colors.tileBorderColor
border.width: 2
color: colors.tileBackgroundColorActive
radius: 6
visible: !root.useMouseCursor
Rectangle {
anchors.centerIn: parent
width: 2
height: 2
color: colors.textColor
radius: 1
opacity: 0.8
}
}
Timer {
interval: root.config.overlayPollingRate
repeat: true
running: overlayTiler.visible
onTriggered: {
updateScreen();
let localCursorPos = Workspace.activeScreen.mapFromGlobal(root.getCursorPosition());
let x = localCursorPos.x - root.config.overlayScreenEdgeMargin;
let y = localCursorPos.y - root.config.overlayScreenEdgeMargin;
let index = -1;
for (let i = 0; i < tileRepeater.count; i++) {
let item = tileRepeater.itemAt(i);
if (item.x <= x && item.x + item.width >= x && item.y <= y && item.y + item.height >= y) {
index = i;
break;
}
}
if (index != activeIndex) {
activeIndex = index;
updateSpan();
}
}
}
}
}
@@ -0,0 +1,978 @@
import QtQuick
import QtQuick.Layouts
import org.kde.kwin
import org.kde.plasma.core as PlasmaCore
// Window {
PlasmaCore.Dialog {
id: popupTiler
property var activeScreen: null
property var clientArea: ({width: 1, height: 1, x: 0, y: 0})
property int tilePadding: 2
property int borderOffset: 2
property int activeLayoutIndex: -1
property int activeTileIndex: -1
property int positionX: 0
property int positionY: 0
property bool showAll: false
property bool lastShowAll: false
property var hint: null
property bool showPopupDropHint: false
property bool hasValidPopupDropHint: false
property var popupDropHintX: 0
property var popupDropHintY: 0
property var popupDropHintWidth: 0
property var popupDropHintHeight: 0
property bool popupDropHintIsCenterInTile: false
property bool sizeEstablished: false
property var revealBox: null
property bool revealed: false
property bool currentlyHovered: false
property int activeVirtualDesktopIndex: -1
property var activeVirtualDesktopHoverTime: -1
property bool virtualDesktopVisibilityOverride: false
// property bool shouldShowAutoTileScroll: false
// property var scrollLeftTime: 0
// property var scrollRightTime: 0
// Workaround for plasma 6.6 and earlier - needed by Window and Plasma Dialog - replaced by Qt.WindowTransparentForInput in Plasma 6.7
property bool outputOnly: true
width: clientArea.width
height: clientArea.height
x: clientArea.x
y: clientArea.y
//flags: Qt.Popup | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint
// flags: Qt.Tool | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus
flags: (root.config.displayAs == 0 ? 0 : Qt.Popup) | (Qt.BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.WindowTransparentForInput)
// color: "transparent" // Window
visible: false
backgroundHints: PlasmaCore.Types.NoBackground // PlasmaCore.Dialog
// outputOnly: true // PlasmaCore.Dialog
// type: PlasmaCore.Dialog.OnScreenDisplay
location: PlasmaCore.Types.Desktop // PlasmaCore.Dialog
function reset() {
activeScreen = null;
activeLayoutIndex = -1;
activeTileIndex = -1;
hint = null;
showPopupDropHint = false;
updateHintContent();
}
function resetShowAll() {
showAll = false;
lastShowAll = false;
}
function startAnimations() {
popupTiler.opacity = 0;
showPopupTilerAnimation.start();
}
function updateScreen(forceUpdate = false) {
if (forceUpdate || activeScreen != Workspace.activeScreen) {
root.logE('updateScreen ' + Workspace.virtualScreenSize);
if (activeScreen != Workspace.activeScreen) {
autoTiler.updateAutoTilersInPopupTiler();
}
autoTiler.updateShouldShowScreenEdges();
// shouldShowAutoTileScroll = autoTiler.shouldShowAutoTileScroll();
activeScreen = Workspace.activeScreen;
clientArea = Workspace.clientArea(KWin.FullScreenArea, Workspace.activeScreen, Workspace.currentDesktop);
let localCursorPos = Workspace.activeScreen.mapFromGlobal(root.getCursorPosition());
let visibleHeight = virtualDesktopVisibilityOverride || root.config.virtualDesktopVisibility != 1 ? layouts.height : 0;
if (root.config.popupGridAt == 0) {
switch (root.config.horizontalAlignment) {
default:
positionX = localCursorPos.x - root.config.gridWidth / 2 - root.config.gridSpacing;
break;
case 1:
positionX = localCursorPos.x - layouts.width / 2;
break;
case 2:
positionX = localCursorPos.x - layouts.width + root.config.gridWidth / 2 + root.config.gridSpacing;
break;
}
if (visibleHeight == 0) {
positionY = localCursorPos.y;
} else {
switch (root.config.verticalAlignment) {
default:
positionY = localCursorPos.y - root.config.gridHeight / 2 - root.config.gridSpacing;
break;
case 1:
positionY = localCursorPos.y - layouts.height / 2;
break;
case 2:
positionY = localCursorPos.y - layouts.height + root.config.gridHeight / 2 + root.config.gridSpacing;
break;
}
}
} else {
switch (root.config.horizontalAlignment) {
default:
positionX = 0;
break;
case 1:
positionX = clientArea.width / 2 - layouts.width / 2;
break;
case 2:
positionX = clientArea.width - layouts.width;
break;
}
switch (root.config.verticalAlignment) {
default:
positionY = 0;
break;
case 1:
positionY = clientArea.height / 2 - visibleHeight / 2;
break;
case 2:
positionY = clientArea.height - visibleHeight;
break;
}
}
// positionX = localCursorPos.x - layouts.width / 2;
// positionY = localCursorPos.y - (layouts.height + popupHint.height + 2) / 2;
if (positionX < 0) {
positionX = 0;
} else if (positionX + layouts.width > popupTiler.width) {
positionX = popupTiler.width - layouts.width;
}
let totalHeight = visibleHeight;
if (root.virtualDesktopVisibile) {
if (totalHeight > 0) {
totalHeight += 2;
}
totalHeight += virtualDesktop.height;
}
if (root.config.showTextHint) {
totalHeight += popupHint.height + 2;
}
if (positionY < 0) {
positionY = 0;
} else if (positionY + totalHeight > popupTiler.height) {
positionY = popupTiler.height - totalHeight;
}
if (root.config.tilerVisibility == 3) {
let triggerLeft = Math.max(positionX - config.revealMargin, 0);
let triggerRight = Math.min(positionX + layouts.width + config.revealMargin, popupTiler.width);
let triggerTop = Math.max(positionY - config.revealMargin, 0);
let triggerBottom = Math.min(positionY + totalHeight + config.revealMargin, popupTiler.height);
let leftTop = Workspace.activeScreen.mapToGlobal(Qt.point(triggerLeft, triggerTop));
let rightBottom = Workspace.activeScreen.mapToGlobal(Qt.point(triggerRight, triggerBottom));
revealBox = { left: leftTop.x, right: rightBottom.x, top: leftTop.y, bottom: rightBottom.y };
updateRevealed(true);
} else {
revealBox = null;
updateRevealed(true);
}
}
}
function getGeometry() {
if (activeLayoutIndex >= 0 && activeTileIndex >= 0) {
if (root.centerInTile && hasValidPopupDropHint) {
return {
x: clientArea.x + popupDropHintX,
y: clientArea.y + popupDropHintY,
width: popupDropHintWidth,
height: popupDropHintHeight
};
} else if (layoutRepeater.model[activeLayoutIndex].special) {
return {
special: layoutRepeater.model[activeLayoutIndex].special,
specialMode: activeTileIndex
};
} else if (!root.centerInTile) {
return {
x: clientArea.x + popupDropHintX,
y: clientArea.y + popupDropHintY,
width: popupDropHintWidth,
height: popupDropHintHeight,
defaultLayouts: !showAll,
layoutIndex: activeLayoutIndex,
tileIndex: activeTileIndex
};
}
}
return null;
}
function getActiveVirtualDesktopIndex() {
return activeVirtualDesktopIndex;
}
function resetVirtualDesktopOverride() {
virtualDesktopVisibilityOverride = false;
}
function toggleShowAll() {
reset();
if (root.config.virtualDesktopVisibility == 1 && !virtualDesktopVisibilityOverride) {
virtualDesktopVisibilityOverride = true;
} else {
showAll = !showAll;
updateHintContent();
}
}
function updateAndShowPopupDropHint() {
let shouldShowPopupDropHint = false;
let special = layoutRepeater.model[activeLayoutIndex].special;
let geometry = null;
switch (special) {
case 'SPECIAL_FILL':
if (root.currentlyMovedWindow != null) {
geometry = root.getFillGeometry(root.currentlyMovedWindow, activeTileIndex == 0);
}
break;
case 'SPECIAL_SPLIT_VERTICAL':
if (root.currentlyMovedWindow != null) {
geometry = splitAndMoveSplitted(root.currentlyMovedWindow, true, activeTileIndex == 0, false);
}
break;
case 'SPECIAL_SPLIT_HORIZONTAL':
if (root.currentlyMovedWindow != null) {
geometry = splitAndMoveSplitted(root.currentlyMovedWindow, false, activeTileIndex == 0, false);
}
break;
case 'SPECIAL_NO_TITLEBAR_AND_FRAME':
case 'SPECIAL_KEEP_ABOVE':
case 'SPECIAL_KEEP_BELOW':
case 'SPECIAL_EMPTY':
case 'SPECIAL_MINIMIZE':
case 'SPECIAL_CLOSE':
case 'SPECIAL_AUTO_TILER_TOGGLE':
break;
case 'SPECIAL_AUTO_TILER_1':
case 'SPECIAL_AUTO_TILER_2':
case 'SPECIAL_AUTO_TILER_3':
if (!layoutRepeater.model[activeLayoutIndex].activeAutoTiler) {
break;
}
default:
let layout = layoutRepeater.model[activeLayoutIndex].tiles[activeTileIndex];
let hintWidth = layout.w == undefined ? layout.pxW : layout.w / 100 * clientArea.width;
let hintHeight = layout.h == undefined ? layout.pxH : layout.h / 100 * clientArea.height;
let hintX = (layout.x == undefined ? layout.pxX : layout.x / 100 * clientArea.width) - (layout.aX == undefined ? 0 : layout.aX * hintWidth / 100);
let hintY = (layout.y == undefined ? layout.pxY : layout.y / 100 * clientArea.height) - (layout.aY == undefined ? 0 : layout.aY * hintHeight / 100);
if (root.centerInTile) {
popupDropHintWidth = root.currentlyMovedWindow.width;
popupDropHintHeight = root.currentlyMovedWindow.height;
popupDropHintX = hintX + hintWidth / 2 - root.currentlyMovedWindow.width / 2;
popupDropHintY = hintY + hintHeight / 2 - root.currentlyMovedWindow.height / 2;
} else {
popupDropHintWidth = hintWidth;
popupDropHintHeight = hintHeight;
popupDropHintX = hintX;
popupDropHintY = hintY;
}
showPopupDropHint = root.config.showTargetTileHint;
hasValidPopupDropHint = true;
return; // Force return to avoid hiding popup
}
if (geometry != null) {
if (root.centerInTile) {
popupDropHintX = (geometry.x - clientArea.x) + geometry.width / 2 - root.currentlyMovedWindow.width / 2;
popupDropHintY = (geometry.y - clientArea.y) + geometry.height / 2 - root.currentlyMovedWindow.height / 2;
popupDropHintWidth = root.currentlyMovedWindow.width;
popupDropHintHeight = root.currentlyMovedWindow.height;
shouldShowPopupDropHint = true;
} else {
popupDropHintX = geometry.x - clientArea.x;
popupDropHintY = geometry.y - clientArea.y;
popupDropHintWidth = geometry.width;
popupDropHintHeight = geometry.height;
shouldShowPopupDropHint = true;
}
}
if (root.config.showTargetTileHint) {
showPopupDropHint = shouldShowPopupDropHint;
} else {
showPopupDropHint = false;
}
hasValidPopupDropHint = shouldShowPopupDropHint;
}
function updateHintContent() {
if (activeVirtualDesktopIndex >= 0) {
hasValidPopupDropHint = false;
showPopupDropHint = false;
if (root.virtualDesktops[activeVirtualDesktopIndex].isAdd) {
switch (config.virtualDesktopDropAction) {
case 0:
if (root.moveToVirtualDesktopOnDrop) {
// hint = '<b>Drop</b> - Switch to a new virtual desktop, and move window';
hint = '<b>Drop</b> - Move window to a new virtual desktop, and switch to it';
} else {
// hint = '<b>Drop</b> - Add new virtual desktop, and move window';
// hint = '<b>Drop</b> - Move window to a new virtual desktop without switching';
hint = '<b>Drop</b> - Move window to a new virtual desktop then go back to ' + root.virtualDesktopAtMoveStart.name;
}
break;
case 1:
if (root.moveToVirtualDesktopOnDrop) {
// hint = '<b>Drop</b> - Switch to a new virtual desktop, and maximize window';
hint = '<b>Drop</b> - Maximize window on a new virtual desktop, and switch to it';
} else {
// hint = '<b>Drop</b> - Add new virtual desktop, and maximize window';
// hint = '<b>Drop</b> - Maximize window on a new virtual desktop without switching';
hint = '<b>Drop</b> - Maximize window on a new virtual desktop then go back to ' + root.virtualDesktopAtMoveStart.name;
}
break;
}
if (root.config.hintMoveOnDrop) {
hint += ' (<b>' + root.config.shortcutMoveOnDrop + '</b>)';
}
} else {
let virtualDesktopName = root.virtualDesktops[activeVirtualDesktopIndex].desktop.name;
let virtualDesktopHoverChanged = activeVirtualDesktopIndex != root.virtualDesktopIndexAtMoveStart;
switch (config.virtualDesktopDropAction) {
case 0:
if (!virtualDesktopHoverChanged) {
hint = '<b>Drop</b> - Cancel move';
} else if (root.moveToVirtualDesktopOnDrop) {
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Move window, and switch to ' + virtualDesktopName;
hint = '<b>Drop</b> - Move window to ' + virtualDesktopName + ', and switch to it';
} else {
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Move window to ' + virtualDesktopName;
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Move window to ' + virtualDesktopName + ' without switching';
hint = '<b>Drop</b> - Move window to ' + virtualDesktopName + ' then go back to ' + root.virtualDesktopAtMoveStart.name;
}
break;
case 1:
if (!virtualDesktopHoverChanged) {
hint = '<b>Drop</b> - Maximize window on current virtual desktop';
} else if (root.moveToVirtualDesktopOnDrop) {
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Maximize window on, and switch to ' + virtualDesktopName;
hint = '<b>Drop</b> - Maximize window on ' + virtualDesktopName + ', and switch to it';
} else {
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Maximize window on ' + virtualDesktopName;
// hint = '<b>Hover</b> - Switch to ' + virtualDesktopName + '<br><b>Drop</b> - Maximize window on ' + virtualDesktopName + ' without switching';
hint = '<b>Drop</b> - Maximize window on ' + virtualDesktopName + ' then go back to ' + root.virtualDesktopAtMoveStart.name;
}
break;
}
if (virtualDesktopHoverChanged && root.config.hintMoveOnDrop) {
hint += ' (<b>' + root.config.shortcutMoveOnDrop + '</b>)';
}
if (root.currentVirtualDesktopIndex != activeVirtualDesktopIndex) {
hint += '<br><b>Hover</b> - Switch to ' + virtualDesktopName;
}
}
} else if (!virtualDesktopVisibilityOverride && root.config.virtualDesktopVisibility == 1) {
hint = "Show tiler (<b>" + root.config.shortcutShowAllSpan + "</b>)";
hasValidPopupDropHint = false;
showPopupDropHint = false;
} else if (activeLayoutIndex >= 0 && activeTileIndex >= 0) {
updateAndShowPopupDropHint();
popupDropHintIsCenterInTile = root.centerInTile;
let special = layoutRepeater.model[activeLayoutIndex].special;
let tile = layoutRepeater.model[activeLayoutIndex].tiles[activeTileIndex];
if (root.centerInTile && hasValidPopupDropHint) {
hint = '<b>Center in tile</b> - will not resize the window' + (root.config.hintCenterInTile ? '<br>Toggle with <b>' + root.config.shortcutCenterInTile + '</b>' : '');
} else if (tile.hint) {
switch (special) {
case 'SPECIAL_KEEP_ABOVE':
hint = tile.hint + '<br>Currently <b>' + (root.currentlyMovedWindow.keepAbove ? 'Enabled' : 'Disabled') + '</b>';
break;
case 'SPECIAL_KEEP_BELOW':
hint = tile.hint + '<br>Currently <b>' + (root.currentlyMovedWindow.keepBelow ? 'Enabled' : 'Disabled') + '</b>';
break;
case 'SPECIAL_FILL':
hint = hasValidPopupDropHint ? tile.hint : '<font color="orange">⚠</font> No free space available';
break;
case 'SPECIAL_SPLIT_HORIZONTAL':
case 'SPECIAL_SPLIT_VERTICAL':
hint = hasValidPopupDropHint ? tile.hint : '<font color="orange">⚠</font> No window available for splitting';
break;
case 'SPECIAL_AUTO_TILER_TOGGLE':
hint = root.currentlyMovedWindow.mt_auto == undefined ? '<b>Enable</b> auto-tiling for this window' : '<b>Disable</b> auto-tiling for this window';
break;
default:
hint = tile.hint;
break;
}
} else if (layoutRepeater.model[activeLayoutIndex].activeAutoTiler == true && tile.t != '*') {
hint = 'Auto-tile in this tile<br>Cycle auto-tiled windows with Ctrl+Alt+Left or Right';
} else if (root.virtualDesktopChangedSinceMoveStart) {
if (root.moveToVirtualDesktopOnTile) {
hint = 'Tile window on current virtual dekstop';
} else {
hint = 'Tile window on ' + Workspace.currentDesktop.name + ' then go back to ' + root.virtualDesktopAtMoveStart.name;
}
if (root.config.hintMoveOnDrop) {
hint += ' (<b>' + root.config.shortcutMoveOnDrop + '</b>)';
}
} else if ((root.config.showSizeHint || root.config.showPositionHint) && !special) {
hint = '';
if (root.config.showPositionHint) {
if (root.config.showPositionHintInPixels) {
hint += 'X: <b>' + Math.round(popupDropHintX) + '</b> Y: <b>' + Math.round(popupDropHintY) + '</b>';
} else {
hint += 'X: <b>' + Math.round(100 * popupDropHintX / clientArea.width) + '</b>% Y: <b>' + Math.round(100 * popupDropHintY / clientArea.height) + '</b>%';
}
}
if (root.config.showSizeHint) {
if (hint.length > 0) {
hint += '<br>';
}
if (root.config.showSizeHintInPixels) {
hint += 'W: <b>' + Math.round(popupDropHintWidth) + '</b> H: <b>' + Math.round(popupDropHintHeight) + '</b>';
} else {
hint += 'W: <b>' + Math.round(100 * popupDropHintWidth / clientArea.width) + '</b>% H: <b>' + Math.round(100 * popupDropHintHeight / clientArea.height) + '</b>%';
}
}
} else {
hint = null;
}
} else {
hasValidPopupDropHint = false;
showPopupDropHint = false;
hint = null;
}
if (hint == null) {
let defaultHint = "";
let hasShortcutHint = false;
if (root.config.showHintHint) {
defaultHint += (defaultHint.length > 0 ? " " : "") + "Configure tiler visibility and these hints in settings.<br>";
}
if (root.config.hintShowAllSpan) {
defaultHint += (hasShortcutHint ? " - " : "") + (showAll ? "Show default (<b>" + root.config.shortcutShowAllSpan + "</b>)" : "Show all (<b>" + root.config.shortcutShowAllSpan + "</b>)");
hasShortcutHint = true;
}
if (root.config.hintVisibility) {
defaultHint += (hasShortcutHint ? " - " : "") + "Visibility (<b>" + root.config.shortcutVisibility + "</b>)";
hasShortcutHint = true;
}
if (root.config.hintChangeMode) {
defaultHint += (hasShortcutHint ? " - " : "") + "Mode (<b>" + root.config.shortcutChangeMode + "</b>)";
hasShortcutHint = true;
}
if (root.config.hintInputType) {
defaultHint += (hasShortcutHint ? " - " : "") + "Input type (<b>" + root.config.shortcutInputType + "</b>)";
hasShortcutHint = true;
}
if (root.config.hintCenterInTile) {
defaultHint += (hasShortcutHint ? " - " : "") + "Center in tile (<b>" + root.config.shortcutCenterInTile + "</b>)";
hasShortcutHint = true;
}
if (root.config.hintToggleTilingSuggestions) {
defaultHint += (hasShortcutHint ? " - " : "") + "Suggestions: <b>" + (settings.showTilingSuggestions ? "Enabled": "Disabled") + "</b> (<b>" + root.config.shortcutToggleTilingSuggestions + "</b>)";
hasShortcutHint = true;
}
if (defaultHint.length > 0) {
hint = defaultHint;
}
}
if (root.useAutoTilerPreview) {
if (root.validAutoTilerPreview) {
hint = 'Valid auto tile configuration - good job! :)';
} else {
hint = '<font color="orange">⚠</font> <b>WARNING</b> <font color="orange">⚠</font> INVALID AUTO TILE CONFIGURATION';
}
}
}
function updateRevealed(forceUpdate = false) {
var updatedRevealed;
if (revealBox == null) {
updatedRevealed = true;
} else {
let x = root.getCursorPosition().x;
let y = root.getCursorPosition().y;
updatedRevealed = revealBox.left <= x && revealBox.right >= x && revealBox.top <= y && revealBox.bottom >= y;
}
if (forceUpdate || updatedRevealed != revealed) {
revealed = updatedRevealed;
root.updateWindowVisibility();
}
}
Item {
id: mainItem
width: popupTiler.width
height: popupTiler.height
visible: revealed
SequentialAnimation {
id: showPopupTilerAnimation
running: false
NumberAnimation {
target: popupTiler;
property: "opacity";
from: 0;
to: 0;
duration: 32;
}
NumberAnimation {
target: popupTiler;
property: "opacity";
from: 1;
to: 1;
duration: 1;
}
onFinished: {
root.updateWindowVisibility();
}
}
Colors {
id: colors
}
Rectangle {
id: popupDropHint
anchors.left: parent.left
anchors.leftMargin: popupDropHintX
anchors.top: parent.top
anchors.topMargin: popupDropHintY
width: popupDropHintWidth
height: popupDropHintHeight
border.color: colors.tileBorderColor
border.width: 2
color: "transparent"
radius: 12
visible: showPopupDropHint
Rectangle {
anchors.fill: parent
color: colors.hintBackgroundColor
radius: 12
}
}
Rectangle {
id: layouts
width: layoutGrid.implicitWidth + layoutGrid.columnSpacing * 2
height: layoutGrid.implicitHeight + layoutGrid.rowSpacing * 2
color: root.config.gridTilerOpacity != 1 ? Qt.alpha(colors.backgroundColor, root.config.gridTilerOpacity) : colors.backgroundColor
border.color: colors.borderColor
border.width: 1
radius: 8
visible: virtualDesktopVisibilityOverride || root.config.virtualDesktopVisibility != 1
anchors.left: parent.left
anchors.leftMargin: positionX
anchors.top: parent.top
anchors.topMargin: positionY
GridLayout {
id: layoutGrid
columns: showAll ? root.config.gridAllColumns : root.config.gridColumns
columnSpacing: root.config.gridSpacing
rowSpacing: root.config.gridSpacing
anchors.fill: parent
anchors.margins: root.config.gridSpacing
uniformCellWidths: true
uniformCellHeights: true
Repeater {
id: layoutRepeater
model: showAll ? root.popupGridAllLayouts : root.popupGridLayouts
Rectangle {
id: tiles
width: root.config.gridWidth
height: root.config.gridHeight
color: "transparent"
border.color: modelData.activeAutoTiler ? colors.tileBorderColor : colors.borderColor
border.width: 1
radius: 8
clip: modelData.clip == true
property bool layoutActive: activeLayoutIndex == index
Repeater {
id: tileRepeater
model: modelData.tiles
Item {
id: tile
property bool tileActive: activeTileIndex == index
property bool tileDisabled: modelData.d ? true : false
width: (modelData.w == undefined ? modelData.pxW / clientArea.width : modelData.w / 100) * (tiles.width - borderOffset * 2)
height: (modelData.h == undefined ? modelData.pxH / clientArea.height : modelData.h / 100) * (tiles.height - borderOffset * 2)
x: (modelData.x == undefined ? modelData.pxX / clientArea.width : modelData.x / 100) * (tiles.width - borderOffset * 2) + borderOffset - (modelData.aX == undefined ? 0 : modelData.aX * width / 100)
y: (modelData.y == undefined ? modelData.pxY / clientArea.height : modelData.y / 100) * (tiles.height - borderOffset * 2) + borderOffset - (modelData.aY == undefined ? 0 : modelData.aY * height / 100)
Rectangle {
anchors.fill: parent
anchors.margins: tilePadding
border.color: colors.tileBorderColor
border.width: 1
// color: "#152030"
color: "transparent"
radius: 6
opacity: tileDisabled ? 0.3 : 1
Rectangle {
anchors.fill: parent
color: layoutActive && tileActive ? colors.tileBackgroundColorActive : colors.tileBackgroundColor
radius: 6
}
Text {
anchors.centerIn: parent
color: colors.textColor
textFormat: Text.StyledText
// ⸰ ·
text: root.centerInTile && layoutActive && tileActive && hasValidPopupDropHint ? "⸰" : (modelData.t && modelData.t.length > 0 ? modelData.t : "")
font.pixelSize: 14
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
visible: root.centerInTile && layoutActive && tileActive && hasValidPopupDropHint ? true : (modelData.t ? modelData.t : false)
}
}
}
}
}
}
}
}
// Rectangle {
// id: autoTilerScrollLeft
// width: 34 + layoutGrid.columnSpacing * 2
// height: 34 + layoutGrid.columnSpacing * 2
// color: root.config.gridTilerOpacity != 1 ? Qt.alpha(colors.backgroundColor, root.config.gridTilerOpacity) : colors.backgroundColor
// border.color: colors.borderColor
// border.width: 1
// radius: 8
// visible: layouts.visible
// anchors.verticalCenter: layouts.verticalCenter
// anchors.right: layouts.left
// anchors.rightMargin: 2
// Text {
// anchors.centerIn: parent
// color: colors.textColor
// textFormat: Text.StyledText
// text: "◀"
// font.pixelSize: 14
// font.family: "Noto Sans"
// horizontalAlignment: Text.AlignHCenter
// }
// }
// Rectangle {
// id: autoTilerScrollRight
// width: 34 + layoutGrid.columnSpacing * 2
// height: 34 + layoutGrid.columnSpacing * 2
// color: root.config.gridTilerOpacity != 1 ? Qt.alpha(colors.backgroundColor, root.config.gridTilerOpacity) : colors.backgroundColor
// border.color: colors.borderColor
// border.width: 1
// radius: 8
// visible: layouts.visible
// anchors.verticalCenter: layouts.verticalCenter
// anchors.left: layouts.right
// anchors.leftMargin: 2
// Text {
// anchors.centerIn: parent
// color: colors.textColor
// textFormat: Text.StyledText
// text: "▶"
// font.pixelSize: 14
// font.family: "Noto Sans"
// horizontalAlignment: Text.AlignHCenter
// }
// }
Rectangle {
id: virtualDesktop
width: layoutGrid.implicitWidth + layoutGrid.columnSpacing * 2
height: virtualDesktopLayout.implicitHeight + layoutGrid.rowSpacing * 2
color: root.config.gridTilerOpacity != 1 ? Qt.alpha(colors.backgroundColor, root.config.gridTilerOpacity) : colors.backgroundColor
border.color: colors.borderColor
border.width: 1
radius: 8
visible: root.virtualDesktopVisibile
anchors.left: parent.left
anchors.leftMargin: positionX
anchors.top: layouts.visible ? layouts.bottom : parent.top
anchors.topMargin: layouts.visible ? 2 : positionY
GridLayout {
id: virtualDesktopLayout
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
rowSpacing: 1
columnSpacing: 1
columns: layoutGrid.implicitWidth / 35
Repeater {
id: virtualDesktopRepeater
model: root.virtualDesktops
Item {
id: virtualDesktop
property bool currentVirtualDesktop: root.currentVirtualDesktopIndex == index
property bool activeVirtualDesktop: activeVirtualDesktopIndex == index
width: 34
height: 34
Rectangle {
anchors.fill: parent
anchors.margins: tilePadding
border.color: colors.tileBorderColor
border.width: 1
color: "transparent"
radius: 6
Rectangle {
anchors.fill: parent
color: activeVirtualDesktop ? colors.tileBackgroundColorActive : colors.tileBackgroundColor
radius: 6
}
Text {
anchors.centerIn: parent
color: colors.textColor
textFormat: Text.StyledText
text: modelData.isAdd ? "+" : "⸰"
font.pixelSize: 14
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
visible: modelData.isAdd || currentVirtualDesktop
}
}
}
}
}
}
Rectangle {
id: popupHint
width: layoutGrid.implicitWidth + layoutGrid.columnSpacing * 2
height: popupHintText.implicitHeight + layoutGrid.rowSpacing * 2
color: root.config.gridTilerOpacity != 1 ? Qt.alpha(colors.backgroundColor, root.config.gridTilerOpacity) : colors.backgroundColor
border.color: colors.borderColor
border.width: 1
radius: 8
visible: root.config.showTextHint && hint != null
anchors.left: parent.left
anchors.leftMargin: positionX
anchors.top: virtualDesktop.visible ? virtualDesktop.bottom : layouts.bottom
anchors.topMargin: 2
Text {
id: popupHintText
width: parent.width - 4
anchors.centerIn: parent
color: colors.textColor
textFormat: Text.StyledText
text: hint != null ? hint : ""
font.pixelSize: 12
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
}
Rectangle {
id: popupWindowCursor
anchors.left: parent.left
anchors.leftMargin: root.getCursorPosition().x - clientArea.x - 6
anchors.top: parent.top
anchors.topMargin: root.getCursorPosition().y - clientArea.y - 6
width: 12
height: 12
border.color: colors.tileBorderColor
border.width: 2
color: colors.tileBackgroundColorActive
radius: 6
visible: !root.useMouseCursor
Rectangle {
anchors.centerIn: parent
width: 2
height: 2
color: colors.textColor
radius: 1
opacity: 0.8
}
}
Timer {
interval: 1
repeat: false
running: popupTiler.visible && !sizeEstablished
onTriggered: {
sizeEstablished = true;
updateScreen(true);
}
}
Timer {
interval: root.config.popupGridPollingRate
repeat: true
running: popupTiler.visible
onTriggered: {
let forceUpdate = lastShowAll != showAll;
if (forceUpdate) {
if (showAll) {
lastShowAll = true;
} else {
lastShowAll = false;
}
}
updateScreen(forceUpdate);
updateRevealed();
let x = root.getCursorPosition().x;
let y = root.getCursorPosition().y;
let layoutIndex = -1;
let tileIndex = -1;
let virtualDesktopIndex = -1;
if (root.config.windowVisibility == 2) {
var updatedHovered;
let layoutsPosition = layouts.mapToGlobal(Qt.point(0, 0));
let visibleHeight = (layouts.visible ? layouts.height : 0) + (virtualDesktop.visible ? virtualDesktop.height : 0) + (layouts.visible && virtualDesktop.visible ? 2 : 0)
if (layoutsPosition.x <= x && layoutsPosition.x + layouts.width >= x && layoutsPosition.y <= y && layoutsPosition.y + visibleHeight >= y) {
updatedHovered = true;
} else {
updatedHovered = false;
}
if (updatedHovered != currentlyHovered) {
currentlyHovered = updatedHovered;
root.updateWindowVisibility();
}
}
if (layouts.visible) {
for (let i = 0; i < layoutRepeater.count; i++) {
let currentLayout = layoutRepeater.itemAt(i);
let currentLayoutPosition = currentLayout.mapToGlobal(Qt.point(0, 0));
if (currentLayoutPosition.x <= x && currentLayoutPosition.x + currentLayout.width >= x && currentLayoutPosition.y <= y && currentLayoutPosition.y + currentLayout.height >= y) {
layoutIndex = i;
// if (layoutRepeater.model[layoutIndex].special) {
// tileIndex = 0;
// } else {
//for (let j = 0; j < currentLayout.children.length; j++) {
for (let j = currentLayout.children.length - 1; j >= 0; j--) {
let currentTile = currentLayout.children[j];
// if (currentTile.tileDisabled) {
// continue;
// }
let currentTilePosition = currentTile.mapToGlobal(Qt.point(0, 0));
if (currentTilePosition.x <= x && currentTilePosition.x + currentTile.width >= x && currentTilePosition.y <= y && currentTilePosition.y + currentTile.height >= y) {
tileIndex = j;
break;
}
}
// }
break;
}
}
}
if (root.virtualDesktopVisibile && layoutIndex == -1) {
for (let i = 0; i < virtualDesktopRepeater.count; i++) {
let currentVirtualDesktop = virtualDesktopRepeater.itemAt(i);
let currentVirtualDesktopPosition = currentVirtualDesktop.mapToGlobal(Qt.point(0, 0));
if (currentVirtualDesktopPosition.x <= x && currentVirtualDesktopPosition.x + currentVirtualDesktop.width >= x && currentVirtualDesktopPosition.y <= y && currentVirtualDesktopPosition.y + currentVirtualDesktop.height >= y) {
virtualDesktopIndex = i;
}
}
}
let switchVirtualDesktop = virtualDesktopIndex != -1 && !root.virtualDesktops[virtualDesktopIndex].isAdd && virtualDesktopIndex != root.currentVirtualDesktopIndex;
if (activeVirtualDesktopIndex != virtualDesktopIndex) {
activeVirtualDesktopIndex = virtualDesktopIndex;
if (switchVirtualDesktop) {
if (config.virtualDesktopHoverTime == 0) {
autoTiler.virtualDesktopAboutToChange(); // Notify to remove window from current virtual desktop auto-tiler
Workspace.currentDesktop = root.virtualDesktops[activeVirtualDesktopIndex].desktop;
} else {
activeVirtualDesktopHoverTime = Date.now();
}
}
updateHintContent();
} else if (switchVirtualDesktop && config.virtualDesktopHoverTime > 0 && Date.now() - activeVirtualDesktopHoverTime > config.virtualDesktopHoverTime) {
autoTiler.virtualDesktopAboutToChange(); // Notify to remove window from current virtual desktop auto-tiler
Workspace.currentDesktop = root.virtualDesktops[activeVirtualDesktopIndex].desktop;
updateHintContent();
}
// if (layoutIndex == -1) {
// let leftScroll = autoTilerScrollLeft.mapToGlobal(Qt.point(0, 0));
// let rightScroll = autoTilerScrollRight.mapToGlobal(Qt.point(0, 0));
// if (leftScroll.x <= x && leftScroll.x + autoTilerScrollLeft.width >= x && leftScroll.y <= y && leftScroll.y + autoTilerScrollLeft.height >= y) {
// logDev('HOVERING LEFT!!!!');
// let timeNow = Date.now();
// if (scrollLeftTime == 0) {
// scrollLeftTime = timeNow + 1000;
// } else if (timeNow >= scrollLeftTime) {
// autoTiler.modifyPrimaryIndex(-1);
// scrollLeftTime = timeNow + 1000;
// }
// } else if (rightScroll.x <= x && rightScroll.x + autoTilerScrollRight.width >= x && rightScroll.y <= y && rightScroll.y + autoTilerScrollRight.height >= y) {
// logDev('HOVERING RIGHT!!!!');
// let timeNow = Date.now();
// if (scrollLeftTime == 0) {
// scrollLeftTime = timeNow + 1000;
// } else if (timeNow >= scrollLeftTime) {
// autoTiler.modifyPrimaryIndex(-1);
// scrollLeftTime = timeNow + 1000;
// }
// } else {
// scrollLeftTime = 0;
// scrollRightTime = 0;
// }
// } else {
// scrollLeftTime = 0;
// scrollRightTime = 0;
// }
// TODO: Add support for negative values (-)
// TODO: Span more than 1 layout if x < 0 || y < 0 || w > 100 || h > 100 to support SPECIAL_EMPTY
if (layoutIndex != activeLayoutIndex || tileIndex != activeTileIndex) {
activeLayoutIndex = layoutIndex;
activeTileIndex = tileIndex;
updateHintContent();
} else if (popupDropHintIsCenterInTile != root.centerInTile && activeLayoutIndex >= 0 && activeTileIndex >= 0) {
updateHintContent();
popupDropHintIsCenterInTile = root.centerInTile;
}
}
}
}
}
@@ -0,0 +1,517 @@
import QtQuick
import org.kde.kwin
import org.kde.plasma.core as PlasmaCore
import org.kde.kirigami as Kirigami
import org.kde.plasma.components as PlasmaComponents
// Window {
PlasmaCore.Dialog {
id: windowSuggestions
property var activeScreen: null
property var currentDesktop: null
property var currentActivity: null
property bool isDefault: true
property int layoutIndex: 0
property int tileIndex: 0
property var clientArea: ({width: 1, height: 1, x: 0, y: 0})
property var tilePadding: 2
property list<var> convertedOverlay: ([])
property int skipIndex: -1
property int activeIndex: -1
property int suggestionsVisibility: 0
property bool suggestionsInsideTile: true
property bool excludeOtherScreens: true
property bool excludeOtherDesktops: true
property bool excludeOtherActivities: true
property bool excludeAutoTiled: true
property bool excludeMinimized: true
property int sortMode: 2
property int colCount: 1
property int rowCount: 1
property int cellWidth: 0
property int previewHeight: 0
property real cellRatio: 0
property int cellHeight: 0
property int cellBottomMargin: 0
property int textSize: 14
property int totalWidth: 0
property int totalHeight: 0
property int offsetX: 0
property int offsetY: 0
property int gridSpacing: 20
property list<var> validWindows: ([])
width: clientArea.width
height: clientArea.height
x: clientArea.x
y: clientArea.y
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.BypassWindowManagerHint
// flags: Qt.Tool | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus
// flags: Qt.Tool | Qt.BypassWindowManagerHint | Qt.FramelessWindowHint
// color: "transparent" // PlasmaCore.Dialog
visible: false
// outputOnly: true
backgroundHints: PlasmaCore.Types.NoBackground // PlasmaCore.Dialog
// type: PlasmaCore.Dialog.OnScreenDisplay
location: PlasmaCore.Types.Desktop // PlasmaCore.Dialog
function init() {
suggestionsVisibility = KWin.readConfig("suggestionsVisibility", 0);
suggestionsInsideTile = KWin.readConfig("suggestionsMode", 0) == 0;
excludeOtherScreens = !KWin.readConfig("suggestOtherScreens", false);
excludeOtherDesktops = !KWin.readConfig("suggestOtherDesktops", false);
excludeOtherActivities = !KWin.readConfig("suggestOtherActivities", false);
excludeAutoTiled = !KWin.readConfig("suggestAutoTiled", false);
excludeMinimized = !KWin.readConfig("suggestMinimized", false);
}
function updateValid(addedWindow) {
let valid = [];
for (let i = 0; i < Workspace.stackingOrder.length; i++) {
let window = Workspace.stackingOrder[i];
if (window == addedWindow) continue;
if (!root.isValidWindow(window)) continue;
if (window.fullScreen) continue;
// if (window.maximizeMode > 0) continue;
if (excludeAutoTiled && window.mt_auto) continue;
if (excludeMinimized && window.minimized) continue;
if (excludeOtherScreens && window.output != activeScreen) continue;
if (excludeOtherDesktops && window.desktops.length > 0 && !window.desktops.includes(currentDesktop)) continue;
if (excludeOtherActivities && window.activities.length > 0 && !window.activities.includes(currentActivity)) continue;
valid.push(window);
}
switch (sortMode) {
case 0: // newest focused first
valid.reverse();
break;
case 1: // oldest focused first
break;
case 2: // a-z
valid.sort((a, b) => a.caption.toLowerCase().localeCompare(b.caption.toLowerCase()));
break;
}
validWindows = valid;
}
function updateSizes() {
let count = validWindows.length;
log('Valid count: ' + count);
let usableHeight = suggestionsInsideTile ? convertedOverlay[activeIndex].height : clientArea.height - 180;
let usableWidth = suggestionsInsideTile ? convertedOverlay[activeIndex].width : clientArea.width / 2;
let wantedRatio = 16 / 9;
let ratio = usableWidth / usableHeight;
if (!suggestionsInsideTile) {
if (ratio < wantedRatio) {
usableWidth = parseInt(usableWidth);
usableHeight = parseInt(usableWidth / wantedRatio);
} else {
usableHeight = parseInt(usableHeight);
usableWidth = parseInt(usableHeight * wantedRatio);
}
}
colCount = Math.ceil(Math.sqrt(count));
rowCount = Math.ceil(count / colCount);
cellWidth = (usableWidth - gridSpacing * (colCount + 1)) / Math.max(colCount, 2);
log('CellWidth before: ' + cellWidth);
if (suggestionsInsideTile) {
if (cellWidth < 320) {
colCount = Math.min(count, Math.floor(convertedOverlay[activeIndex].width / 320));
} else {
colCount = Math.min(count, Math.floor(convertedOverlay[activeIndex].width / cellWidth));
}
cellWidth = (convertedOverlay[activeIndex].width - gridSpacing * (colCount + 1)) / Math.max(colCount, 2);
}
log('ColCount: ' + colCount + ' width: ' + cellWidth + ' gridSpacing: ' + gridSpacing + ' width: ' + convertedOverlay[activeIndex].width + ' usable: ' + usableWidth);
cellBottomMargin = Kirigami.Units.iconSizes.large / 4;
previewHeight = (usableHeight - gridSpacing * (rowCount + 1)) / Math.max(colCount, 2);
if (suggestionsInsideTile && previewHeight < 200) {
rowCount = Math.ceil(count / colCount);
previewHeight = 200;
}
cellHeight = previewHeight - cellBottomMargin - textSize;
totalWidth = suggestionsInsideTile ? convertedOverlay[activeIndex].width : cellWidth * Math.max(colCount, 2) + gridSpacing * (colCount + 1);
totalHeight = suggestionsInsideTile ? convertedOverlay[activeIndex].height : previewHeight * rowCount + gridSpacing * (rowCount + 1);
cellRatio = cellWidth / previewHeight;
log('Count: ' + count + ' rowCount: ' + rowCount + ' colCount: ' + colCount + ' width: ' + usableWidth + ' height: ' + usableHeight + ' ratio: ' + ratio + ' wanted ratio: ' + wantedRatio + ' cell width: ' + cellWidth + ' cell height: ' + cellHeight + ' totalHeight: ' + totalHeight);
}
function showSuggestions(window, screen, virtualDesktop, activity, isDefault, layoutIndex, tileIndex) {
log('screen: ' + screen.name + ' virtualDesktop: ' + virtualDesktop.id + ' activity: ' + activity + ' isDefault: ' + isDefault + ' layoutIndex: ' + layoutIndex + ' tileIndex: ' + tileIndex);
activeScreen = screen;
currentDesktop = virtualDesktop;
currentActivity = activity;
windowSuggestions.isDefault = isDefault;
windowSuggestions.layoutIndex = layoutIndex;
windowSuggestions.tileIndex = tileIndex;
clientArea = Workspace.clientArea(KWin.FullScreenArea, activeScreen, currentDesktop);
activeIndex = 0;
skipIndex = tileIndex;
convertLayoutToScreen();
updateValid(window);
if (validWindows.length > 0 && convertedOverlay.length > 0) {
updateSizes();
updateOffsets();
windowSuggestions.visible = true;
}
}
function convertLayoutToScreen() {
let layout = isDefault ? popupGridLayouts[layoutIndex].tiles : popupGridAllLayouts[layoutIndex].tiles;
log('Window suggestions layout to convert: ' + JSON.stringify(layout));
let converted = [];
for (let i = 0; i < layout.length; i++) {
if (i == skipIndex) continue;
let tile = layout[i];
let width = (tile.pxW == undefined ? tile.w / 100 * windowSuggestions.width : tile.pxW);
let height = (tile.pxH == undefined ? tile.h / 100 * windowSuggestions.height : tile.pxH);
// Temporarily add clientArea x/y since it is needed for calculating margins
let geometry = {
x: clientArea.x + ((tile.pxX == undefined ? tile.x / 100 * windowSuggestions.width : tile.pxX) - (tile.aX == undefined ? 0 : tile.aX * width / 100)),
y: clientArea.y + ((tile.pxY == undefined ? tile.y / 100 * windowSuggestions.height : tile.pxY) - (tile.aY == undefined ? 0 : tile.aY * height / 100)),
width: width,
height: height,
tileIndex: i
};
root.addMargins(geometry, true, true, true, true);
geometry.x -= clientArea.x;
geometry.y -= clientArea.y;
converted.push(geometry);
}
windowSuggestions.convertedOverlay = converted;
}
function updateOffsets() {
if (activeIndex < convertedOverlay.length) {
if (suggestionsInsideTile) {
offsetX = convertedOverlay[activeIndex].x;
offsetY = convertedOverlay[activeIndex].y;
} else {
let overlay = convertedOverlay[activeIndex];
let rightSpace = clientArea.width - overlay.x - overlay.width;
if (overlay.x < rightSpace) {
offsetX = Math.min(clientArea.width - rightSpace + 2, clientArea.width - totalWidth - 2);
} else {
offsetX = Math.max(overlay.x - totalWidth - 2, 2);
}
offsetY = overlay.y - ((totalHeight + 80 - overlay.height) / 2);
if (offsetY < 0) {
offsetY = 0;
} else if (offsetY + totalHeight + 80 > clientArea.height) {
offsetY = clientArea.height - totalHeight - 80;
}
}
}
}
function selectSuggestion(index, tileIndex) {
log('Select suggestion: ' + index + ' ' + tileIndex);
activeIndex = index;
if (suggestionsInsideTile) {
updateSizes();
}
updateOffsets();
}
function addWindowToSelected(window) {
log('Selected window caption: ' + window.caption);
if (activeIndex != -1 && validWindows.length > 0) {
let localWindows = [...validWindows];
let overlays = JSON.parse(JSON.stringify(windowSuggestions.convertedOverlay)); // Workaround for a strange bug where wrong item is fetched even if activeIndex is correct
let geometry = overlays.splice(activeIndex, 1)[0];
windowSuggestions.convertedOverlay = [...overlays];
geometry.x += clientArea.x;
geometry.y += clientArea.y;
let index = localWindows.indexOf(window);
if (index != -1) {
localWindows.splice(index, 1);
if (!window.desktops.includes(currentDesktop)) {
window.desktops = [currentDesktop];
}
if (!window.activities.includes(currentActivity)) {
window.activities = [currentActivity];
}
if (window.mt_auto) {
autoTiler.disableAutoTiling(window);
}
if (window.minimized) {
window.minimized = false;
}
if (window.maximizeMode > 0) {
window.setMaximize(false, false);
}
if (!window.mt_originalSize) {
window.mt_originalSize = {x: window.x, y: window.y, width: window.width, height: window.height};
}
root.moveAndResizeWindow(window, geometry);
Workspace.raiseWindow(window);
activeIndex = 0;
if (localWindows.length == 0 || convertedOverlay.length == 0) {
windowSuggestions.visible = false;
validWindows = [];
} else {
validWindows = localWindows;
if (suggestionsInsideTile) {
updateSizes();
}
updateOffsets();
}
}
}
}
function windowClosed(window) {
log('Closed window caption: ' + window.caption);
if (activeIndex != -1 && validWindows.length > 0) {
let localWindows = [...validWindows];
let index = localWindows.indexOf(window);
if (index != -1) {
localWindows.splice(index, 1);
if (localWindows.length == 0) {
validWindows = [];
} else {
validWindows = localWindows;
if (suggestionsInsideTile) {
updateSizes();
}
}
}
}
}
Item {
id: mainItem
width: windowSuggestions.width
height: windowSuggestions.height
TapHandler {
onTapped: windowSuggestions.visible = false;
}
Colors {
id: colors
}
Repeater {
id: tileRepeater
model: windowSuggestions.convertedOverlay
Item {
id: tile
property bool active: activeIndex == index
property int tileIndex: modelData.tileIndex
x: modelData.x
y: modelData.y
width: modelData.width
height: modelData.height
Rectangle {
anchors.fill: parent
anchors.margins: tilePadding
border.color: colors.tileBorderColor
border.width: 2
color: active ? colors.tileBackgroundColorActive : colors.tileBackgroundColorIntense
radius: 12
Text {
anchors.centerIn: parent
color: colors.overlayTextColor
textFormat: Text.StyledText
text: "<b>Select a window to tile here</b>"
font.pixelSize: 16
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
visible: active && !suggestionsInsideTile
}
TapHandler {
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: selectSuggestion(index, tileIndex);
}
}
}
}
Item {
height: suggestionsInsideTile ? totalHeight : totalHeight + 80
width: totalWidth
anchors.top: parent.top
// anchors.topMargin: (clientArea.height - height) / 2
anchors.topMargin: offsetY
anchors.left: parent.left
anchors.leftMargin: offsetX
Rectangle {
id: cellWrapper
width: totalWidth
height: suggestionsInsideTile ? totalHeight : totalHeight + 80
color: suggestionsInsideTile ? "transparent" : "#BB000000"
border.width: 1
border.color: suggestionsInsideTile ? "transparent" : "#80FFFFFF"
radius: 6
GridView {
id: listView
model: validWindows
width: colCount * (windowSuggestions.cellWidth + gridSpacing)
// height: rowCount * (previewHeight + gridSpacing)
height: suggestionsInsideTile ? Math.min((previewHeight + gridSpacing) * rowCount, totalHeight - 80 - gridSpacing * 2) : totalHeight - 80 - gridSpacing * 2
cellWidth: windowSuggestions.cellWidth + gridSpacing
cellHeight: previewHeight + gridSpacing
interactive: suggestionsInsideTile ? true : false
clip: suggestionsInsideTile
anchors.top: parent.top
anchors.topMargin: suggestionsInsideTile ? (cellWrapper.height - height) / 2 : 40 + gridSpacing
anchors.left: parent.left
anchors.leftMargin: suggestionsInsideTile ? (cellWrapper.width + gridSpacing - width) / 2 : gridSpacing
delegate: Rectangle {
width: windowSuggestions.cellWidth
height: previewHeight
color: "transparent"
property real previewRatio: modelData.width / modelData.height
property int previewActualWidth: previewRatio > cellRatio ? windowSuggestions.cellWidth : windowSuggestions.cellHeight * previewRatio
property int previewActualHeight: previewRatio > cellRatio ? windowSuggestions.cellWidth / previewRatio : windowSuggestions.cellHeight
WindowThumbnail {
id: preview
client: modelData
width: previewActualWidth
height: previewActualHeight
anchors.centerIn: parent
anchors.verticalCenterOffset: -((textSize + cellBottomMargin) / 2)
TapHandler {
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: addWindowToSelected(modelData);
}
Kirigami.Icon {
id: icon
width: Kirigami.Units.iconSizes.large
height: Kirigami.Units.iconSizes.large
source: modelData.icon
anchors.horizontalCenter: preview.horizontalCenter
anchors.verticalCenter: preview.bottom
anchors.verticalCenterOffset: -Math.round(height / 4)
PlasmaComponents.Label {
id: text
width: windowSuggestions.cellWidth
height: textSize
text: modelData.caption
anchors.top: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
color: "white"
background: Rectangle {
anchors.centerIn: parent
anchors.verticalCenterOffset: 3
height: parent.height + 4
width: parent.contentWidth + 6
color: "#66000000"
radius: 3
}
}
}
}
}
}
}
Rectangle {
anchors.top: parent.top
anchors.topMargin: suggestionsInsideTile ? 4 : 1
anchors.left: parent.left
anchors.leftMargin: suggestionsInsideTile ? 4 : 1
width: suggestionsInsideTile ? totalWidth - 8 : totalWidth - 2
height: 40;
color: "#EE333333"
radius: suggestionsInsideTile ? 12 : 6
Text {
anchors.centerIn: parent
color: "white"
textFormat: Text.StyledText
text: suggestionsInsideTile ? "Select a window to place here" : "Select a window to place in the highlighted tile"
font.pixelSize: 16
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
}
Text {
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
color: "white"
textFormat: Text.StyledText
text: "✖"
font.pixelSize: 16
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
}
TapHandler {
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: windowSuggestions.visible = false;
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: suggestionsInsideTile ? 4 : 1
anchors.left: parent.left
anchors.leftMargin: suggestionsInsideTile ? 4 : 1
width: suggestionsInsideTile ? totalWidth - 8 : totalWidth - 2
height: 40;
color: "#EE333333"
radius: suggestionsInsideTile ? 12 : 6
Text {
anchors.centerIn: parent
color: "white"
textFormat: Text.StyledText
text: "Click a tile to switch to it. Click anywhere else to close suggestions. Disable suggestions (<b>" + config.shortcutToggleTilingSuggestions + "</b>)"
font.pixelSize: 16
font.family: "Noto Sans"
horizontalAlignment: Text.AlignHCenter
}
TapHandler {
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: windowSuggestions.visible = false;
}
}
}
}
}
@@ -0,0 +1,20 @@
{
"KPackageStructure": "KWin/Script",
"KPlugin": {
"Authors": [
{
"Name": "rxappdev"
}
],
"Name": "Mouse Tiler",
"Description": "Tile your windows with minimal effort",
"Icon": "preferences-desktop-virtual",
"Id": "mousetiler",
"Version": "6.4.0",
"License": "GPLv3",
"Website": "https://github.com/rxappdev/MouseTiler"
},
"X-Plasma-API": "declarativescript",
"X-Plasma-MainScript": "ui/main.qml",
"X-KDE-ConfigModule": "kwin/effects/configs/kcm_kwin4_genericscripted"
}