Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI 3 #484

Merged
merged 107 commits into from
Oct 16, 2022
Merged

UI 3 #484

Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
af8fd46
using a gtkstack to switch between devices, presets and the editor
sezanzeb Oct 2, 2022
27a0080
moving stop-injection and help to headerbar
sezanzeb Oct 2, 2022
4bb26d1
devices are a flowbox now
sezanzeb Oct 2, 2022
3a98c5a
mypy and margins
sezanzeb Oct 2, 2022
c38421b
fixed tests, changed editor margins
sezanzeb Oct 2, 2022
11fc053
switches to preset selection when clicking on a device
sezanzeb Oct 2, 2022
a5ade7f
split menu, breadcrumbs
sezanzeb Oct 2, 2022
0e5867d
preset selection
sezanzeb Oct 2, 2022
bf45fdb
title line wrapping
sezanzeb Oct 3, 2022
c119fa8
responsive device and preset list
sezanzeb Oct 3, 2022
e4cc6e9
comments
sezanzeb Oct 3, 2022
5ce4c55
fixed margin when scrolling
sezanzeb Oct 3, 2022
d957e14
fixed mapping type switch without a mapping
sezanzeb Oct 3, 2022
bda6736
copying a preset goes back to preset overview
sezanzeb Oct 3, 2022
8a10872
some rearranging of the editor page
sezanzeb Oct 3, 2022
4fcffcf
some rearranging of the editor page
sezanzeb Oct 3, 2022
1a332ec
spacings
sezanzeb Oct 3, 2022
73e10a9
button relief normal
sezanzeb Oct 3, 2022
fcb0e14
some css cleanup, border for mapping list
sezanzeb Oct 3, 2022
93b3038
icon size increased, only matters for other icon themes
sezanzeb Oct 3, 2022
4c2943d
typo
sezanzeb Oct 3, 2022
134ad1a
bold title on presets list page
sezanzeb Oct 3, 2022
752405b
simplified _on_gtk_row_selected, added spacing in combination editor …
sezanzeb Oct 3, 2022
9b44b70
put "Type:" in front of the ouptut switch
sezanzeb Oct 4, 2022
cb0378f
reverted entry spacing, improved window resizing in editor page
sezanzeb Oct 4, 2022
90434b0
removed width request from window
sezanzeb Oct 4, 2022
feeb7ef
adjusted spacings in analog axis editor
sezanzeb Oct 4, 2022
82ab155
rearranged graph
sezanzeb Oct 4, 2022
f141cc0
slider vertical height
sezanzeb Oct 4, 2022
d57f310
reduced margin top for the key/macro editor
sezanzeb Oct 4, 2022
597a8ed
moved advanced button down
sezanzeb Oct 4, 2022
ecb12ee
cleanup of default-widths
sezanzeb Oct 4, 2022
eb7725c
moving buttons up
sezanzeb Oct 4, 2022
7bebafc
rearranged input of editor
sezanzeb Oct 4, 2022
982be45
black
sezanzeb Oct 4, 2022
3e0b213
removed the no-mapping-error, updated docs since the advanced buttons…
sezanzeb Oct 4, 2022
fae3524
breadcrumbs class, increased margin
sezanzeb Oct 6, 2022
390b4cb
changed button order
sezanzeb Oct 6, 2022
9f510ef
modern gtk dialogs, dynamic dialog creation
sezanzeb Oct 8, 2022
511ab15
primary and secondary messages in dialog
sezanzeb Oct 8, 2022
c7fb0ce
comment for _on_user_confirm_request
sezanzeb Oct 8, 2022
6a2f883
cleaner
sezanzeb Oct 8, 2022
87cedfd
using double-quotes in messages
sezanzeb Oct 9, 2022
b562642
black
sezanzeb Oct 9, 2022
0e4ee67
split components.py
sezanzeb Oct 9, 2022
9b94df4
reviewdog
sezanzeb Oct 9, 2022
cdd8849
flowbox baseclasses
sezanzeb Oct 9, 2022
41ce6df
module docstrings, some more refactoring
sezanzeb Oct 9, 2022
5ad4f65
black
sezanzeb Oct 9, 2022
89a9d70
reviewdog
sezanzeb Oct 9, 2022
8a3c2ab
black
sezanzeb Oct 9, 2022
0b0f5af
fixed unittest
sezanzeb Oct 9, 2022
19ab0f3
fixed unittest
sezanzeb Oct 9, 2022
9f68b01
record button does nothing without a mapping
sezanzeb Oct 9, 2022
15e9122
RequireActiveMapping
sezanzeb Oct 9, 2022
bf49b0e
removed obsolete CodeEditor enable/disable logic
sezanzeb Oct 9, 2022
f6731ff
Stop Injection -> Stop
sezanzeb Oct 9, 2022
26eaee3
consistent opacities
sezanzeb Oct 9, 2022
47a2be6
black
sezanzeb Oct 9, 2022
e5a3e5f
changes on combination editor and labels of input-widgets
sezanzeb Oct 10, 2022
e637a9c
more advanced editor stuff
sezanzeb Oct 10, 2022
f5517e5
most test_components tests pass
sezanzeb Oct 10, 2022
f3ee984
flexible tearDown in ComponentBaseTest
sezanzeb Oct 10, 2022
bbb3361
extend docstring of ComponentBaseTest
sezanzeb Oct 10, 2022
2d17467
extend docstring of ComponentBaseTest
sezanzeb Oct 10, 2022
b1c2188
found a method of destroying all unreferenced widgets as well
sezanzeb Oct 11, 2022
e52a5ce
dsfakj
sezanzeb Oct 11, 2022
91d81c3
fixed test_draws_transform
sezanzeb Oct 11, 2022
f9dbbfc
cleanup after test_data
sezanzeb Oct 11, 2022
a239e9c
fixed a few tests
sezanzeb Oct 11, 2022
3ec9f94
fixed more tests
sezanzeb Oct 11, 2022
695ab4a
fixed test_cycling
sezanzeb Oct 11, 2022
003ce99
fixed opacity assertion
sezanzeb Oct 11, 2022
3fc4769
ctrl + n shortcut into UserInterface.shortcuts, removed accelerator
sezanzeb Oct 11, 2022
3af9b93
Merge branch 'beta' into ui-3
sezanzeb Oct 11, 2022
e4ce586
all tests passing
sezanzeb Oct 12, 2022
6f07cf7
some new tests
sezanzeb Oct 12, 2022
293e570
all tests written and passing
sezanzeb Oct 12, 2022
2e3c833
showing mapping conflict error, cleaned utils.py
sezanzeb Oct 12, 2022
4d8fc2c
updated badges, fixed test_show_status
sezanzeb Oct 12, 2022
347a4a1
updated coverage badge
sezanzeb Oct 12, 2022
6d9b637
update screenshots
sezanzeb Oct 12, 2022
4048dc4
review
sezanzeb Oct 13, 2022
d5551f9
reviewdog
sezanzeb Oct 13, 2022
9e1c5c2
comment gtk.main
sezanzeb Oct 13, 2022
9da3afb
black
sezanzeb Oct 13, 2022
7c5cdb9
updated usage.md
sezanzeb Oct 13, 2022
34bcc8d
updated known issues
sezanzeb Oct 13, 2022
f9246f8
changed screenshot
sezanzeb Oct 13, 2022
df75d2a
graph style
sezanzeb Oct 15, 2022
ec93dca
using theme colors for the graph
sezanzeb Oct 15, 2022
4948c51
removed commented line
sezanzeb Oct 15, 2022
e22d138
cleanup
sezanzeb Oct 15, 2022
0244981
cleanup
sezanzeb Oct 15, 2022
053dcf1
typo
sezanzeb Oct 15, 2022
cb28bad
typo
sezanzeb Oct 15, 2022
d597316
updated screenshot
sezanzeb Oct 15, 2022
7f0f03e
updated screenshot
sezanzeb Oct 15, 2022
959de35
updated screenshot and autocompletion position
sezanzeb Oct 15, 2022
644c809
updated screenshot
sezanzeb Oct 15, 2022
2492550
moved Colors to utils
sezanzeb Oct 15, 2022
b7cb845
PresetData uses MappingData, format_name, duplicate name formatting code
sezanzeb Oct 16, 2022
91ed3b8
mypy
sezanzeb Oct 16, 2022
5a6c106
message_classes -> message_data
sezanzeb Oct 16, 2022
75fd672
mypy
sezanzeb Oct 16, 2022
ed33553
get_bus_message
sezanzeb Oct 16, 2022
5d96d77
changed punctuation in output type message
sezanzeb Oct 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1,352 changes: 720 additions & 632 deletions data/input-remapper.glade

