Skip to content

Commit 4833c62

Browse files
committed
Skip confusing buttons from dom0 and disp template
- Dom0: hide shutdown and pause, technically, shutdown can be used by GUIVMs in the future if the action was modified, currently, qubesd logs a failure. - There is no possibility to differentiate between "APPS" and "TEMPLATES" tabs, this means that disposable template "Start qube" is hidden from both tabs, that is not the intended behavior though but would require a redesign to fix. Current behavior though, allows searching for "default-dvm" and not have the "Start qube" button appear. Therefore, to start the disposable template from the app menu, select an application in the "TEMPLATES" tab. Fixes: QubesOS/qubes-issues#10288 For: QubesOS/qubes-issues#1512
1 parent 4d858cd commit 4833c62

5 files changed

Lines changed: 130 additions & 50 deletions

File tree

qubes_menu/app_widgets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
logger = logging.getLogger("qubes-appmenu")
4747

48-
DISP_TEXT = "new Disposable Qube from "
48+
DISP_TEXT = "New disposable qube from "
4949

5050

5151
class AppEntry(Gtk.ListBoxRow):

qubes_menu/application_page.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ def _selection_changed(self, _widget, row: Optional[VMRow]):
342342
self.selected_vm_entry = row
343343
self._set_right_visibility(True)
344344
self.network_indicator.set_network_state(row.vm_entry.has_network)
345-
self.control_list.update_visibility(row.vm_entry.power_state)
345+
self.control_list.update_visibility(
346+
row.vm_entry, self.toggle_buttons.apps_toggle.get_active()
347+
)
346348
self.control_list.unselect_all()
347349
self.app_list.ephemeral_vm = bool(self.selected_vm_entry.vm_entry.parent_vm)
348350
self.app_list.invalidate_filter()

qubes_menu/custom_widgets.py

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ def get_appinfo(self) -> ApplicationInfo:
269269
vm_entry.settings_desktop_file_name
270270
)
271271

272-
def update_state(self, state): # pylint: disable=unused-argument
272+
def update_state(self, vm_entry: VMEntry):
273273
"""Update state: should be always visible."""
274+
# pylint: disable=unused-argument
274275
self.show_all()
275276

276277
def show_menu(self, _widget, event):
@@ -449,7 +450,7 @@ def __init__(self):
449450
self.show_all()
450451
self.command = None
451452

