[gnome-games/wip/abhinavsingh/gamepad-reassign: 24/24] Add gamepad reassignment



commit 5ad3abbfee002799dac677ef95eec1e255168f31
Author: theawless <theawless gmail com>
Date:   Mon Jul 31 13:15:52 2017 +0530

    Add gamepad reassignment

 data/Makefile.am                   |    2 +
 data/org.gnome.Games.gresource.xml |    2 +
 data/ui/controller-popover.ui      |   31 ++++++++++++
 data/ui/controller-reassigner.ui   |   70 ++++++++++++++++++++++++++
 data/ui/display-header-bar.ui      |   25 +++++++++
 src/Makefile.am                    |    3 +
 src/command/command-runner.vala    |    4 ++
 src/core/controller-set.vala       |   53 ++++++++++++++++++++
 src/core/runner.vala               |    1 +
 src/dummy/dummy-runner.vala        |    4 ++
 src/retro/retro-input-manager.vala |   95 +++++++++++++++++-------------------
 src/retro/retro-runner.vala        |    6 ++
 src/ui/application-window.vala     |    2 +
 src/ui/controller-popover.vala     |   78 +++++++++++++++++++++++++++++
 src/ui/controller-reassigner.vala  |   82 +++++++++++++++++++++++++++++++
 src/ui/display-header-bar.vala     |   14 +++++
 16 files changed, 421 insertions(+), 51 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index fff2a95..a48c201 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -41,6 +41,8 @@ EXTRA_DIST = \
        ui/gamepad-mapper.ui \
        ui/gamepad-tester.ui \
        ui/game-icon-view.ui \
+       ui/controller-popover.ui \
+       ui/controller-reassigner.ui \
        ui/media-menu-button.ui \
        ui/media-selector.ui \
        ui/preferences-page-controllers.ui \
diff --git a/data/org.gnome.Games.gresource.xml b/data/org.gnome.Games.gresource.xml
index b8ba7b5..06a29ab 100644
--- a/data/org.gnome.Games.gresource.xml
+++ b/data/org.gnome.Games.gresource.xml
@@ -20,6 +20,8 @@
     <file preprocess="xml-stripblanks">ui/gamepad-mapper.ui</file>
     <file preprocess="xml-stripblanks">ui/gamepad-tester.ui</file>
     <file preprocess="xml-stripblanks">ui/game-icon-view.ui</file>
+    <file preprocess="xml-stripblanks">ui/controller-popover.ui</file>
+    <file preprocess="xml-stripblanks">ui/controller-reassigner.ui</file>
     <file preprocess="xml-stripblanks">ui/media-menu-button.ui</file>
     <file preprocess="xml-stripblanks">ui/media-selector.ui</file>
     <file preprocess="xml-stripblanks">ui/preferences-page-controllers.ui</file>
diff --git a/data/ui/controller-popover.ui b/data/ui/controller-popover.ui
new file mode 100644
index 0000000..762f57f
--- /dev/null
+++ b/data/ui/controller-popover.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GamesControllerPopover" parent="GtkPopover">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="margin-top">6</property>
+        <property name="margin-bottom">6</property>
+        <property name="margin-start">6</property>
+        <property name="margin-end">6</property>
+        <property name="margin">6</property>
+        <property name="spacing">12</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkListBox" id="controllers_list_box">
+            <property name="visible">True</property>
+            <property name="selection_mode">none</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="reassign_button">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Reassign</property>
+            <signal name="clicked" handler="on_reassign_clicked"/>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/controller-reassigner.ui b/data/ui/controller-reassigner.ui
new file mode 100644
index 0000000..ee5f805
--- /dev/null
+++ b/data/ui/controller-reassigner.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GamesControllerReassigner" parent="GtkDialog">
+    <property name="destroy-with-parent">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="visible">True</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkInfoBar" id="info_bar">
+            <property name="visible">True</property>
+            <child internal-child="content_area">
+              <object class="GtkBox">
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Set up the order of the gamepads</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <property name="margin-top">12</property>
+            <property name="margin-bottom">12</property>
+            <property name="margin-start">12</property>
+            <property name="margin-end">12</property>
+            <child>
+              <object class="GtkListBox" id="controllers_list_box">
+                <property name="visible">True</property>
+                <property name="selection_mode">none</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="button_apply">
+        <property name="visible">True</property>
+        <property name="can-default">True</property>
+        <property name="label" translatable="yes">Apply</property>
+        <style>
+          <class name="suggested-action"/>
+        </style>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="button_cancel">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">Cancel</property>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="apply" default="true">button_apply</action-widget>
+      <action-widget response="cancel">button_cancel</action-widget>
+    </action-widgets>
+  </template>
+</interface>
diff --git a/data/ui/display-header-bar.ui b/data/ui/display-header-bar.ui
index aae8f8d..90547b6 100644
--- a/data/ui/display-header-bar.ui
+++ b/data/ui/display-header-bar.ui
@@ -92,5 +92,30 @@
         <property name="pack-type">end</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkMenuButton" id="controller_button">
+        <property name="visible">True</property>
+        <property name="valign">center</property>
+        <property name="use-underline">True</property>
+        <style>
+          <class name="image-button"/>
+        </style>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="a11y-controller">
+            <property name="accessible-name" translatable="yes">controller</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="controller_image">
+            <property name="visible">True</property>
+            <property name="icon-name">input-gaming-symbolic</property>
+            <property name="icon-size">1</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="pack-type">end</property>
+      </packing>
+    </child>
   </template>
 </interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 645eb45..edab034 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,6 +64,7 @@ gnome_games_SOURCES = \
        core/game-collection.vala \
        core/game-uri-adapter.vala \
        core/icon.vala \
+       core/controller-set.vala \
        core/input-capabilities.vala \
        core/media.vala \
        core/media-info.vala \
@@ -153,6 +154,8 @@ gnome_games_SOURCES = \
        ui/game-icon-view.vala \
        ui/game-thumbnail.vala \
        ui/gamepad-view-configuration.vala \
+       ui/controller-popover.vala \
+       ui/controller-reassigner.vala \
        ui/media-selector.vala \
        ui/media-menu-button.vala \
        ui/preferences-page.vala \
diff --git a/src/command/command-runner.vala b/src/command/command-runner.vala
index e8b8612..2306c27 100644
--- a/src/command/command-runner.vala
+++ b/src/command/command-runner.vala
@@ -17,6 +17,10 @@ public class Games.CommandRunner : Object, Runner {
                get { return null; }
        }
 
+       internal ControllerSet? controller_set {
+               get { return null; }
+       }
+
        private string[] args;
        private bool watch_child;
 