Large diffs are not rendered by default.

310 changes: 256 additions & 54 deletions inputremapper/gui/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,70 +73,159 @@
]


class DeviceSelection:
"""the dropdown menu to select the active_group"""
class DeviceGroupEntry(Gtk.ToggleButton):
"""A device that can be selected in the GUI.

For example a keyboard or a mouse.
"""

__gtype_name__ = "DeviceGroupEntry"

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
combobox: Gtk.ComboBox,
icon_name: Optional[str],
group_key: str,
):
self._message_broker = message_broker
super().__init__()
self.icon_name = icon_name
self.group_key = group_key
self._controller = controller
self._device_store = Gtk.ListStore(str, str, str)
self._gui = combobox

# https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html#the-view
combobox.set_model(self._device_store)
renderer_icon = Gtk.CellRendererPixbuf()
renderer_text = Gtk.CellRendererText()
renderer_text.set_padding(5, 0)
combobox.pack_start(renderer_icon, False)
combobox.pack_start(renderer_text, False)
combobox.add_attribute(renderer_icon, "icon-name", 1)
combobox.add_attribute(renderer_text, "text", 2)
combobox.set_id_column(0)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

if icon_name:
icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DND)
box.add(icon)

label = Gtk.Label()
label.set_label(group_key)
box.add(label)