452-
def update_state(self, state):
453+
def update_state(self, vm_entry: VMEntry):
453454
"""
454455
Update own state (visibility/text/sensitivity) based on provided VM
455456
state.
@@ -497,35 +498,49 @@ def show_menu(self, _widget, event):
497498
if event.button == 3:
498499
self.menu.popup_at_pointer(None) # None means current event
499500

500-
def update_state(self, state):
501+
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
501502
"""
502503
Update own state (visibility/text/sensitivity) based on provided VM
503504
state.
504505
"""
505-
self.state = state
506-
if state == "Running":
507-
self.row_label.set_label("Shutdown qube")
508-
self.command = "qvm-shutdown"
506+
vm_name = vm_entry.vm_name
507+
is_dispvm_template = vm_entry.is_dispvm_template
508+
self.state = vm_entry.power_state
509+
510+
if (
511+
vm_name == "dom0"
512+
or (apps_tab and is_dispvm_template and self.state != "Running")
513+
):
514+
self.row_label.set_label(" ")
515+
self.set_sensitive(False)
516+
self.command = None
517+
self.icon.hide()
518+
return
519+
520+
self.set_sensitive(True)
521+
self.icon.show()
522+
if self.state == "Halted" and not (is_dispvm_template and apps_tab):
523+
self.row_label.set_label("Start qube")
524+
self.command = "qvm-start"
509525
self.icon.set_from_pixbuf(
510-
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
526+
load_icon("qappmenu-start", size=None, pixel_size=15)
511527
)
512528
return
513-
if state == "Transient":
529+
if self.state == "Transient":
514530
self.row_label.set_label("Kill qube")
515531
self.command = "qvm-kill"
516532
self.icon.set_from_pixbuf(
517533
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
518534
)
519535
return
520-
if state == "Halted":
521-
self.row_label.set_label("Start qube")
522-
self.command = "qvm-start"
536+
if self.state == "Running":
537+
self.row_label.set_label("Shutdown qube")
538+
self.command = "qvm-shutdown"
523539
self.icon.set_from_pixbuf(
524-
load_icon("qappmenu-start", size=None, pixel_size=15)
540+
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
525541
)
526-
527542
return
528-
if state == "Paused":
543+
if self.state == "Paused":
529544
self.row_label.set_label("Unpause qube")
530545
self.command = "qvm-unpause"
531546
self.icon.set_from_pixbuf(
@@ -544,13 +559,16 @@ def __init__(self):
544559
self.icon.set_from_pixbuf(load_icon("qappmenu-pause", size=None, pixel_size=15))
545560
self.state = None
546561

547-
def update_state(self, state):
562+
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
548563
"""
549564
Update own state (visibility/text/sensitivity) based on provided VM
550565
state.
551566
"""
552-
self.state = state
553-
if state == "Running":
567+
# pylint: disable=unused-argument
568+
vm_name = vm_entry.vm_name
569+
self.state = vm_entry.power_state
570+
571+
if self.state == "Running" and vm_name != "dom0":
554572
self.row_label.set_label("Pause qube")
555573
self.set_sensitive(True)
556574
self.command = "qvm-pause"
@@ -581,12 +599,12 @@ def __init__(self, app_page):
581599
self.add(self.start_item)
582600
self.add(self.pause_item)
583601

584-
def update_visibility(self, state):
602+
def update_visibility(self, vm_entry: VMEntry, apps_tab: bool = False):
585603
"""
586604
Update children's state based on provided VM state.
587605
"""
588606
for row in self.get_children():
589-
row.update_state(state)
607+
row.update_state(vm_entry, apps_tab)
590608

591609

592610
class KeynavController:

qubes_menu/search_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def _selection_changed(self, _widget, row: Optional[SearchVMRow]):
511511
else:
512512
self.selected_vm_row = row
513513
self.control_list.show()
514-
self.control_list.update_visibility(row.vm_entry.power_state)
514+
self.control_list.update_visibility(row.vm_entry, apps_tab=True)
515515
self.control_list.unselect_all()
516516
self.app_list.invalidate_filter()
517517
self.app_list.select_row(None)

qubes_menu/tests/test_app_page.py

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,101 @@ def test_app_page_vm_state(test_desktop_file_path, test_qapp, test_builder):
3030
dispatcher = MockDispatcher(test_qapp)
3131
vm_manager = VMManager(test_qapp, dispatcher)
3232

33-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
34-
[test_desktop_file_path]):
33+
with mock.patch.object(
34+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
35+
):
3536
desktop_file_manager = DesktopFileManager(test_qapp)
3637

3738
app_page = AppPage(vm_manager, test_builder, desktop_file_manager)
3839

39-
# select a turned off vm
40-
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
41-
if row.vm_name == 'test-red'][0])
40+
# select dom0
41+
app_page.vm_list.select_row(
42+
[
43+
row
44+
for row in app_page.vm_list.get_children()
45+
if row.vm_name == "dom0"
46+
][0]
47+
)
48+
assert app_page.control_list.start_item.row_label.get_label() == " "
49+
assert app_page.control_list.pause_item.row_label.get_label() == " "
4250

43-
assert app_page.control_list.start_item.row_label.get_label() == \
44-
"Start qube"
45-
assert app_page.control_list.pause_item.row_label.get_label() == \
46-
" "
51+
# select a turned off vm
52+
app_page.vm_list.select_row(
53+
[
54+
row
55+
for row in app_page.vm_list.get_children()
56+
if row.vm_name == "test-red"
57+
][0]
58+
)
59+
60+
assert (
61+
app_page.control_list.start_item.row_label.get_label() == "Start qube"
62+
)
63+
assert app_page.control_list.pause_item.row_label.get_label() == " "
4764

4865
# select a turned on vm
49-
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
50-
if row.vm_name == 'sys-usb'][0])
51-
52-
assert app_page.control_list.start_item.row_label.get_label() == \
53-
"Shutdown qube"
54-
assert app_page.control_list.pause_item.row_label.get_label() == \
55-
"Pause qube"
66+
app_page.vm_list.select_row(
67+
[
68+
row
69+
for row in app_page.vm_list.get_children()
70+
if row.vm_name == "sys-usb"
71+
][0]
72+
)
73+
74+
assert (
75+
app_page.control_list.start_item.row_label.get_label()
76+
== "Shutdown qube"
77+
)
78+
assert (
79+
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
80+
)
81+
82+
# select a turned off disposable template
83+
app_page.vm_list.select_row(
84+
[
85+
row
86+
for row in app_page.vm_list.get_children()
87+
if row.vm_name == "test-alt-dvm"
88+
][0]
89+
)
90+
assert app_page.control_list.start_item.row_label.get_label() == " "
91+
assert app_page.control_list.pause_item.row_label.get_label() == " "
92+
93+
# select a turned on disposable template
94+
app_page.vm_list.select_row(
95+
[
96+
row
97+
for row in app_page.vm_list.get_children()
98+
if row.vm_name == "test-alt-dvm-running"
99+
][0]
100+
)
101+
assert (
102+
app_page.control_list.start_item.row_label.get_label()
103+
== "Shutdown qube"
104+
)
105+
assert (
106+
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
107+
)
56108

57109

58110
def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):
59111
# check if dispvm child is sorted after the parent
60-
test_qapp._qubes['disp1233'] = MockQube(
61-
name="disp1233", qapp=test_qapp, klass='DispVM',
62-
template_for_dispvms='True', template='default-dvm', auto_cleanup=True)
112+
test_qapp._qubes["disp1233"] = MockQube(
113+
name="disp1233",
114+
qapp=test_qapp,
115+
klass="DispVM",
116+
template_for_dispvms="True",
117+
template="default-dvm",
118+
auto_cleanup=True,
119+
)
63120
test_qapp.update_vm_calls()
64121

65122
dispatcher = MockDispatcher(test_qapp)
66123
vm_manager = VMManager(test_qapp, dispatcher)
67124

68-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
69-
[test_desktop_file_path]):
125+
with mock.patch.object(
126+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
127+
):
70128
desktop_file_manager = DesktopFileManager(test_qapp)
71129

72130
app_page = AppPage(vm_manager, test_builder, desktop_file_manager)
@@ -75,11 +133,11 @@ def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):
75133

76134
for row in app_page.vm_list.get_children():
77135
if found_dvm:
78-
if row.vm_name == 'disp1233' and row.vm_entry.parent_vm:
136+
if row.vm_name == "disp1233" and row.vm_entry.parent_vm:
79137
break
80138
found_dvm = False
81139
continue
82-
if row.vm_name == 'default-dvm' and row.vm_entry._is_dispvm_template:
140+
if row.vm_entry.is_dispvm_template:
83141
found_dvm = True
84142
continue
85143
found_dvm = False
@@ -92,12 +150,14 @@ def test_settings_app_page(test_desktop_file_path, test_qapp, test_builder):
92150
dispatcher = MockDispatcher(test_qapp)
93151
vm_manager = VMManager(test_qapp, dispatcher)
94152

95-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
96-
[test_desktop_file_path]):
153+
with mock.patch.object(
154+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
155+
):
97156
desktop_file_manager = DesktopFileManager(test_qapp)
98157

99-
settings_page = SettingsPage(test_qapp, test_builder,
100-
desktop_file_manager, dispatcher)
158+
settings_page = SettingsPage(
159+
test_qapp, test_builder, desktop_file_manager, dispatcher
160+
)
101161

102162
for row in settings_page.app_list.get_children():
103163
assert not row.app_info.vm

0 commit comments

Comments
 (0)