Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1ea364c
Add gschema
leonardo-lemos May 30, 2025
762da1f
Implement MonitorLayoutManager
leonardo-lemos May 30, 2025
dd6c098
Use MonitorLayoutManager
leonardo-lemos May 30, 2025
b2c0ff1
Fix build
leonardo-lemos May 30, 2025
36dd686
Merge branch 'main' into introduce-layout-manager
leonardo-lemos May 30, 2025
441b280
Notify monitor number change only once
leonardo-lemos May 31, 2025
12a1a02
Show display label only once
leonardo-lemos May 31, 2025
f860515
Rescan Displays only if monitors changed
leonardo-lemos May 31, 2025
6712461
Add display transformation saving
leonardo-lemos May 31, 2025
756c165
Removed unused variable
leonardo-lemos Jun 1, 2025
26bcac8
Merge branch 'main' into introduce-layout-manager
zeebok Dec 4, 2025
756431b
Merge branch 'main' into introduce-layout-manager
jeremypw May 22, 2026
3690b20
Drop unnecessary check
jeremypw May 27, 2026
e43a21c
Reduce scope of some functions
jeremypw May 27, 2026
a9f5d71
Simplify arrange_monitors() & get_layout_key()
jeremypw May 27, 2026
c5071c6
Lose unused object
jeremypw May 27, 2026
0e85221
Inline simplified find_match_layout()
jeremypw May 27, 2026
a90c359
Drop hash for key
jeremypw May 27, 2026
4442fa3
Inline build_layout_variant()
jeremypw May 27, 2026
844dc04
Inline add_or_update_layout()
jeremypw May 27, 2026
11ebe2c
Simplify save layout using VariantDict
jeremypw May 27, 2026
5ee9993
Do not arrange unnecessarily in get_monitor_config()
jeremypw May 27, 2026
1b7cdc9
Merge branch 'main' into introduce-layout-manager
jeremypw May 27, 2026
c07e6cb
Amend schema variant type to match that generated by simplified save …
jeremypw May 31, 2026
2b27cd6
Add comment and expanded schema description
jeremypw Jun 1, 2026
4d4b5b7
Fix saving and restoring layouts
jeremypw Jun 1, 2026
7c7b111
Save and restore which monitor is primary
jeremypw Jun 1, 2026
f8633d0
Remove extraneous code
jeremypw Jun 1, 2026
c741f5d
Remove unused
jeremypw Jun 1, 2026
e0f0912
Arrange monitors after getting config to fix toggling mirror mode
jeremypw Jun 1, 2026
6aaaf57
Rescan displays on monitors-changed signal to redraw after "Keep prev…
jeremypw Jun 1, 2026
a76d5b9
Save and restore is-active property
jeremypw Jun 2, 2026
fb242bc
Mention "is-active" property in schema description
jeremypw Jun 2, 2026
2af8f91
Update some comments
jeremypw Jun 2, 2026
e444c18
Merge branch 'main' into introduce-layout-manager
jeremypw Jul 1, 2026
611e65c
Merge branch 'main' into introduce-layout-manager
zeebok Jul 25, 2026
7e922bc
Merge branch 'main' into introduce-layout-manager
zeebok Aug 26, 2026
d9ec128
Label as widget
jeremypw Jun 2, 2026
9caf75f
Move inactive display widget to the side
jeremypw Jun 2, 2026
20edbfa
Show inactive display widgets at side (small)
jeremypw Jun 2, 2026
8bbd569
Position inactive widget correctly
jeremypw Jun 3, 2026
d5fe528
Different layout key when inactive monitors
jeremypw Jun 3, 2026
76572be
Popdown menu when in is-active changes
jeremypw Jun 3, 2026
a05482d
Arrange monitors when is-active changes
jeremypw Jun 3, 2026
1baebd2
Lose unneeded message
jeremypw Jun 3, 2026
fa97a45
Apply button sensitive after is-active change
jeremypw Jun 3, 2026
5b33a64
Revert redundant change
jeremypw Jun 3, 2026
44e298d
fix: Avoid trying to drag inactive displays
flodavid Sep 15, 2026
1375b6f
fix: Avoid aligning and checking intersection for inactive displays
flodavid Sep 15, 2026
99c832b
Use recursion level to stop aligning display, similiar to intersectio…
flodavid Sep 15, 2026
f81e0f1
fix: Recompute inactive display positions when activing display
flodavid Sep 19, 2026
7d4c9dd
fix: Sort monitors to have predictable layout keys
flodavid Sep 19, 2026
e93f75e
fix: x and y position could be wrong when enabling display
flodavid Oct 4, 2026
5d17110
Merge branch 'main' into testing/inactive-widget
jeremypw Oct 7, 2026
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
69 changes: 40 additions & 29 deletions src/Objects/MonitorLayoutManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,42 @@ public class Display.MonitorLayoutManager : GLib.Object {
Variant? monitors = null;
if (layouts != null) {
monitors = layouts.lookup_value (layout_key, VariantType.VARDICT);
foreach (var virtual_monitor in virtual_monitors) {
Variant? props = monitors.lookup_value (virtual_monitor.id, VariantType.VARDICT);
if (props != null) {
int32 x = 0, y = 0;
uint32 t = 0;
bool p = false, e = false;
if (props.lookup ("x", "i", out x) &&
props.lookup ("y", "i", out y) &&
props.lookup ("transform", "u", out t) &&
props.lookup ("primary", "b", out p) &&
props.lookup ("enabled", "b", out e)) {

virtual_monitor.x = x;
virtual_monitor.y = y;
virtual_monitor.transform = t;
virtual_monitor.primary = p;
virtual_monitor.is_active = e;
} else {
warning ("property setting missing for monitor %s", virtual_monitor.get_display_name ());
}
} else {
warning ("no property dictionary found for monitor.id %s", virtual_monitor.get_display_name ());
if (monitors != null) {
foreach (var virtual_monitor in virtual_monitors) {
Variant? props = monitors.lookup_value (virtual_monitor.id, VariantType.VARDICT);
if (props != null) {
debug ("monitor %s props: %s", virtual_monitor.id, props.print (false));
int32 x = 0, y = 0;
uint32 t = 0;
bool p = false, e = false;
if (props.lookup ("x", "i", out x) &&
props.lookup ("y", "i", out y) &&
props.lookup ("transform", "u", out t) &&
props.lookup ("primary", "b", out p) &&
props.lookup ("enabled", "b", out e)
) {
// Set transform first, since modifying it changes the transform selected value, which
// triggers configuration and positions checks, mofifying x and y
virtual_monitor.transform = t;
virtual_monitor.x = x;
virtual_monitor.y = y;
virtual_monitor.primary = p;
virtual_monitor.is_active = e;
} else {
warning ("no properties for monitor %s in a configuration that is supposed to include it",
virtual_monitor.get_display_name ());
}
} else {
warning ("no property dictionary found for monitor.id %s", virtual_monitor.get_display_name ());
}
}
} else {
warning ("no configuration for the layout %s ", layout_key);
}

return;
} else {
warning ("layout key %s not found", layout_key);
warning ("preferred layout key %s not found", PREFERRED_MONITOR_LAYOUTS_KEY);
}

// If no layout found, we save the current layout to use later
Expand All @@ -67,7 +75,6 @@ public class Display.MonitorLayoutManager : GLib.Object {

public void save_layout (Gee.LinkedList<VirtualMonitor> virtual_monitors) {
var save_key = get_layout_key (virtual_monitors);

var monitor_dict = new VariantDict ();
foreach (var monitor in virtual_monitors) {
var props_dict = new VariantDict ();
Expand All @@ -89,19 +96,23 @@ public class Display.MonitorLayoutManager : GLib.Object {
settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layouts_dict.end ());
}

/**
* Generate a unique key based on the virtual monitors' monitors hashes
*/
private string get_layout_key (Gee.LinkedList<VirtualMonitor> virtual_monitors) {
// Generate a unique key based on the virtual monitors' monitors hashes
//NOTE The key depends on the order of the list which will change depending on whether monitors are
// active or not (and possibly on the order they were connected).
//TODO Consider whether a more controlled key is needed
var sorted_virtual_monitor_list = virtual_monitors;
sorted_virtual_monitor_list.sort ((vm1, vm2) => strcmp (vm1.id, vm2.id));

var key = new StringBuilder ();

foreach (var virtual_monitor in virtual_monitors) {
foreach (var monitor in virtual_monitor.monitors) {
if (virtual_monitor.is_active) {
key.append ("-");
key.append (virtual_monitor.id);
}
}

key.erase (0, 1);

@flodavid flodavid Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment there would be nice, like Remove the dangling separator at the start

return key.str;
}
}
4 changes: 4 additions & 0 deletions src/Objects/MonitorManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ public class Display.MonitorManager : GLib.Object {
notify_property ("is-mirrored");
}

public void on_virtual_monitor_active_changed (VirtualMonitor vm) {
layout_manager.arrange_monitors (virtual_monitors);
}

private VirtualMonitor? get_virtual_monitor_by_id (string id) {
foreach (var vm in virtual_monitors) {
if (vm.id == id) {
Expand Down
11 changes: 10 additions & 1 deletion src/Widgets/DisplayWidget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,18 @@ public class Display.DisplayWidget : Gtk.Box {
display_label = new Gtk.Label (virtual_monitor_name) {
halign = CENTER,
valign = CENTER,
lines = 3,
ellipsize = END,
wrap = true,
wrap_mode = WORD,
hexpand = true,
vexpand = true
};

var grid = new Gtk.Grid ();
grid.attach (primary_image, 0, 0);
grid.attach (toggle_settings, 2, 0);
grid.attach (display_label, 0, 0, 3, 2);
grid.attach (display_label, 0, 1, 3, 1);

append (grid);

Expand All @@ -291,12 +295,17 @@ public class Display.DisplayWidget : Gtk.Box {

if (use_switch.active) {
remove_css_class ("disabled");
remove_css_class (Granite.STYLE_CLASS_DIM_LABEL);
remove_css_class (Granite.STYLE_CLASS_SMALL_LABEL);
} else {
add_css_class ("disabled");
add_css_class (Granite.STYLE_CLASS_DIM_LABEL);
add_css_class (Granite.STYLE_CLASS_SMALL_LABEL);
}

configuration_changed ();
active_changed ();
popover.popdown ();
});

if (!virtual_monitor.is_active) {
Expand Down
Loading
Loading