box.set_margin_top(18)
box.set_margin_bottom(18)
box.set_homogeneous(True)
box.set_spacing(12)

self.add(box)

self.show_all()

self.connect("toggled", self._on_gtk_select_device)

def _on_gtk_select_device(self, *_, **__):
logger.debug('Selecting device "%s"', self.group_key)
self._controller.load_group(self.group_key)

def show_active(self, active):
"""Show the active state without triggering anything."""
with HandlerDisabled(self, self._on_gtk_select_device):
self.set_active(active)


class DeviceGroupSelection:
"""A wrapper for the container with our groups.

A group is a collection of devices.
"""

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
flowbox: Gtk.FlowBox,
):
self._message_broker = message_broker
self._controller = controller
self._gui = flowbox

self._message_broker.subscribe(MessageType.groups, self._on_groups_changed)
self._message_broker.subscribe(MessageType.group, self._on_group_changed)
combobox.connect("changed", self._on_gtk_select_device)

def _on_groups_changed(self, data: GroupsData):
with HandlerDisabled(self._gui, self._on_gtk_select_device):
self._device_store.clear()
for group_key, types in data.groups.items():
if len(types) > 0:
device_type = sorted(types, key=ICON_PRIORITIES.index)[0]
icon_name = ICON_NAMES[device_type]
else:
icon_name = None

logger.debug(f"adding {group_key} to device dropdown ")
self._device_store.append([group_key, icon_name, group_key])
self._gui.foreach(lambda group: self._gui.remove(group))

for group_key, types in data.groups.items():
if len(types) > 0:
device_type = sorted(types, key=ICON_PRIORITIES.index)[0]
icon_name = ICON_NAMES[device_type]
else:
icon_name = None