diff --git a/src/core/controller-set.vala b/src/core/controller-set.vala
new file mode 100644
index 0000000..eee90ca
--- /dev/null
+++ b/src/core/controller-set.vala
@@ -0,0 +1,53 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.ControllerSet : Object {
+       public signal void changed ();
+       public signal void reset ();
+
+       public bool has_multiple_inputs {
+               get {
+                       return has_keyboard && gamepads.length > 0
+                       || gamepads.length > 1;
+               }
+       }
+
+       public bool has_keyboard {
+               get { return keyboard_port != uint.MAX; }
+       }
+
+       public bool has_gamepads {
+               get { return gamepads.length > 0; }
+       }
+
+       public uint first_unplugged_port {
+               get {
+                       uint i = 0;
+                       while (gamepads.contains (i) || i == keyboard_port) {
+                               i++;
+                       }
+                       return i;
+               }
+       }
+
+       public uint keyboard_port {     set; get; }
+       public HashTable<uint, Gamepad?> gamepads { set; get; }
+
+       construct {
+               keyboard_port = uint.MAX;
+               gamepads = new HashTable<uint, Gamepad?> (GLib.direct_hash, GLib.direct_equal);
+               notify["gamepads"].connect (() => changed ());
+               notify["keyboard-port"].connect (() => changed ());
+       }
+
+       public void add_gamepad (uint port, Gamepad gamepad) {
+               gamepads.insert (port, gamepad);
+
+               changed ();
+       }
+
+       public void remove_gamepad (uint port) {
+               gamepads.remove (port);
+
+               changed ();
+       }
+}
diff --git a/src/core/runner.vala b/src/core/runner.vala
index 25d79b8..0daec18 100644
--- a/src/core/runner.vala
+++ b/src/core/runner.vala
@@ -7,6 +7,7 @@ public interface Games.Runner : Object {
        public abstract bool can_quit_safely { get; }
        public abstract bool can_resume { get; }
        public abstract MediaSet? media_set { get; }
+       internal abstract ControllerSet? controller_set { get; }
 
        public abstract bool check_is_valid (out string error_message) throws Error;
        public abstract Gtk.Widget get_display ();
diff --git a/src/dummy/dummy-runner.vala b/src/dummy/dummy-runner.vala
index ce18b22..b6f7528 100644
--- a/src/dummy/dummy-runner.vala
+++ b/src/dummy/dummy-runner.vala
@@ -17,6 +17,10 @@ private class Games.DummyRunner : Object, Runner {
                get { return null; }
        }
 
+       internal ControllerSet? controller_set {
+               get { return null; }
+       }
+
        public bool check_is_valid (out string error_message) throws Error {
                return true;
        }
diff --git a/src/retro/retro-input-manager.vala b/src/retro/retro-input-manager.vala
index 26fdc52..79cacf2 100644
--- a/src/retro/retro-input-manager.vala
+++ b/src/retro/retro-input-manager.vala
@@ -1,80 +1,73 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 private class Games.RetroInputManager : Retro.InputDeviceManager, Retro.Rumble {
+       public ControllerSet controller_set { set; get; }
+
        private Retro.VirtualGamepad keyboard;
        private GamepadMonitor gamepad_monitor;
-       private Retro.InputDevice?[] input_devices;
-       private int keyboard_port;
        private bool present_analog_sticks;
 
+       construct {
+               controller_set = new ControllerSet ();
+               controller_set.reset.connect (reset);
+
+               gamepad_monitor = GamepadMonitor.get_instance ();
+               gamepad_monitor.gamepad_plugged.connect (add_gamepad);
+       }
+
        public RetroInputManager (Gtk.Widget widget, bool present_analog_sticks) {
                this.present_analog_sticks = present_analog_sticks;
 
+               // Assumption: keyboard always exists.
                keyboard = new Retro.VirtualGamepad (widget);
                set_keyboard (widget);
+               controller_set.keyboard_port = 0;
 
-               gamepad_monitor = GamepadMonitor.get_instance ();
-               gamepad_monitor.foreach_gamepad ((gamepad) => {
-                       var port = input_devices.length;
-                       var retro_gamepad = new RetroGamepad (gamepad, present_analog_sticks);
-                       input_devices += retro_gamepad;
-                       set_controller_device (port, retro_gamepad);
-                       gamepad.unplugged.connect (() => handle_gamepad_unplugged (port));
+               gamepad_monitor.foreach_gamepad (add_gamepad);
+       }
+
+       private void reset () {
+               foreach_controller ((port, controller) => {
+                       remove_controller_device (port);
                });
 
-               keyboard_port = input_devices.length;
-               input_devices += keyboard;
-               set_controller_device (keyboard_port, keyboard);
-               gamepad_monitor.gamepad_plugged.connect (handle_gamepad_plugged);
+               if (controller_set.has_gamepads) {
+                       controller_set.gamepads.foreach ((port, gamepad) => {
+                               var retro_gamepad = new RetroGamepad (gamepad, present_analog_sticks);
+                               set_controller_device (port, retro_gamepad);
+                       });
+               }
+               if (controller_set.has_keyboard)
+                       set_controller_device (controller_set.keyboard_port, keyboard);
        }
 
-       private void handle_gamepad_plugged (Gamepad gamepad) {
-               // Plug this gamepad to the port where the keyboard was plugged as a last resort
-               var port = keyboard_port;
+       private void add_gamepad (Gamepad gamepad) {
+               // Plug this gamepad to the port where the keyboard was plugged
+               var port = controller_set.keyboard_port;
+               controller_set.add_gamepad (port, gamepad);
                var retro_gamepad = new RetroGamepad (gamepad, present_analog_sticks);
-               input_devices[port] = retro_gamepad;
                set_controller_device (port, retro_gamepad);
-               gamepad.unplugged.connect (() => handle_gamepad_unplugged (port));
-
-               // Assign keyboard to another unplugged port if exists and return
-               for (var i = keyboard_port; i < input_devices.length; i++) {
-                       if (input_devices[i] == null) {
-                               // Found an unplugged port and so assigning keyboard to it
-                               keyboard_port = i;
-                               input_devices[keyboard_port] = keyboard;
-                               set_controller_device (keyboard_port, keyboard);
-
-                               return;
-                       }
-               }
+               gamepad.unplugged.connect (() => remove_gamepad (port));
 
-               // Now it means that there is no unplugged port so append keyboard to ports
-               keyboard_port = input_devices.length;
-               input_devices += keyboard;
-               set_controller_device (keyboard_port, keyboard);
+               // Assign keyboard to first unplugged port
+               controller_set.keyboard_port = controller_set.first_unplugged_port;
+               set_controller_device (controller_set.keyboard_port, keyboard);
        }
 
-       private void handle_gamepad_unplugged (int port) {
-               if (keyboard_port > port) {
-                       // Remove the controller and shift keyboard to "lesser" port
-                       input_devices[keyboard_port] = null;
-                       remove_controller_device (keyboard_port);
-                       keyboard_port = port;
-                       input_devices[keyboard_port] = keyboard;
-                       set_controller_device (keyboard_port, keyboard);
-               }
-               else {
-                       // Just remove the controller as no need to shift keyboard
-                       input_devices[port] = null;
-                       remove_controller_device (port);
+       private void remove_gamepad (uint port) {
+               controller_set.remove_gamepad (port);
+               remove_controller_device (port);
+
+               if (controller_set.has_keyboard && controller_set.keyboard_port > port) {
+                       // Shift keyboard to lesser port
+                       remove_controller_device (controller_set.keyboard_port);
+                       controller_set.keyboard_port = port;
+                       set_controller_device (controller_set.keyboard_port, keyboard);
                }
        }
 
        private bool set_rumble_state (uint port, Retro.RumbleEffect effect, uint16 strength) {
-               if (port > input_devices.length)
-                       return false;
-
-               if (input_devices[port] == null || input_devices[port] == keyboard)
+               if (controller_set.gamepads.contains (port))
                        return false;
 
                // TODO Transmit the rumble signal to the gamepad.
diff --git a/src/retro/retro-runner.vala b/src/retro/retro-runner.vala
index adeec2d..3e2c0b9 100644
--- a/src/retro/retro-runner.vala
+++ b/src/retro/retro-runner.vala
@@ -34,6 +34,10 @@ public class Games.RetroRunner : Object, Runner {
                get { return _media_set; }
        }
 
+       internal ControllerSet? controller_set {
+               get { return input_manager.controller_set; }
+       }
+
        private Retro.Core core;
        private Retro.CairoDisplay video;
        private Retro.PaPlayer audio;
@@ -71,6 +75,8 @@ public class Games.RetroRunner : Object, Runner {
        private bool is_ready;
        private bool should_save;
 
+       private Binding controller_set_binding;
+
        public RetroRunner (RetroCoreSource core_source, Uri uri, Uid uid, Title game_title) {
                is_initialized = false;
                is_ready = false;
diff --git a/src/ui/application-window.vala b/src/ui/application-window.vala
index 4c157ef..bfaeb9e 100644
--- a/src/ui/application-window.vala
+++ b/src/ui/application-window.vala
@@ -254,6 +254,8 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
                display_box.runner = runner;
                display_header_bar.media_set = runner.media_set;
                display_box.header_bar.media_set = runner.media_set;
+               display_header_bar.controller_set = runner.controller_set;
+               display_box.header_bar.controller_set = runner.controller_set;
 
                is_fullscreen = settings.get_boolean ("fullscreen") && runner.can_fullscreen;
 
diff --git a/src/ui/controller-popover.vala b/src/ui/controller-popover.vala
new file mode 100644
index 0000000..f0da512
--- /dev/null
+++ b/src/ui/controller-popover.vala
@@ -0,0 +1,78 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/controller-popover.ui")]
+private class Games.ControllerPopover : Gtk.Popover {
+       [GtkChild]
+       private Gtk.ListBox controllers_list_box;
+       [GtkChild]
+       private Gtk.Button reassign_button;
+
+       private ControllerSet _controller_set;
+       public ControllerSet controller_set {
+               set {
+                       _controller_set = value;
+
+                       reset_inputs ();
+                       if (controller_set != null)
+                               controller_set.changed.connect (reset_inputs);
+               }
+               get { return _controller_set; }
+       }
+
+       private void reset_inputs () {
+               reassign_button.sensitive = controller_set != null && controller_set.has_multiple_inputs;
+
+               remove_inputs ();
+               update_inputs ();
+       }
+
+       private void update_inputs () {
+               if (controller_set == null)
+                       return;
+
+               if (controller_set.has_gamepads) {
+                       controller_set.gamepads.foreach ((port, gamepad) => {
+                               add_input (gamepad.name, port);
+                       });
+               }
+               if (controller_set.has_keyboard)
+                       add_input (_("Keyboard"), controller_set.keyboard_port);
+       }
+
+       private void add_input (string label, uint port) {
+               var position = (int) port;
+               var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+               box.pack_start (new Gtk.Label (label), true, true);
+               box.pack_end (new Gtk.Label (position.to_string ()), false, false);
+               box.margin = 6;
+               box.show_all ();
+               controllers_list_box.insert (box, position);
+       }
+
+       private void remove_inputs () {
+               controllers_list_box.foreach ((child) => child.destroy ());
+       }
+
+       [GtkCallback]
+       private void on_reassign_clicked () {
+               popdown ();
+
+               var controller_reassigner = new ControllerReassigner ();
+               controller_reassigner.set_transient_for ((Gtk.Window) get_toplevel ());
+               controller_reassigner.response.connect ((response) => {
+                       switch (response) {
+                       case Gtk.ResponseType.APPLY:
+                               controller_set.gamepads = controller_reassigner.controller_set.gamepads;
+                               controller_set.keyboard_port = 
controller_reassigner.controller_set.keyboard_port;
+                               controller_set.reset ();
+
+                               break;
+                       default:
+                               break;
+                       }
+
+                       controller_reassigner.destroy ();
+               });
+               controller_reassigner.show ();
+       }
+}
diff --git a/src/ui/controller-reassigner.vala b/src/ui/controller-reassigner.vala
new file mode 100644
index 0000000..c8a8780
--- /dev/null
+++ b/src/ui/controller-reassigner.vala
@@ -0,0 +1,82 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/controller-reassigner.ui")]
+private class Games.ControllerReassigner : Gtk.Dialog {
+       public ControllerSet controller_set { private set; get; }
+
+       [GtkChild]
+       private Gtk.ListBox controllers_list_box;
+
+       private uint current_port;
+       private GamepadMonitor gamepad_monitor;
+
+       private ulong key_release_event_handler_id;
+       private HashTable<Gamepad, ulong> gamepad_button_press_event_handler_ids;
+
+       construct {
+               use_header_bar = 1;
+
+               controller_set = new ControllerSet ();
+               controller_set.changed.connect (reset_inputs);
+               current_port = 0;
+               gamepad_monitor = GamepadMonitor.get_instance ();
+               gamepad_button_press_event_handler_ids = new HashTable<Gamepad, uint> (GLib.int_hash, 
GLib.int_equal);
+
+               events |= Gdk.EventMask.KEY_RELEASE_MASK;
+               key_release_event_handler_id = key_release_event.connect (keyboard_event);
+       }
+
+       public ControllerReassigner () {
+               gamepad_monitor.foreach_gamepad ((gamepad) => {
+                       var gamepad_button_press_event_handler_id = gamepad.button_press_event.connect (() => 
gamepad_event (gamepad));
+                       gamepad_button_press_event_handler_ids[gamepad] = 
gamepad_button_press_event_handler_id;
+               });
+       }
+
+       private void reset_inputs () {
+               remove_inputs ();
+               update_inputs ();
+       }
+
+       private void update_inputs () {
+               if (controller_set == null)
+                       return;
+
+               if (controller_set.has_gamepads) {
+                       controller_set.gamepads.foreach ((port, gamepad) => {
+                               add_input (gamepad.name, port);
+                       });
+               }
+               if (controller_set.has_keyboard)
+                       add_input (_("Keyboard"), controller_set.keyboard_port);
+       }
+
+       private void add_input (string label, uint port) {
+               var position = (int) port;
+               var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+               box.pack_start (new Gtk.Label (label), true, true);
+               box.pack_end (new Gtk.Label (position.to_string ()), false, false);
+               box.margin = 6;
+               box.show_all ();
+               controllers_list_box.insert (box, position);
+       }
+
+       private void remove_inputs () {
+               controllers_list_box.foreach ((child) => child.destroy ());
+       }
+
+       private bool keyboard_event () {
+               disconnect (key_release_event_handler_id);
+
+               controller_set.keyboard_port = current_port++;
+
+               return false;
+       }
+
+       private void gamepad_event (Gamepad gamepad) {
+               gamepad.disconnect (gamepad_button_press_event_handler_ids[gamepad]);
+               gamepad_button_press_event_handler_ids.remove (gamepad);
+
+               controller_set.add_gamepad (current_port++, gamepad);
+       }
+}
diff --git a/src/ui/display-header-bar.vala b/src/ui/display-header-bar.vala
index 5fa6749..8481480 100644
--- a/src/ui/display-header-bar.vala
+++ b/src/ui/display-header-bar.vala
@@ -6,6 +6,8 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
 
        [GtkChild]
        private MediaMenuButton media_button;
+       [GtkChild]
+       private Gtk.MenuButton controller_button;
 
        public string game_title {
                set { title = value; }
@@ -21,7 +23,15 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
                }
        }
 
+       public ControllerSet? controller_set {
+               set {
+                       controller_popover.controller_set = value;
+                       controller_button.visible = value != null;
+               }
+       }
+
        private MediaSelector media_selector;
+       private ControllerPopover controller_popover;
 
        [GtkChild]
        private Gtk.Button fullscreen;
@@ -34,6 +44,10 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
        construct {
                settings = new Settings ("org.gnome.Games");
 
+               controller_popover = new ControllerPopover ();
+               controller_popover.set_relative_to (controller_button);
+               controller_button.set_popover (controller_popover);
+
                media_selector = new MediaSelector ();
                media_selector.set_relative_to (media_button);
                media_button.set_popover (media_selector);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]