We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bottom panel switches to a different game when it stops
If another game is running and you switch to a different one, and the first game is closed by itself (like quitting it manually, closing the game window), not through Lutris, the bottom panel will switch to that stopped game all by itself, without user's interaction:
![Peek 2022-02-04 13-29](https://user-images.githubusercontent.com/10602045/152513690-224ef6c1-b56f-4510-9d0f-897ffe2dd8ea.gif)

It should be noted that so far, only I can reproduce this, for some bizarre reason.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/gui/widgets/game_bar.py`
Content:
```
1 from datetime import datetime
2 from gettext import gettext as _
3 
4 from gi.repository import GObject, Gtk, Pango
5 
6 from lutris import runners, services
7 from lutris.database.games import get_game_by_field, get_game_for_service
8 from lutris.game import Game
9 from lutris.gui.widgets.utils import get_link_button
10 from lutris.util.strings import gtk_safe
11 
12 
13 class GameBar(Gtk.Box):
14     def __init__(self, db_game, game_actions, application):
15         """Create the game bar with a database row"""
16         super().__init__(orientation=Gtk.Orientation.VERTICAL, visible=True,
17                          margin_top=12,
18                          margin_left=12,
19                          margin_bottom=12,
20                          margin_right=12,
21                          spacing=6)
22         GObject.add_emission_hook(Game, "game-start", self.on_game_state_changed)
23         GObject.add_emission_hook(Game, "game-started", self.on_game_state_changed)
24         GObject.add_emission_hook(Game, "game-stopped", self.on_game_state_changed)
25         GObject.add_emission_hook(Game, "game-updated", self.on_game_state_changed)
26         GObject.add_emission_hook(Game, "game-removed", self.on_game_state_changed)
27         GObject.add_emission_hook(Game, "game-installed", self.on_game_state_changed)
28 
29         self.set_margin_bottom(12)
30         self.game_actions = game_actions
31         self.db_game = db_game
32         self.service = None
33         if db_game.get("service"):
34             try:
35                 self.service = services.SERVICES[db_game["service"]]()
36             except KeyError:
37                 pass
38 
39         game_id = None
40         if "service_id" in db_game:
41             self.appid = db_game["service_id"]
42             game_id = db_game["id"]
43         elif self.service:
44             self.appid = db_game["appid"]
45             if self.service.id == "lutris":
46                 game = get_game_by_field(self.appid, field="slug")
47             else:
48                 game = get_game_for_service(self.service.id, self.appid)
49             if game:
50                 game_id = game["id"]
51         if game_id:
52             self.game = application.get_game_by_id(game_id) or Game(game_id)
53         else:
54             self.game = Game()
55             self.game.name = db_game["name"]
56             self.game.slug = db_game["slug"]
57             self.game.appid = self.appid
58             self.game.service = self.service.id if self.service else None
59         game_actions.set_game(self.game)
60         self.update_view()
61 
62     def clear_view(self):
63         """Clears all widgets from the container"""
64         for child in self.get_children():
65             child.destroy()
66 
67     def update_view(self):
68         """Populate the view with widgets"""
69         game_label = self.get_game_name_label()
70         game_label.set_halign(Gtk.Align.START)
71         self.pack_start(game_label, False, False, 0)
72 
73         hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing=6)
74         self.pack_start(hbox, False, False, 0)
75 
76         self.play_button = self.get_play_button()
77         hbox.pack_start(self.play_button, False, False, 0)
78 
79         if self.game.is_installed:
80             hbox.pack_start(self.get_runner_button(), False, False, 0)
81             hbox.pack_start(self.get_platform_label(), False, False, 0)
82         if self.game.lastplayed:
83             hbox.pack_start(self.get_last_played_label(), False, False, 0)
84         if self.game.playtime:
85             hbox.pack_start(self.get_playtime_label(), False, False, 0)
86         hbox.show_all()
87 
88     def get_popover(self, buttons, parent):
89         """Return the popover widget containing a list of link buttons"""
90         if not buttons:
91             return None
92         popover = Gtk.Popover()
93         vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, visible=True)
94 
95         for action in buttons:
96             vbox.pack_end(buttons[action], False, False, 1)
97         popover.add(vbox)
98         popover.set_position(Gtk.PositionType.TOP)
99         popover.set_constrain_to(Gtk.PopoverConstraint.NONE)
100         popover.set_relative_to(parent)
101         return popover
102 
103     def get_game_name_label(self):
104         """Return the label with the game's title"""
105         title_label = Gtk.Label(visible=True)
106         title_label.set_ellipsize(Pango.EllipsizeMode.END)
107         title_label.set_markup("<span font_desc='16'><b>%s</b></span>" % gtk_safe(self.game.name))
108         return title_label
109 
110     def get_runner_button(self):
111         icon_name = self.game.runner.name + "-symbolic"
112         runner_icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU)
113         runner_icon.show()
114         box = Gtk.HBox(visible=True)
115         runner_button = Gtk.Button(visible=True)
116         popover = self.get_popover(self.get_runner_buttons(), runner_button)
117         if popover:
118             runner_button.set_image(runner_icon)
119             popover_button = Gtk.MenuButton(visible=True)
120             popover_button.set_size_request(32, 32)
121             popover_button.props.direction = Gtk.ArrowType.UP
122             popover_button.set_popover(popover)
123             runner_button.connect("clicked", lambda _x: popover_button.emit("clicked"))
124             box.add(runner_button)
125             box.add(popover_button)
126             style_context = box.get_style_context()
127             style_context.add_class("linked")
128         else:
129             runner_icon.set_margin_left(49)
130             runner_icon.set_margin_right(6)
131             box.add(runner_icon)
132         return box
133 
134     def get_platform_label(self):
135         platform_label = Gtk.Label(visible=True)
136         platform_label.set_size_request(120, -1)
137         platform_label.set_alignment(0, 0.5)
138         platform = gtk_safe(self.game.platform)
139         platform_label.set_tooltip_markup(platform)
140         platform_label.set_markup(_("Platform:\n<b>%s</b>") % platform)
141         platform_label.set_property("ellipsize", Pango.EllipsizeMode.END)
142         return platform_label
143 
144     def get_playtime_label(self):
145         """Return the label containing the playtime info"""
146         playtime_label = Gtk.Label(visible=True)
147         playtime_label.set_size_request(120, -1)
148         playtime_label.set_alignment(0, 0.5)
149         playtime_label.set_markup(_("Time played:\n<b>%s</b>") % self.game.formatted_playtime)
150         return playtime_label
151 
152     def get_last_played_label(self):
153         """Return the label containing the last played info"""
154         last_played_label = Gtk.Label(visible=True)
155         last_played_label.set_size_request(120, -1)
156         last_played_label.set_alignment(0, 0.5)
157         lastplayed = datetime.fromtimestamp(self.game.lastplayed)
158         last_played_label.set_markup(_("Last played:\n<b>%s</b>") % lastplayed.strftime("%x"))
159         return last_played_label
160 
161     def get_popover_button(self):
162         """Return the popover button+menu for the Play button"""
163         popover_button = Gtk.MenuButton(visible=True)
164         popover_button.set_size_request(32, 32)
165         popover_button.props.direction = Gtk.ArrowType.UP
166 
167         return popover_button
168 
169     def get_popover_box(self):
170         """Return a container for a button + a popover button attached to it"""
171         box = Gtk.HBox(visible=True)
172         style_context = box.get_style_context()
173         style_context.add_class("linked")
174         return box
175 
176     def get_locate_installed_game_button(self):
177         """Return a button to locate an existing install"""
178         button = get_link_button("Locate installed game")
179         button.show()
180         button.connect("clicked", self.game_actions.on_locate_installed_game, self.game)
181         return {"locate": button}
182 
183     def get_play_button(self):
184         """Return the widget for install/play/stop and game config"""
185         button = Gtk.Button(visible=True)
186         button.set_size_request(120, 32)
187         box = self.get_popover_box()
188         popover_button = self.get_popover_button()
189         if self.game.is_installed:
190             if self.game.state == self.game.STATE_STOPPED:
191                 button.set_label(_("Play"))
192                 button.connect("clicked", self.game_actions.on_game_launch)
193             elif self.game.state == self.game.STATE_LAUNCHING:
194                 button.set_label(_("Launching"))
195                 button.set_sensitive(False)
196             else:
197                 button.set_label(_("Stop"))
198                 button.connect("clicked", self.game_actions.on_game_stop)
199         else:
200             button.set_label(_("Install"))
201             button.connect("clicked", self.game_actions.on_install_clicked)
202             if self.service:
203                 if self.service.local:
204                     # Local services don't show an install dialog, they can be launched directly
205                     button.set_label(_("Play"))
206                 if self.service.drm_free:
207                     button.set_size_request(84, 32)
208                     box.add(button)
209                     popover = self.get_popover(self.get_locate_installed_game_button(), popover_button)
210                     popover_button.set_popover(popover)
211                     box.add(popover_button)
212                     return box
213                 return button
214         button.set_size_request(84, 32)
215         box.add(button)
216         popover = self.get_popover(self.get_game_buttons(), popover_button)
217         popover_button.set_popover(popover)
218         box.add(popover_button)
219         return box
220 
221     def get_game_buttons(self):
222         """Return a dictionary of buttons to use in the panel"""
223         displayed = self.game_actions.get_displayed_entries()
224         buttons = {}
225         for action in self.game_actions.get_game_actions():
226             action_id, label, callback = action
227             if action_id in ("play", "stop", "install"):
228                 continue
229             button = get_link_button(label)
230             if displayed.get(action_id):
231                 button.show()
232             else:
233                 button.hide()
234             buttons[action_id] = button
235             button.connect("clicked", self.on_link_button_clicked, callback)
236         return buttons
237 
238     def get_runner_buttons(self):
239         buttons = {}
240         if self.game.runner_name and self.game.is_installed:
241             runner = runners.import_runner(self.game.runner_name)(self.game.config)
242             for entry in runner.context_menu_entries:
243                 name, label, callback = entry
244                 button = get_link_button(label)
245                 button.show()
246                 button.connect("clicked", self.on_link_button_clicked, callback)
247                 buttons[name] = button
248         return buttons
249 
250     def on_link_button_clicked(self, button, callback):
251         """Callback for link buttons. Closes the popover then runs the actual action"""
252         popover = button.get_parent().get_parent()
253         popover.popdown()
254         callback(button)
255 
256     def on_install_clicked(self, button):
257         """Handler for installing service games"""
258         self.service.install(self.db_game)
259 
260     def on_game_state_changed(self, game):
261         """Handler called when the game has changed state"""
262         if (
263             game.id == self.game.id
264             or game.appid == self.appid
265         ):
266             self.game = game
267         else:
268             return True
269         self.clear_view()
270         self.update_view()
271         return True
272
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/gui/widgets/game_bar.py b/lutris/gui/widgets/game_bar.py
--- a/lutris/gui/widgets/game_bar.py
+++ b/lutris/gui/widgets/game_bar.py
@@ -261,7 +261,7 @@
         """Handler called when the game has changed state"""
         if (
             game.id == self.game.id
-            or game.appid == self.appid
+            or (self.appid and game.appid == self.appid)
         ):
             self.game = game
         else:

{"golden_diff": "diff --git a/lutris/gui/widgets/game_bar.py b/lutris/gui/widgets/game_bar.py\n--- a/lutris/gui/widgets/game_bar.py\n+++ b/lutris/gui/widgets/game_bar.py\n@@ -261,7 +261,7 @@\n         \"\"\"Handler called when the game has changed state\"\"\"\n         if (\n             game.id == self.game.id\n-            or game.appid == self.appid\n+            or (self.appid and game.appid == self.appid)\n         ):\n             self.game = game\n         else:\n", "issue": "Bottom panel switches to a different game when it stops\nIf another game is running and you switch to a different one, and the first game is closed by itself (like quitting it manually, closing the game window), not through Lutris, the bottom panel will switch to that stopped game all by itself, without user's interaction:\r\n![Peek 2022-02-04 13-29](https://user-images.githubusercontent.com/10602045/152513690-224ef6c1-b56f-4510-9d0f-897ffe2dd8ea.gif)\r\n\r\nIt should be noted that so far, only I can reproduce this, for some bizarre reason.\r\n\n", "before_files": [{"content": "from datetime import datetime\nfrom gettext import gettext as _\n\nfrom gi.repository import GObject, Gtk, Pango\n\nfrom lutris import runners, services\nfrom lutris.database.games import get_game_by_field, get_game_for_service\nfrom lutris.game import Game\nfrom lutris.gui.widgets.utils import get_link_button\nfrom lutris.util.strings import gtk_safe\n\n\nclass GameBar(Gtk.Box):\n    def __init__(self, db_game, game_actions, application):\n        \"\"\"Create the game bar with a database row\"\"\"\n        super().__init__(orientation=Gtk.Orientation.VERTICAL, visible=True,\n                         margin_top=12,\n                         margin_left=12,\n                         margin_bottom=12,\n                         margin_right=12,\n                         spacing=6)\n        GObject.add_emission_hook(Game, \"game-start\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-started\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-stopped\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-updated\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-removed\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-installed\", self.on_game_state_changed)\n\n        self.set_margin_bottom(12)\n        self.game_actions = game_actions\n        self.db_game = db_game\n        self.service = None\n        if db_game.get(\"service\"):\n            try:\n                self.service = services.SERVICES[db_game[\"service\"]]()\n            except KeyError:\n                pass\n\n        game_id = None\n        if \"service_id\" in db_game:\n            self.appid = db_game[\"service_id\"]\n            game_id = db_game[\"id\"]\n        elif self.service:\n            self.appid = db_game[\"appid\"]\n            if self.service.id == \"lutris\":\n                game = get_game_by_field(self.appid, field=\"slug\")\n            else:\n                game = get_game_for_service(self.service.id, self.appid)\n            if game:\n                game_id = game[\"id\"]\n        if game_id:\n            self.game = application.get_game_by_id(game_id) or Game(game_id)\n        else:\n            self.game = Game()\n            self.game.name = db_game[\"name\"]\n            self.game.slug = db_game[\"slug\"]\n            self.game.appid = self.appid\n            self.game.service = self.service.id if self.service else None\n        game_actions.set_game(self.game)\n        self.update_view()\n\n    def clear_view(self):\n        \"\"\"Clears all widgets from the container\"\"\"\n        for child in self.get_children():\n            child.destroy()\n\n    def update_view(self):\n        \"\"\"Populate the view with widgets\"\"\"\n        game_label = self.get_game_name_label()\n        game_label.set_halign(Gtk.Align.START)\n        self.pack_start(game_label, False, False, 0)\n\n        hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing=6)\n        self.pack_start(hbox, False, False, 0)\n\n        self.play_button = self.get_play_button()\n        hbox.pack_start(self.play_button, False, False, 0)\n\n        if self.game.is_installed:\n            hbox.pack_start(self.get_runner_button(), False, False, 0)\n            hbox.pack_start(self.get_platform_label(), False, False, 0)\n        if self.game.lastplayed:\n            hbox.pack_start(self.get_last_played_label(), False, False, 0)\n        if self.game.playtime:\n            hbox.pack_start(self.get_playtime_label(), False, False, 0)\n        hbox.show_all()\n\n    def get_popover(self, buttons, parent):\n        \"\"\"Return the popover widget containing a list of link buttons\"\"\"\n        if not buttons:\n            return None\n        popover = Gtk.Popover()\n        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, visible=True)\n\n        for action in buttons:\n            vbox.pack_end(buttons[action], False, False, 1)\n        popover.add(vbox)\n        popover.set_position(Gtk.PositionType.TOP)\n        popover.set_constrain_to(Gtk.PopoverConstraint.NONE)\n        popover.set_relative_to(parent)\n        return popover\n\n    def get_game_name_label(self):\n        \"\"\"Return the label with the game's title\"\"\"\n        title_label = Gtk.Label(visible=True)\n        title_label.set_ellipsize(Pango.EllipsizeMode.END)\n        title_label.set_markup(\"<span font_desc='16'><b>%s</b></span>\" % gtk_safe(self.game.name))\n        return title_label\n\n    def get_runner_button(self):\n        icon_name = self.game.runner.name + \"-symbolic\"\n        runner_icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU)\n        runner_icon.show()\n        box = Gtk.HBox(visible=True)\n        runner_button = Gtk.Button(visible=True)\n        popover = self.get_popover(self.get_runner_buttons(), runner_button)\n        if popover:\n            runner_button.set_image(runner_icon)\n            popover_button = Gtk.MenuButton(visible=True)\n            popover_button.set_size_request(32, 32)\n            popover_button.props.direction = Gtk.ArrowType.UP\n            popover_button.set_popover(popover)\n            runner_button.connect(\"clicked\", lambda _x: popover_button.emit(\"clicked\"))\n            box.add(runner_button)\n            box.add(popover_button)\n            style_context = box.get_style_context()\n            style_context.add_class(\"linked\")\n        else:\n            runner_icon.set_margin_left(49)\n            runner_icon.set_margin_right(6)\n            box.add(runner_icon)\n        return box\n\n    def get_platform_label(self):\n        platform_label = Gtk.Label(visible=True)\n        platform_label.set_size_request(120, -1)\n        platform_label.set_alignment(0, 0.5)\n        platform = gtk_safe(self.game.platform)\n        platform_label.set_tooltip_markup(platform)\n        platform_label.set_markup(_(\"Platform:\\n<b>%s</b>\") % platform)\n        platform_label.set_property(\"ellipsize\", Pango.EllipsizeMode.END)\n        return platform_label\n\n    def get_playtime_label(self):\n        \"\"\"Return the label containing the playtime info\"\"\"\n        playtime_label = Gtk.Label(visible=True)\n        playtime_label.set_size_request(120, -1)\n        playtime_label.set_alignment(0, 0.5)\n        playtime_label.set_markup(_(\"Time played:\\n<b>%s</b>\") % self.game.formatted_playtime)\n        return playtime_label\n\n    def get_last_played_label(self):\n        \"\"\"Return the label containing the last played info\"\"\"\n        last_played_label = Gtk.Label(visible=True)\n        last_played_label.set_size_request(120, -1)\n        last_played_label.set_alignment(0, 0.5)\n        lastplayed = datetime.fromtimestamp(self.game.lastplayed)\n        last_played_label.set_markup(_(\"Last played:\\n<b>%s</b>\") % lastplayed.strftime(\"%x\"))\n        return last_played_label\n\n    def get_popover_button(self):\n        \"\"\"Return the popover button+menu for the Play button\"\"\"\n        popover_button = Gtk.MenuButton(visible=True)\n        popover_button.set_size_request(32, 32)\n        popover_button.props.direction = Gtk.ArrowType.UP\n\n        return popover_button\n\n    def get_popover_box(self):\n        \"\"\"Return a container for a button + a popover button attached to it\"\"\"\n        box = Gtk.HBox(visible=True)\n        style_context = box.get_style_context()\n        style_context.add_class(\"linked\")\n        return box\n\n    def get_locate_installed_game_button(self):\n        \"\"\"Return a button to locate an existing install\"\"\"\n        button = get_link_button(\"Locate installed game\")\n        button.show()\n        button.connect(\"clicked\", self.game_actions.on_locate_installed_game, self.game)\n        return {\"locate\": button}\n\n    def get_play_button(self):\n        \"\"\"Return the widget for install/play/stop and game config\"\"\"\n        button = Gtk.Button(visible=True)\n        button.set_size_request(120, 32)\n        box = self.get_popover_box()\n        popover_button = self.get_popover_button()\n        if self.game.is_installed:\n            if self.game.state == self.game.STATE_STOPPED:\n                button.set_label(_(\"Play\"))\n                button.connect(\"clicked\", self.game_actions.on_game_launch)\n            elif self.game.state == self.game.STATE_LAUNCHING:\n                button.set_label(_(\"Launching\"))\n                button.set_sensitive(False)\n            else:\n                button.set_label(_(\"Stop\"))\n                button.connect(\"clicked\", self.game_actions.on_game_stop)\n        else:\n            button.set_label(_(\"Install\"))\n            button.connect(\"clicked\", self.game_actions.on_install_clicked)\n            if self.service:\n                if self.service.local:\n                    # Local services don't show an install dialog, they can be launched directly\n                    button.set_label(_(\"Play\"))\n                if self.service.drm_free:\n                    button.set_size_request(84, 32)\n                    box.add(button)\n                    popover = self.get_popover(self.get_locate_installed_game_button(), popover_button)\n                    popover_button.set_popover(popover)\n                    box.add(popover_button)\n                    return box\n                return button\n        button.set_size_request(84, 32)\n        box.add(button)\n        popover = self.get_popover(self.get_game_buttons(), popover_button)\n        popover_button.set_popover(popover)\n        box.add(popover_button)\n        return box\n\n    def get_game_buttons(self):\n        \"\"\"Return a dictionary of buttons to use in the panel\"\"\"\n        displayed = self.game_actions.get_displayed_entries()\n        buttons = {}\n        for action in self.game_actions.get_game_actions():\n            action_id, label, callback = action\n            if action_id in (\"play\", \"stop\", \"install\"):\n                continue\n            button = get_link_button(label)\n            if displayed.get(action_id):\n                button.show()\n            else:\n                button.hide()\n            buttons[action_id] = button\n            button.connect(\"clicked\", self.on_link_button_clicked, callback)\n        return buttons\n\n    def get_runner_buttons(self):\n        buttons = {}\n        if self.game.runner_name and self.game.is_installed:\n            runner = runners.import_runner(self.game.runner_name)(self.game.config)\n            for entry in runner.context_menu_entries:\n                name, label, callback = entry\n                button = get_link_button(label)\n                button.show()\n                button.connect(\"clicked\", self.on_link_button_clicked, callback)\n                buttons[name] = button\n        return buttons\n\n    def on_link_button_clicked(self, button, callback):\n        \"\"\"Callback for link buttons. Closes the popover then runs the actual action\"\"\"\n        popover = button.get_parent().get_parent()\n        popover.popdown()\n        callback(button)\n\n    def on_install_clicked(self, button):\n        \"\"\"Handler for installing service games\"\"\"\n        self.service.install(self.db_game)\n\n    def on_game_state_changed(self, game):\n        \"\"\"Handler called when the game has changed state\"\"\"\n        if (\n            game.id == self.game.id\n            or game.appid == self.appid\n        ):\n            self.game = game\n        else:\n            return True\n        self.clear_view()\n        self.update_view()\n        return True\n", "path": "lutris/gui/widgets/game_bar.py"}], "after_files": [{"content": "from datetime import datetime\nfrom gettext import gettext as _\n\nfrom gi.repository import GObject, Gtk, Pango\n\nfrom lutris import runners, services\nfrom lutris.database.games import get_game_by_field, get_game_for_service\nfrom lutris.game import Game\nfrom lutris.gui.widgets.utils import get_link_button\nfrom lutris.util.strings import gtk_safe\n\n\nclass GameBar(Gtk.Box):\n    def __init__(self, db_game, game_actions, application):\n        \"\"\"Create the game bar with a database row\"\"\"\n        super().__init__(orientation=Gtk.Orientation.VERTICAL, visible=True,\n                         margin_top=12,\n                         margin_left=12,\n                         margin_bottom=12,\n                         margin_right=12,\n                         spacing=6)\n        GObject.add_emission_hook(Game, \"game-start\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-started\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-stopped\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-updated\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-removed\", self.on_game_state_changed)\n        GObject.add_emission_hook(Game, \"game-installed\", self.on_game_state_changed)\n\n        self.set_margin_bottom(12)\n        self.game_actions = game_actions\n        self.db_game = db_game\n        self.service = None\n        if db_game.get(\"service\"):\n            try:\n                self.service = services.SERVICES[db_game[\"service\"]]()\n            except KeyError:\n                pass\n\n        game_id = None\n        if \"service_id\" in db_game:\n            self.appid = db_game[\"service_id\"]\n            game_id = db_game[\"id\"]\n        elif self.service:\n            self.appid = db_game[\"appid\"]\n            if self.service.id == \"lutris\":\n                game = get_game_by_field(self.appid, field=\"slug\")\n            else:\n                game = get_game_for_service(self.service.id, self.appid)\n            if game:\n                game_id = game[\"id\"]\n        if game_id:\n            self.game = application.get_game_by_id(game_id) or Game(game_id)\n        else:\n            self.game = Game()\n            self.game.name = db_game[\"name\"]\n            self.game.slug = db_game[\"slug\"]\n            self.game.appid = self.appid\n            self.game.service = self.service.id if self.service else None\n        game_actions.set_game(self.game)\n        self.update_view()\n\n    def clear_view(self):\n        \"\"\"Clears all widgets from the container\"\"\"\n        for child in self.get_children():\n            child.destroy()\n\n    def update_view(self):\n        \"\"\"Populate the view with widgets\"\"\"\n        game_label = self.get_game_name_label()\n        game_label.set_halign(Gtk.Align.START)\n        self.pack_start(game_label, False, False, 0)\n\n        hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing=6)\n        self.pack_start(hbox, False, False, 0)\n\n        self.play_button = self.get_play_button()\n        hbox.pack_start(self.play_button, False, False, 0)\n\n        if self.game.is_installed:\n            hbox.pack_start(self.get_runner_button(), False, False, 0)\n            hbox.pack_start(self.get_platform_label(), False, False, 0)\n        if self.game.lastplayed:\n            hbox.pack_start(self.get_last_played_label(), False, False, 0)\n        if self.game.playtime:\n            hbox.pack_start(self.get_playtime_label(), False, False, 0)\n        hbox.show_all()\n\n    def get_popover(self, buttons, parent):\n        \"\"\"Return the popover widget containing a list of link buttons\"\"\"\n        if not buttons:\n            return None\n        popover = Gtk.Popover()\n        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, visible=True)\n\n        for action in buttons:\n            vbox.pack_end(buttons[action], False, False, 1)\n        popover.add(vbox)\n        popover.set_position(Gtk.PositionType.TOP)\n        popover.set_constrain_to(Gtk.PopoverConstraint.NONE)\n        popover.set_relative_to(parent)\n        return popover\n\n    def get_game_name_label(self):\n        \"\"\"Return the label with the game's title\"\"\"\n        title_label = Gtk.Label(visible=True)\n        title_label.set_ellipsize(Pango.EllipsizeMode.END)\n        title_label.set_markup(\"<span font_desc='16'><b>%s</b></span>\" % gtk_safe(self.game.name))\n        return title_label\n\n    def get_runner_button(self):\n        icon_name = self.game.runner.name + \"-symbolic\"\n        runner_icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU)\n        runner_icon.show()\n        box = Gtk.HBox(visible=True)\n        runner_button = Gtk.Button(visible=True)\n        popover = self.get_popover(self.get_runner_buttons(), runner_button)\n        if popover:\n            runner_button.set_image(runner_icon)\n            popover_button = Gtk.MenuButton(visible=True)\n            popover_button.set_size_request(32, 32)\n            popover_button.props.direction = Gtk.ArrowType.UP\n            popover_button.set_popover(popover)\n            runner_button.connect(\"clicked\", lambda _x: popover_button.emit(\"clicked\"))\n            box.add(runner_button)\n            box.add(popover_button)\n            style_context = box.get_style_context()\n            style_context.add_class(\"linked\")\n        else:\n            runner_icon.set_margin_left(49)\n            runner_icon.set_margin_right(6)\n            box.add(runner_icon)\n        return box\n\n    def get_platform_label(self):\n        platform_label = Gtk.Label(visible=True)\n        platform_label.set_size_request(120, -1)\n        platform_label.set_alignment(0, 0.5)\n        platform = gtk_safe(self.game.platform)\n        platform_label.set_tooltip_markup(platform)\n        platform_label.set_markup(_(\"Platform:\\n<b>%s</b>\") % platform)\n        platform_label.set_property(\"ellipsize\", Pango.EllipsizeMode.END)\n        return platform_label\n\n    def get_playtime_label(self):\n        \"\"\"Return the label containing the playtime info\"\"\"\n        playtime_label = Gtk.Label(visible=True)\n        playtime_label.set_size_request(120, -1)\n        playtime_label.set_alignment(0, 0.5)\n        playtime_label.set_markup(_(\"Time played:\\n<b>%s</b>\") % self.game.formatted_playtime)\n        return playtime_label\n\n    def get_last_played_label(self):\n        \"\"\"Return the label containing the last played info\"\"\"\n        last_played_label = Gtk.Label(visible=True)\n        last_played_label.set_size_request(120, -1)\n        last_played_label.set_alignment(0, 0.5)\n        lastplayed = datetime.fromtimestamp(self.game.lastplayed)\n        last_played_label.set_markup(_(\"Last played:\\n<b>%s</b>\") % lastplayed.strftime(\"%x\"))\n        return last_played_label\n\n    def get_popover_button(self):\n        \"\"\"Return the popover button+menu for the Play button\"\"\"\n        popover_button = Gtk.MenuButton(visible=True)\n        popover_button.set_size_request(32, 32)\n        popover_button.props.direction = Gtk.ArrowType.UP\n\n        return popover_button\n\n    def get_popover_box(self):\n        \"\"\"Return a container for a button + a popover button attached to it\"\"\"\n        box = Gtk.HBox(visible=True)\n        style_context = box.get_style_context()\n        style_context.add_class(\"linked\")\n        return box\n\n    def get_locate_installed_game_button(self):\n        \"\"\"Return a button to locate an existing install\"\"\"\n        button = get_link_button(\"Locate installed game\")\n        button.show()\n        button.connect(\"clicked\", self.game_actions.on_locate_installed_game, self.game)\n        return {\"locate\": button}\n\n    def get_play_button(self):\n        \"\"\"Return the widget for install/play/stop and game config\"\"\"\n        button = Gtk.Button(visible=True)\n        button.set_size_request(120, 32)\n        box = self.get_popover_box()\n        popover_button = self.get_popover_button()\n        if self.game.is_installed:\n            if self.game.state == self.game.STATE_STOPPED:\n                button.set_label(_(\"Play\"))\n                button.connect(\"clicked\", self.game_actions.on_game_launch)\n            elif self.game.state == self.game.STATE_LAUNCHING:\n                button.set_label(_(\"Launching\"))\n                button.set_sensitive(False)\n            else:\n                button.set_label(_(\"Stop\"))\n                button.connect(\"clicked\", self.game_actions.on_game_stop)\n        else:\n            button.set_label(_(\"Install\"))\n            button.connect(\"clicked\", self.game_actions.on_install_clicked)\n            if self.service:\n                if self.service.local:\n                    # Local services don't show an install dialog, they can be launched directly\n                    button.set_label(_(\"Play\"))\n                if self.service.drm_free:\n                    button.set_size_request(84, 32)\n                    box.add(button)\n                    popover = self.get_popover(self.get_locate_installed_game_button(), popover_button)\n                    popover_button.set_popover(popover)\n                    box.add(popover_button)\n                    return box\n                return button\n        button.set_size_request(84, 32)\n        box.add(button)\n        popover = self.get_popover(self.get_game_buttons(), popover_button)\n        popover_button.set_popover(popover)\n        box.add(popover_button)\n        return box\n\n    def get_game_buttons(self):\n        \"\"\"Return a dictionary of buttons to use in the panel\"\"\"\n        displayed = self.game_actions.get_displayed_entries()\n        buttons = {}\n        for action in self.game_actions.get_game_actions():\n            action_id, label, callback = action\n            if action_id in (\"play\", \"stop\", \"install\"):\n                continue\n            button = get_link_button(label)\n            if displayed.get(action_id):\n                button.show()\n            else:\n                button.hide()\n            buttons[action_id] = button\n            button.connect(\"clicked\", self.on_link_button_clicked, callback)\n        return buttons\n\n    def get_runner_buttons(self):\n        buttons = {}\n        if self.game.runner_name and self.game.is_installed:\n            runner = runners.import_runner(self.game.runner_name)(self.game.config)\n            for entry in runner.context_menu_entries:\n                name, label, callback = entry\n                button = get_link_button(label)\n                button.show()\n                button.connect(\"clicked\", self.on_link_button_clicked, callback)\n                buttons[name] = button\n        return buttons\n\n    def on_link_button_clicked(self, button, callback):\n        \"\"\"Callback for link buttons. Closes the popover then runs the actual action\"\"\"\n        popover = button.get_parent().get_parent()\n        popover.popdown()\n        callback(button)\n\n    def on_install_clicked(self, button):\n        \"\"\"Handler for installing service games\"\"\"\n        self.service.install(self.db_game)\n\n    def on_game_state_changed(self, game):\n        \"\"\"Handler called when the game has changed state\"\"\"\n        if (\n            game.id == self.game.id\n            or (self.appid and game.appid == self.appid)\n        ):\n            self.game = game\n        else:\n            return True\n        self.clear_view()\n        self.update_view()\n        return True\n", "path": "lutris/gui/widgets/game_bar.py"}]}