New UI (#484)
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
"""Components used in multiple places."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from inputremapper.configs.mapping import MappingData
|
||||
from inputremapper.event_combination import EventCombination
|
||||
|
||||
from inputremapper.gui.controller import Controller
|
||||
from inputremapper.gui.gettext import _
|
||||
from inputremapper.gui.messages.message_broker import (
|
||||
MessageBroker,
|
||||
MessageType,
|
||||
)
|
||||
from inputremapper.gui.messages.message_data import GroupData, PresetData
|
||||
from inputremapper.gui.utils import HandlerDisabled
|
||||
|
||||
|
||||
class FlowBoxEntry(Gtk.ToggleButton):
|
||||
"""A device that can be selected in the GUI.
|
||||
|
||||
For example a keyboard or a mouse.
|
||||
"""
|
||||
|
||||
__gtype_name__ = "FlowBoxEntry"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_broker: MessageBroker,
|
||||
controller: Controller,
|
||||
name: str,
|
||||
icon_name: Optional[str] = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.icon_name = icon_name
|
||||
self.message_broker = message_broker
|
||||
self._controller = controller
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
if icon_name:
|
||||
icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG)
|
||||
box.add(icon)
|
||||
|
||||
label = Gtk.Label()
|
||||
label.set_label(name)
|
||||
self.name = name
|
||||
|
||||
# wrap very long names properly
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(2)
|
||||
# this affeects how many device entries fit next to each other
|
||||
label.set_width_chars(28)
|
||||
label.set_max_width_chars(28)
|
||||
|
||||
box.add(label)
|
||||
|
||||
box.set_margin_top(18)
|
||||
box.set_margin_bottom(18)
|
||||
box.set_homogeneous(True)
|
||||
box.set_spacing(12)
|
||||
|
||||
# self.set_relief(Gtk.ReliefStyle.NONE)
|
||||
|
||||
self.add(box)
|
||||
|
||||
self.show_all()
|
||||
|
||||
self.connect("toggled", self._on_gtk_toggle)
|
||||
|
||||
def _on_gtk_toggle(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def show_active(self, active):
|
||||
"""Show the active state without triggering anything."""
|
||||
with HandlerDisabled(self, self._on_gtk_toggle):
|
||||
self.set_active(active)
|
||||
|
||||
|
||||
class FlowBoxWrapper:
|
||||
"""A wrapper for a flowbox that contains FlowBoxEntry widgets."""
|
||||
|
||||
def __init__(self, flowbox: Gtk.FlowBox):
|
||||
self._gui = flowbox
|
||||
|
||||
def show_active_entry(self, name: Optional[str]):
|
||||
"""Activate the togglebutton that matches the name."""
|
||||
for child in self._gui.get_children():
|
||||
flow_box_entry: FlowBoxEntry = child.get_children()[0]
|
||||
flow_box_entry.show_active(flow_box_entry.name == name)
|
||||
|
||||
|
||||
class Breadcrumbs:
|
||||
"""Writes a breadcrumbs string into a given label."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_broker: MessageBroker,
|
||||
label: Gtk.Label,
|
||||
show_device_group: bool = False,
|
||||
show_preset: bool = False,
|
||||
show_mapping: bool = False,
|
||||
):
|
||||
self._message_broker = message_broker
|
||||
self._gui = label
|
||||
self._connect_message_listener()
|
||||
|
||||
self.show_device_group = show_device_group
|
||||
self.show_preset = show_preset
|
||||
self.show_mapping = show_mapping
|
||||
|
||||
self._group_key: str = ""
|
||||
self._preset_name: str = ""
|
||||
self._mapping_name: str = ""
|
||||
|
||||
label.set_max_width_chars(50)
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(2)
|
||||
|
||||
self._render()
|
||||
|
||||
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)
|
||||
self._message_broker.subscribe(MessageType.mapping, self._on_mapping_changed)
|
||||
|
||||
def _on_preset_changed(self, data: PresetData):
|
||||
self._preset_name = data.name or ""
|
||||
self._render()
|
||||
|
||||
def _on_group_changed(self, data: GroupData):
|
||||
self._group_key = data.group_key
|
||||
self._render()
|
||||
|
||||
def _on_mapping_changed(self, mapping_data: MappingData):
|
||||
self._mapping_name = mapping_data.format_name()
|
||||
self._render()
|
||||
|
||||
def _render(self):
|
||||
label = []
|
||||
|
||||
if self.show_device_group:
|
||||
label.append(self._group_key or "?")
|
||||
|
||||
if self.show_preset:
|
||||
label.append(self._preset_name or "?")
|
||||
|
||||
if self.show_mapping:
|
||||
label.append(self._mapping_name or "?")
|
||||
|
||||
self._gui.set_label(" / ".join(label))
|
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Optional
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from inputremapper.gui.components.common import FlowBoxEntry, FlowBoxWrapper
|
||||
from inputremapper.gui.components.editor import ICON_PRIORITIES, ICON_NAMES
|
||||
from inputremapper.gui.components.main import Stack
|
||||
from inputremapper.gui.controller import Controller
|
||||
from inputremapper.gui.messages.message_broker import (
|
||||
MessageBroker,
|
||||
MessageType,
|
||||
)
|
||||
from inputremapper.gui.messages.message_data import (
|
||||
GroupsData,
|
||||
GroupData,
|
||||
DoStackSwitch,
|
||||
)
|
||||
from inputremapper.logger import logger
|
||||
|
||||
|
||||
class DeviceGroupEntry(FlowBoxEntry):
|
||||
"""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,
|
||||
icon_name: Optional[str],
|
||||
group_key: str,
|
||||
):
|
||||
super().__init__(
|
||||
message_broker=message_broker,
|
||||
controller=controller,
|
||||
icon_name=icon_name,
|
||||
name=group_key,
|
||||
)
|
||||
self.group_key = group_key
|
||||
|
||||
def _on_gtk_toggle(self, *_, **__):
|
||||
logger.debug('Selecting device "%s"', self.group_key)
|
||||
self._controller.load_group(self.group_key)
|
||||
self.message_broker.send(DoStackSwitch(Stack.presets_page))
|
||||
|
||||
|
||||
class DeviceGroupSelection(FlowBoxWrapper):
|
||||
"""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,
|
||||
):
|
||||
super().__init__(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)
|
||||
|
||||
def _on_groups_changed(self, data: GroupsData):
|
||||
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,
|
||||
group_key,
|
||||
)
|
||||
self._gui.insert(device_group_entry, -1)
|
||||
|
||||
def _on_group_changed(self, data: GroupData):
|
||||
self.show_active_entry(data.group_key)
|
@ -0,0 +1,139 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
"""Components that wrap everything."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from inputremapper.gui.controller import Controller
|
||||
from inputremapper.gui.messages.message_broker import (
|
||||
MessageBroker,
|
||||
MessageType,
|
||||
)
|
||||
from inputremapper.gui.messages.message_data import StatusData, DoStackSwitch
|
||||
from inputremapper.gui.utils import CTX_ERROR, CTX_MAPPING, CTX_WARNING
|
||||
|
||||
|
||||
class Stack:
|
||||
"""Wraps the Stack, which contains the main menu pages."""
|
||||
|
||||
devices_page = 0
|
||||
presets_page = 1
|
||||
editor_page = 2
|
||||
|
||||
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.do_stack_switch, self._do_stack_switch
|
||||
)
|
||||
|
||||
def _do_stack_switch(self, msg: DoStackSwitch):
|
||||
self._gui.set_visible_child(self._gui.get_children()[msg.page_index])
|
||||
|
||||
|
||||
class StatusBar:
|
||||
"""the status bar on the bottom of the main window"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_broker: MessageBroker,
|
||||
controller: Controller,
|
||||
status_bar: Gtk.Statusbar,
|
||||
error_icon: Gtk.Image,
|
||||
warning_icon: Gtk.Image,
|
||||
):
|
||||
self._message_broker = message_broker
|
||||
self._controller = controller
|
||||
self._gui = status_bar
|
||||
self._error_icon = error_icon
|
||||
self._warning_icon = warning_icon
|
||||
|
||||
self._message_broker.subscribe(MessageType.status_msg, self._on_status_update)
|
||||
self._message_broker.subscribe(MessageType.init, self._on_init)
|
||||
|
||||
# keep track if there is an error or warning in the stack of statusbar
|
||||
# unfortunately this is not exposed over the api
|
||||
self._error = False
|
||||
self._warning = False
|
||||
|
||||
def _on_init(self, _):
|
||||
self._error_icon.hide()
|
||||
self._warning_icon.hide()
|
||||
|
||||
def _on_status_update(self, data: StatusData):
|
||||
"""Show a status message and set its tooltip.
|
||||
|
||||
If message is None, it will remove the newest message of the
|
||||
given context_id.
|
||||
"""
|
||||
context_id = data.ctx_id
|
||||
message = data.msg
|
||||
tooltip = data.tooltip
|
||||
status_bar = self._gui
|
||||
|
||||
if message is None:
|
||||
status_bar.remove_all(context_id)
|
||||
|
||||
if context_id in (CTX_ERROR, CTX_MAPPING):
|
||||
self._error_icon.hide()
|
||||
self._error = False
|
||||
if self._warning:
|
||||
self._warning_icon.show()
|
||||
|
||||
if context_id == CTX_WARNING:
|
||||
self._warning_icon.hide()
|
||||
self._warning = False
|
||||
if self._error:
|
||||
self._error_icon.show()
|
||||
|
||||
status_bar.set_tooltip_text("")
|
||||
else:
|
||||
if tooltip is None:
|
||||
tooltip = message
|
||||
|
||||
self._error_icon.hide()
|
||||
self._warning_icon.hide()
|
||||
|
||||
if context_id in (CTX_ERROR, CTX_MAPPING):
|
||||
self._error_icon.show()
|
||||
self._error = True
|
||||
|
||||
if context_id == CTX_WARNING:
|
||||
self._warning_icon.show()
|
||||
self._warning = True
|
||||
|
||||
max_length = 135
|
||||
if len(message) > max_length:
|
||||
message = message[: max_length - 3] + "..."
|
||||
|
||||
status_bar.push(context_id, message)
|
||||
status_bar.set_tooltip_text(tooltip)
|
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
"""All components that are visible on the page that shows all the presets."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from inputremapper.gui.components.common import FlowBoxEntry, FlowBoxWrapper
|
||||
from inputremapper.gui.components.main import Stack
|
||||
from inputremapper.gui.controller import Controller
|
||||
from inputremapper.gui.messages.message_broker import (
|
||||
MessageBroker,
|
||||
MessageType,
|
||||
)
|
||||
from inputremapper.gui.messages.message_data import (
|
||||
GroupData,
|
||||
PresetData,
|
||||
DoStackSwitch,
|
||||
)
|
||||
from inputremapper.logger import logger
|
||||
|
||||
|
||||
class PresetEntry(FlowBoxEntry):
|
||||
"""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__(
|
||||
message_broker=message_broker, controller=controller, name=preset_name
|
||||
)
|
||||
self.preset_name = preset_name
|
||||
|
||||
def _on_gtk_toggle(self, *_, **__):
|
||||
logger.debug('Selecting preset "%s"', self.preset_name)
|
||||
self._controller.load_preset(self.preset_name)
|
||||
self.message_broker.send(DoStackSwitch(Stack.editor_page))
|
||||
|
||||
|
||||
class PresetSelection(FlowBoxWrapper):
|
||||
"""A wrapper for the container with our presets.
|
||||
|
||||
Selectes the active_preset.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_broker: MessageBroker,
|
||||
controller: Controller,
|
||||
flowbox: Gtk.FlowBox,
|
||||
):
|
||||
super().__init__(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_name in data.presets:
|
||||
preset_entry = PresetEntry(
|
||||
self._message_broker,
|
||||
self._controller,
|
||||
preset_name,
|
||||
)
|
||||
self._gui.insert(preset_entry, -1)
|
||||
|
||||
def _on_preset_changed(self, data: PresetData):
|
||||
self.show_active_entry(data.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)
|
@ -0,0 +1,107 @@
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Tuple, Optional, Callable
|
||||
|
||||
from inputremapper.configs.mapping import MappingData
|
||||
from inputremapper.event_combination import EventCombination
|
||||
from inputremapper.gui.messages.message_types import (
|
||||
MessageType,
|
||||
Name,
|
||||
Capabilities,
|
||||
Key,
|
||||
DeviceTypes,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UInputsData:
|
||||
message_type = MessageType.uinputs
|
||||
uinputs: Dict[Name, Capabilities]
|
||||
|
||||
def __str__(self):
|
||||
string = f"{self.__class__.__name__}(uinputs={self.uinputs})"
|
||||
|
||||
# find all sequences of comma+space separated numbers, and shorten them
|
||||
# to the first and last number
|
||||
all_matches = [m for m in re.finditer("(\d+, )+", string)]
|
||||
all_matches.reverse()
|
||||
for match in all_matches:
|
||||
start = match.start()
|
||||
end = match.end()
|
||||
start += string[start:].find(",") + 2
|
||||
if start == end:
|
||||
continue
|
||||
string = f"{string[:start]}... {string[end:]}"
|
||||
|
||||
return string
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GroupsData:
|
||||
"""Message containing all available groups and their device types"""
|
||||
|
||||
message_type = MessageType.groups
|
||||
groups: Dict[Key, DeviceTypes]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GroupData:
|
||||
"""Message with the active group and available presets for the group"""
|
||||
|
||||
message_type = MessageType.group
|
||||
group_key: str
|
||||
presets: Tuple[str, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PresetData:
|
||||
"""Message with the active preset name and mapping names/combinations"""
|
||||
|
||||
message_type = MessageType.preset
|
||||
name: Optional[Name]
|
||||
mappings: Optional[Tuple[MappingData, ...]]
|
||||
autoload: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StatusData:
|
||||
"""Message with the strings and id for the status bar"""
|
||||
|
||||
message_type = MessageType.status_msg
|
||||
ctx_id: int
|
||||
msg: Optional[str] = None
|
||||
tooltip: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CombinationRecorded:
|
||||
"""Message with the latest recoded combination"""
|
||||
|
||||
message_type = MessageType.combination_recorded
|
||||
combination: "EventCombination"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CombinationUpdate:
|
||||
"""Message with the old and new combination (hash for a mapping) when it changed"""
|
||||
|
||||
message_type = MessageType.combination_update
|
||||
old_combination: "EventCombination"
|
||||
new_combination: "EventCombination"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UserConfirmRequest:
|
||||
"""Message for requesting a user response (confirm/cancel) from the gui"""
|
||||
|
||||
message_type = MessageType.user_confirm_request
|
||||
msg: str
|
||||
respond: Callable[[bool], None] = lambda _: None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DoStackSwitch:
|
||||
"""Command the stack to switch to a different page."""
|
||||
|
||||
message_type = MessageType.do_stack_switch
|
||||
page_index: int
|
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from enum import Enum
|
||||
from typing import Dict, List
|
||||
|
||||
from inputremapper.groups import DeviceType
|
||||
|
||||
# useful type aliases
|
||||
Capabilities = Dict[int, List]
|
||||
Name = str
|
||||
Key = str
|
||||
DeviceTypes = List[DeviceType]
|
||||
|
||||
|
||||
class MessageType(Enum):
|
||||
reset_gui = "reset_gui"
|
||||
terminate = "terminate"
|
||||
init = "init"
|
||||
|
||||
uinputs = "uinputs"
|
||||
groups = "groups"
|
||||
group = "group"
|
||||
preset = "preset"
|
||||
mapping = "mapping"
|
||||
selected_event = "selected_event"
|
||||
combination_recorded = "combination_recorded"
|
||||
recording_started = "recording_started"
|
||||
recording_finished = "recording_finished"
|
||||
combination_update = "combination_update"
|
||||
status_msg = "status_msg"
|
||||
injector_state = "injector_state"
|
||||
|
||||
gui_focus_request = "gui_focus_request"
|
||||
user_confirm_request = "user_confirm_request"
|
||||
|
||||
do_stack_switch = "do_stack_switch"
|
||||
|
||||
# for unit tests:
|
||||
test1 = "test1"
|
||||
test2 = "test2"
|
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 12 KiB |
@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="73" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_1">
|
||||
<rect width="80" height="20" rx="3" fill="#fff"/>
|
||||
<rect width="73" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_1)">
|
||||
<path fill="#555" d="M0 0h44v20H0z"/>
|
||||
<path fill="#4c1" d="M44 0h36v20H44z"/>
|
||||
<path fill="url(#b)" d="M0 0h80v20H0z"/>
|
||||
<path fill="#4C1" d="M44 0h29v20H44z"/>
|
||||
<path fill="url(#b)" d="M0 0h73v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="23.0" y="15" fill="#010101" fill-opacity=".3">pylint</text>
|
||||
<text x="22.0" y="14">pylint</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.18</text>
|
||||
<text x="62.0" y="14">9.18</text>
|
||||
<text x="59.5" y="15" fill="#010101" fill-opacity=".3">8.7</text>
|
||||
<text x="58.5" y="14">8.7</text>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |