diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index f7cdb687..2f23642d 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -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 @@ -67,7 +75,6 @@ public class Display.MonitorLayoutManager : GLib.Object { public void save_layout (Gee.LinkedList 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 (); @@ -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 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); return key.str; } } diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index 57a5b2c5..9e97c1c5 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -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) { diff --git a/src/Widgets/DisplayWidget.vala b/src/Widgets/DisplayWidget.vala index fb8d6a7a..fedb8d16 100644 --- a/src/Widgets/DisplayWidget.vala +++ b/src/Widgets/DisplayWidget.vala @@ -267,6 +267,10 @@ 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 }; @@ -274,7 +278,7 @@ public class Display.DisplayWidget : Gtk.Box { 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); @@ -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) { diff --git a/src/Widgets/DisplaysOverlay.vala b/src/Widgets/DisplaysOverlay.vala index 1ca4fc1e..7472acbe 100644 --- a/src/Widgets/DisplaysOverlay.vala +++ b/src/Widgets/DisplaysOverlay.vala @@ -41,8 +41,11 @@ public class Display.DisplaysOverlay : Gtk.Box { private unowned Display.MonitorManager monitor_manager; private GalaDBus gala_dbus = null; public int active_displays { get; set; default = 0; } + /// Number of inactive displays. This should never be decreased. Always go up from 0 + private int inactive_displays = 0; private List display_widgets; + private DisplayWidget? dragging_display = null; public bool only_display { get { @@ -129,10 +132,12 @@ public class Display.DisplaysOverlay : Gtk.Box { Gtk.Allocation alloc; prev_dx = 0; prev_dy = 0; - foreach (var display_widget in display_widgets) { + foreach (unowned var display_widget in display_widgets) { get_child_position (display_widget, out alloc); if (start_rect.intersect (alloc, null)) { - dragging_display = display_widget; + if (display_widget.virtual_monitor.is_active) { + dragging_display = display_widget; + } break; } } @@ -159,29 +164,35 @@ public class Display.DisplaysOverlay : Gtk.Box { // virtual monitor geometry and any offsets when dragging. private bool get_child_position (Gtk.Widget widget, out Gdk.Rectangle allocation) { allocation = Gdk.Rectangle (); - if (current_width != get_width () || - current_height != get_height ()) { + if ((current_width != (get_width () - inactive_displays > 0 ? 96 : 0)) || + (current_height != get_height ())) { calculate_ratio (); } - if (widget is DisplayWidget) { - var display_widget = (DisplayWidget) widget; - - int x, y, width, height; - display_widget.get_virtual_monitor_geometry (out x, out y, out width, out height); - var x_start = (int) Math.round (x * current_ratio); - var y_start = (int) Math.round (y * current_ratio); - var x_end = (int) Math.round ((x + width) * current_ratio); - var y_end = (int) Math.round ((y + height) * current_ratio); - allocation.x = default_x_margin + x_start; - allocation.y = default_y_margin + y_start; - allocation.width = x_end - x_start; - allocation.height = y_end - y_start; + if (!(widget is DisplayWidget) || !((DisplayWidget)widget).virtual_monitor.is_active) { + int n_inactive = widget.get_data ("n-inactive"); + allocation.x = 0; + allocation.width = 96; + allocation.height = 96; + allocation.y = (n_inactive - 1) * allocation.height; return true; } - return false; + + int x, y, width, height; + var display_widget = (DisplayWidget) widget; + display_widget.get_virtual_monitor_geometry (out x, out y, out width, out height); + var x_start = (int) Math.round (x * current_ratio); + var y_start = (int) Math.round (y * current_ratio); + var x_end = (int) Math.round ((x + width) * current_ratio); + var y_end = (int) Math.round ((y + height) * current_ratio); + allocation.x = default_x_margin + x_start; + allocation.y = default_y_margin + y_start; + allocation.width = x_end - x_start; + allocation.height = y_end - y_start; + + return true; } public void rescan_displays () { @@ -193,8 +204,9 @@ public class Display.DisplaysOverlay : Gtk.Box { }); active_displays = 0; + inactive_displays = 0; + foreach (var virtual_monitor in monitor_manager.virtual_monitors) { - active_displays += virtual_monitor.is_active ? 1 : 0; add_output (virtual_monitor); } @@ -240,14 +252,18 @@ public class Display.DisplaysOverlay : Gtk.Box { } private void change_active_displays_sensitivity () { + } private void check_configuration_change () { // check if valid (connected) int active_display_count = 0; + int inactive_displays = 0; foreach (unowned var dw in display_widgets) { if (dw.virtual_monitor.is_active) { active_display_count++; + } else { + inactive_displays++; } } @@ -306,8 +322,14 @@ public class Display.DisplaysOverlay : Gtk.Box { int added_height = 0; int max_width = int.MIN; int max_height = int.MIN; + int min_x = int.MAX; + int min_y = int.MAX; foreach (unowned var display_widget in display_widgets) { + if (!display_widget.virtual_monitor.is_active) { + continue; + } + int x, y, width, height; display_widget.get_virtual_monitor_geometry (out x, out y, out width, out height); @@ -315,16 +337,39 @@ public class Display.DisplaysOverlay : Gtk.Box { added_height += height; max_width = int.max (max_width, x + width); max_height = int.max (max_height, y + height); + min_x = int.min (min_x, x); + min_y = int.min (min_y, y); } + // Origin may not be at (0, 0) while a monitor is marked inactive but not yet applied + max_width -= min_x; + max_height -= min_y; + current_width = get_width (); + current_width -= inactive_displays > 0 ? 96 : 0; current_height = get_height (); current_ratio = double.min ( - (double) (get_width () - 24) / (double) added_width, - (double) (get_height () - 24) / (double) added_height + (double) (current_width - 24) / (double) added_width, + (double) (current_height - 24) / (double) added_height ); - default_x_margin = (int) ((get_width () - max_width * current_ratio) / 2); - default_y_margin = (int) ((get_height () - max_height * current_ratio) / 2); + + + default_x_margin = (int) (current_width - (max_width * current_ratio)) / 2; + default_x_margin += inactive_displays > 0 ? 96 : 0; // Allow for width of inactive widgets + default_x_margin -= (int) (min_x * current_ratio); // Allow for origin not being at 0, 0 + default_y_margin = (int) ((current_height - (max_height * current_ratio)) / 2); + default_y_margin -= (int) (min_y * current_ratio); // Allow for origin not being at 0, 0 + } + + private void refresh_inactive_display_indexes () { + inactive_displays = 0; + + foreach (unowned var display_widget in display_widgets) { + if (!display_widget.virtual_monitor.is_active) { + inactive_displays++; + display_widget.set_data ("n-inactive", inactive_displays); + } + } } private void add_output (Display.VirtualMonitor virtual_monitor) { @@ -338,6 +383,13 @@ public class Display.DisplaysOverlay : Gtk.Box { overlay.add_overlay (display_widget); display_widgets.append (display_widget); + if (virtual_monitor.is_active) { + active_displays++; + } else { + inactive_displays++; + display_widget.set_data ("n-inactive", inactive_displays); + } + display_widget.set_as_primary.connect (() => set_as_primary (display_widget.virtual_monitor)); display_widget.check_position.connect (() => { @@ -345,19 +397,32 @@ public class Display.DisplaysOverlay : Gtk.Box { }); display_widget.configuration_changed.connect (check_configuration_change); + display_widget.active_changed.connect (() => { - active_displays += virtual_monitor.is_active ? 1 : -1; - change_active_displays_sensitivity (); - check_configuration_change (); - calculate_ratio (); + if (virtual_monitor.is_active) { + refresh_inactive_display_indexes (); + display_widget.steal_data ("n-inactive"); + active_displays++; + } else { + inactive_displays++; + display_widget.set_data ("n-inactive", inactive_displays); + active_displays--; + } + + monitor_manager.on_virtual_monitor_active_changed (display_widget.virtual_monitor); + configuration_changed (true); }); } private void set_as_primary (Display.VirtualMonitor new_primary) { - foreach (unowned var widget in display_widgets) { - var virtual_monitor = widget.virtual_monitor; + foreach (unowned var display_widget in display_widgets) { + if (!display_widget.virtual_monitor.is_active) { + continue; + } + + var virtual_monitor = display_widget.virtual_monitor; var is_primary = virtual_monitor == new_primary; - widget.set_primary (is_primary); + display_widget.set_primary (is_primary); virtual_monitor.primary = is_primary; } @@ -390,9 +455,21 @@ public class Display.DisplaysOverlay : Gtk.Box { bool moved = false, uint level = 0 ) { + if (!changed_widget.virtual_monitor.is_active) { + return moved; + } + if (level > 10) { + warning ("Depth of edges alignment recursion exceeds limit (10)"); + return moved; + } + int x, y, width, height; Gdk.Rectangle overlap; foreach (unowned var other_display_widget in display_widgets) { + if (!other_display_widget.virtual_monitor.is_active) { + continue; + } + if (other_display_widget == changed_widget) { continue; } @@ -469,6 +546,10 @@ public class Display.DisplaysOverlay : Gtk.Box { int min_y = int.MAX; foreach (unowned var display_widget in display_widgets) { + if (!display_widget.virtual_monitor.is_active) { + continue; + } + int x, y, width, height; // assert (display_widget.delta_x == 0 && display_widget.delta_y == 0); display_widget.get_virtual_monitor_geometry ( @@ -486,6 +567,10 @@ public class Display.DisplaysOverlay : Gtk.Box { } foreach (unowned var display_widget in display_widgets) { + if (!display_widget.virtual_monitor.is_active) { + continue; + } + int x, y, width, height; display_widget.get_virtual_monitor_geometry ( out x, @@ -515,8 +600,11 @@ public class Display.DisplaysOverlay : Gtk.Box { return false; } + if (!changed_widget.virtual_monitor.is_active) { + return moved; + } if (level > 10) { - warning ("Depth of recursion exceeds limit (10)"); + warning ("Depth of intersection checks recursion exceeds limit (10)"); return moved; } @@ -530,6 +618,10 @@ public class Display.DisplaysOverlay : Gtk.Box { Gdk.Rectangle src_rect = { x, y, width, height }; foreach (unowned var other_display_widget in display_widgets) { + if (!other_display_widget.virtual_monitor.is_active) { + continue; + } + int distance_x = 0; int distance_y = 0; if (other_display_widget == changed_widget) {