Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Imitates the way macOS handles maximized and full-screened windows.
## Functionality

### Core Features:
* **Automatic Virtual Desktop Creation**: The script moves full-screened and/or (fully) maximized windows to a new virtual desktop.
* **Smart Virtual Desktop Creation**: The script intelligently manages virtual desktops based on window count. If a window is alone on its desktop, it maximizes in place. If multiple windows exist, the maximized window moves to a new virtual desktop, preventing empty desktops.
* **Desktop Restoration**: When a window is restored to regular state (not full-screened and not fully maximized), the script returns it to the main desktop and removes the temporary virtual desktop.
* **Main Desktop Focus**: New non-maximized windows opened while on a full-screen application's desktop are automatically moved to and opened on the main desktop.
* **Dialog & Toolbar Support**: Related windows (dialogs, toolbars, etc.) are automatically moved to the same desktop as their parent window.
Expand All @@ -16,10 +16,28 @@ Imitates the way macOS handles maximized and full-screened windows.

### Behavior Logic:
1. Initial state has only one virtual desktop (main desktop)
2. When a window is maximized, a new virtual desktop is created after all existing ones, and the window is moved to it
3. The new virtual desktop displays only that maximized window and its related dialogs/toolbars
4. When a window is un-maximized, its virtual desktop is deleted and the window returns to the main desktop
2. When a window is maximized:
- If it's the **only window** on the current desktop, it maximizes in place (no new desktop created)
- If there are **other windows** on the desktop, a new virtual desktop is created and the window is moved to it
3. The new virtual desktop (if created) displays only that maximized window and its related dialogs/toolbars
4. When a window is un-maximized, its virtual desktop is deleted (if applicable) and the window returns to the main desktop
5. When on a non-main desktop (full-screen app), opening a new non-maximized app forces it to open on the main desktop and switches to it

## Configuration

You can configure MACsimize6 in **System Settings** → **Window Management** → **KWin Scripts** → **MACsimize6** → **Configure**

### Available Options:

- **Handle Fullscreen**: Enable/disable fullscreen window management
- **Handle Maximized**: Enable/disable maximized window management
- **Move to Last**: Create new desktops at the end instead of next to current desktop
- **Enable if Only One**: Only enable script when using a single monitor
- **Enable Panel Visibility**: Auto-hide panel on non-main desktops
- **Exclusive Desktops**: Force new windows to open on main desktop when on a fullscreen app
- **Prevent Empty Desktop**: When enabled, maximizing the only window on a desktop keeps it in place instead of creating a new desktop (default: enabled)
- **Debug Mode**: Enable debug logging
- **Skip Windows**: Comma-separated list of applications to exclude from all rules

### Preview:
![Macsimize6](https://github.com/Ubiquitine/MACsimize6/assets/3274951/354014b3-5ea0-49ff-b2a2-5aab27471845)
50 changes: 50 additions & 0 deletions contents/code/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var enableIfOnlyOne = readConfig("enableIfOnlyOne", false);
var enablePanelVisibility = readConfig("enablePanelVisibility", false);
var exclusiveDesktops = readConfig("exclusiveDesktops", true)
var debugMode = readConfig("debugMode", false)
var preventEmptyDesktop = readConfig("preventEmptyDesktop", true)

function log(msg) {
if (debugMode) {
Expand Down Expand Up @@ -119,6 +120,43 @@ function moveToNewDesktop(window) {
log(`Window: ${windowId} is already on separate desktop`);
return;
} else {
// Check if there are other normal windows on the current desktop (if preventEmptyDesktop is enabled)
if (preventEmptyDesktop) {
let currentDesktop = workspace.currentDesktop;
let otherWindowsOnDesktop = 0;

for (let i = 0; i < workspace.windowList().length; i++) {
let w = workspace.windowList()[i];
// Count only normal windows that are not the current window, not skipped, and on the current desktop
if (w.internalId !== windowId &&
!shouldSkip(w) &&
w.normalWindow &&
!w.skipTaskbar &&
w.desktops.includes(currentDesktop)) {
otherWindowsOnDesktop++;
log(`Found other window on desktop: ${w.resourceClass.toString()}`);
}
}

log(`Other windows on current desktop: ${otherWindowsOnDesktop}`);

// If this is the only window on the current desktop, don't create a new desktop
if (otherWindowsOnDesktop === 0) {
log(`Window ${windowId} is the only window on desktop. Not creating new desktop.`);
// Just mark it as macsimized in place
updateSavedData(windowId, {
resourceClass: window.resourceClass.toString(),
desktops: window.desktops,
macsimized: true
});

if (!managedDesktops.includes(currentDesktop)) {
managedDesktops.push(currentDesktop);
}
return;
}
}

log(`Creating new desktop with name: ${windowName}`);
let newDesktopNumber = -1;

Expand Down Expand Up @@ -190,6 +228,18 @@ function restoreDesktop(window) {
// Remove MACsimized indicator for the window
deleteSavedData(windowId, "macsimized");

// Check if the window is already on the main desktop (macsimized in place)
let mainDesktop = workspace.desktops[0];
if (windowDesktop === mainDesktop) {
log(`Window ${windowId} was macsimized in place on main desktop. Not moving.`);
// Remove from managed desktops if it's there
let idx = managedDesktops.indexOf(windowDesktop);
if (idx !== -1) {
managedDesktops.splice(idx, 1);
}
return;
}

// Delete the window's desktop and move the window to the main desktop
window.desktops = [workspace.desktops[0]];
cleanDesktop(windowDesktop);
Expand Down
3 changes: 3 additions & 0 deletions contents/config/main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<entry name="debugMode" type="bool">
<default>false</default>
</entry>
<entry name="preventEmptyDesktop" type="bool">
<default>true</default>
</entry>
<entry name="SkipWindows" type="string">
<default>lattedock, latte-dock, org.kde.spectacle, spectacle, org.kde.yakuake</default>
</entry>
Expand Down
16 changes: 13 additions & 3 deletions contents/ui/config.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>334</width>
<height>59</height>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -75,13 +75,23 @@
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="kcfg_preventEmptyDesktop">
<property name="text">
<string>Prevent empty desktop (maximize single windows in place)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="Ignore">
<property name="text">
<string>Ignore:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QLineEdit" name="kcfg_SkipWindows">
<property name="text">
<string>lattedock, latte-dock, org.kde.spectacle, spectacle, org.kde.yakuake</string>
Expand All @@ -91,7 +101,7 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QCheckBox" name="kcfg_debugMode">
<property name="text">
<string>Enable debug in kwin logs</string>
Expand Down