logger.debug(f"adding {group_key} to device selection")
device_group_entry = DeviceGroupEntry(
self._message_broker,
self._controller,
icon_name,
sezanzeb marked this conversation as resolved.
Show resolved Hide resolved
group_key,
)
self._gui.insert(device_group_entry, -1)

def _on_group_changed(self, data: GroupData):
with HandlerDisabled(self._gui, self._on_gtk_select_device):
self._gui.set_active_id(data.group_key)
self.show_active_group_key(data.group_key)

def _on_gtk_select_device(self, *_, **__):
group_key = self._gui.get_active_id()
logger.debug('Selecting device "%s"', group_key)
self._controller.load_group(group_key)
def get_active_device_group_entry(self) -> DeviceGroupEntry:
sezanzeb marked this conversation as resolved.
Show resolved Hide resolved
"""Find the currently selected DeviceGroupEntry."""
# TODO only used in tests, move there
for child in self._gui.get_children():
device_group_entry = child.get_children()[0]

if device_group_entry.get_active():
return device_group_entry

# I dunno if this can happen
raise Exception("Expected one device group to be selected.")

def set_active_group_key(self, group_key: str):
"""Change the currently selected group."""
# TODO only used in tests
for child in self._gui.get_children():
device_group_entry: DeviceGroupEntry = child.get_children()[0]
device_group_entry.set_active(device_group_entry.group_key == group_key)

def show_active_group_key(self, group_key: str):
"""Highlight the button of the given group."""
for child in self._gui.get_children():
device_group_entry: DeviceGroupEntry = child.get_children()[0]
device_group_entry.show_active(device_group_entry.group_key == group_key)


# TODO test
class Stack:
"""Wraps the Stack ("Devices", "Presets", "Editor")."""
def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
stack: Gtk.Stack,
):
self._message_broker = message_broker
self._controller = controller
self._gui = stack

self._message_broker.subscribe(MessageType.group, self._on_group_changed)

def _on_group_changed(self, _):
# switch to the preset selection
self._gui.set_visible_child(self._gui.get_children()[1])


class TargetSelection:
"""the dropdown menu to select the targe_uinput of the active_mapping"""
"""The dropdown menu to select the targe_uinput of the active_mapping,

For example "keyboard" or "gamepad".
"""

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
combobox: Gtk.ComboBox,
listbox: Gtk.listbox,
):
self._message_broker = message_broker
self._controller = controller
self._gui = combobox
self._gui = listbox

self._message_broker.subscribe(MessageType.uinputs, self._on_uinputs_changed)
self._message_broker.subscribe(MessageType.mapping, self._on_mapping_loaded)
Expand Down Expand Up @@ -175,40 +264,150 @@ def _on_gtk_target_selected(self, *_):
self._controller.update_mapping(target_uinput=target)


class PresetSelection:
"""the dropdown menu to select the active_preset"""
class PresetEntry(Gtk.ToggleButton):
"""A preset that can be selected in the GUI."""

__gtype_name__ = "PresetEntry"

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
preset_name: str,
):
super().__init__()
self.preset_name = preset_name
self._controller = controller

box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

label = Gtk.Label()
label.set_label(preset_name)
box.add(label)

box.set_margin_top(18)
box.set_margin_bottom(18)
box.set_homogeneous(True)
box.set_spacing(12)

self.add(box)

self.show_all()

self.connect("toggled", self._on_gtk_select_preset)

def _on_gtk_select_preset(self, *_, **__):
logger.debug('Selecting preset "%s"', self.preset_name)
self._controller.load_preset(self.preset_name)

def show_active(self, active):
"""Show the active state without triggering anything."""
with HandlerDisabled(self, self._on_gtk_select_preset):
self.set_active(active)


# TODO test
class PresetSelectionTitle:
"""The title of the preset selection, which is the device name."""

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
combobox: Gtk.ComboBoxText,
label: Gtk.Label,
):
self._message_broker = message_broker
self._controller = controller
self._gui = combobox
self._gui = label
self._connect_message_listener()

def _connect_message_listener(self):
self._message_broker.subscribe(MessageType.group, self._on_group_changed)

def _on_group_changed(self, data: GroupData):
self._gui.set_label(data.group_key)


# TODO test
class EditorTitle:
"""The title of the editor, which is the device and preset name."""

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
label: Gtk.Label,
):
self._message_broker = message_broker
self._controller = controller
self._gui = label
self._connect_message_listener()
combobox.connect("changed", self._on_gtk_select_preset)

self._group_key = ""
self._preset_name = ""

def _connect_message_listener(self):
self._message_broker.subscribe(MessageType.group, self._on_group_changed)
self._message_broker.subscribe(MessageType.preset, self._on_preset_changed)

def _on_preset_changed(self, data: PresetData):
self._preset_name = data.name
sezanzeb marked this conversation as resolved.
Show resolved Hide resolved
self._render()

def _on_group_changed(self, data: GroupData):
with HandlerDisabled(self._gui, self._on_gtk_select_preset):
self._gui.remove_all()
for preset in data.presets:
self._gui.append(preset, preset)
self._group_key = data.group_key
self._render()

def _render(self):
self._gui.set_label(f"{self._group_key} / {self._preset_name}")


class PresetSelection:
"""A wrapper for the container with our presets.

Selectes the active_preset.
"""

def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
flowbox: Gtk.FlowBox,
):
self._message_broker = message_broker
self._controller = controller
self._gui = flowbox
self._connect_message_listener()

def _connect_message_listener(self):
self._message_broker.subscribe(MessageType.group, self._on_group_changed)
self._message_broker.subscribe(MessageType.preset, self._on_preset_changed)

def _on_group_changed(self, data: GroupData):
self._gui.foreach(lambda preset: self._gui.remove(preset))
for preset in data.presets:
preset_entry = PresetEntry(
self._message_broker,
self._controller,
preset,
)
self._gui.insert(preset_entry, -1)

def _on_preset_changed(self, data: PresetData):
with HandlerDisabled(self._gui, self._on_gtk_select_preset):
self._gui.set_active_id(data.name)
self.show_active_preset(data.name)
Copy link

Choose a reason for hiding this comment

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

[mypy] reported by reviewdog 🐶
error: Argument 1 to "show_active_preset" of "PresetSelection" has incompatible type "Optional[str]"; expected "str"


def _on_gtk_select_preset(self, *_, **__):
name = self._gui.get_active_id()
logger.debug('Selecting preset "%s"', name)
self._controller.load_preset(name)
def set_active_preset(self, preset_name: str):
"""Change the currently selected preset."""
# TODO might only be needed in tests
for child in self._gui.get_children():
preset_entry: PresetEntry = child.get_children()[0]
preset_entry.set_active(preset_entry.preset_name == preset_name)

def show_active_preset(self, preset_name: str):
"""Highlight the button of the given preset."""
for child in self._gui.get_children():
preset_entry: PresetEntry = child.get_children()[0]
preset_entry.show_active(preset_entry.preset_name == preset_name)


class MappingListBox:
Expand Down Expand Up @@ -246,7 +445,10 @@ def _on_preset_changed(self, data: PresetData):

for name, combination in data.mappings:
selection_label = SelectionLabel(
self._message_broker, self._controller, name, combination
self._message_broker,
self._controller,
name,
combination,
)
self._gui.insert(selection_label, -1)
self._gui.invalidate_sort()
Expand Down Expand Up @@ -931,7 +1133,7 @@ def __init__(
self,
message_broker: MessageBroker,
controller: Controller,
gui: Gtk.ComboBox,
gui: Gtk.listbox,
):
self._message_broker = message_broker
self._controller = controller
Expand Down