From 72587f427620c0d591e19d7cf41a195aa2a99a45 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 26 May 2023 21:45:11 +0200 Subject: [PATCH 01/69] Scroll area will no longer expand settings horizontally --- shortcut_composer/INFO.py | 2 +- .../pie_menu_utils/settings_gui/scroll_area.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/shortcut_composer/INFO.py b/shortcut_composer/INFO.py index ad6b66bb..acf1f139 100644 --- a/shortcut_composer/INFO.py +++ b/shortcut_composer/INFO.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -__version__ = "1.3.2" +__version__ = "1.4.0dev" __author__ = "Wojciech Trybus" __license__ = "GPL-3.0-or-later" diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py index 3af534ed..e5e08d1e 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py @@ -12,7 +12,8 @@ QLabel, QLineEdit, QVBoxLayout, - QHBoxLayout) + QHBoxLayout, + QSizePolicy) from ..label import Label from ..label_widget import LabelWidget @@ -82,7 +83,7 @@ def __init__( self._children_list: List[LabelWidget] = [] self._grid = OffsetGridLayout(self._columns, self) - self._active_label_display = QLabel(self) + self._active_label_display = self._init_active_label_display() self._search_bar = self._init_search_bar() self._layout = self._init_layout() @@ -106,6 +107,15 @@ def _init_layout(self) -> QVBoxLayout: layout.addLayout(footer) return layout + def _init_active_label_display(self): + """Return a label displaying hovered label.""" + label = QLabel(self) + label.setSizePolicy( + QSizePolicy.Ignored, + QSizePolicy.Expanding) + label.setWordWrap(True) + return label + def _init_scroll_area(self) -> QScrollArea: """Create a widget, which scrolls internal widget with grid layout.""" internal = QWidget() From 46d9959dd188795db17e27c40ab8af3af03c4c5f Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 26 May 2023 21:45:11 +0200 Subject: [PATCH 02/69] Simplify pretty names creation --- shortcut_composer/api_krita/enums/blending_mode.py | 6 ++---- shortcut_composer/api_krita/enums/tool.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/shortcut_composer/api_krita/enums/blending_mode.py b/shortcut_composer/api_krita/enums/blending_mode.py index 381c5cae..1afc0778 100644 --- a/shortcut_composer/api_krita/enums/blending_mode.py +++ b/shortcut_composer/api_krita/enums/blending_mode.py @@ -143,7 +143,5 @@ class BlendingMode(Enum): @property def pretty_name(self) -> str: - """Format blending mode name like: `Darker Color`.""" - parts = self.name.split("_") - parts = [f"{part[0]}{part[1:].lower()}" for part in parts] - return " ".join(parts) + """Format blending mode name like: `Darker color`.""" + return self.name.replace("_", " ").capitalize() diff --git a/shortcut_composer/api_krita/enums/tool.py b/shortcut_composer/api_krita/enums/tool.py index 38a631ae..3d9cbb55 100644 --- a/shortcut_composer/api_krita/enums/tool.py +++ b/shortcut_composer/api_krita/enums/tool.py @@ -69,7 +69,7 @@ def icon(self) -> QIcon: @property def pretty_name(self) -> str: """Format tool name like: `Shape select tool`.""" - return f"{self.name[0]}{self.name[1:].lower().replace('_', ' ')} tool" + return f"{self.name.replace('_', ' ').capitalize()} tool" _PAINTABLE = { From b0969fadb8746742c974b603548307fa60726c14 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 26 May 2023 21:45:11 +0200 Subject: [PATCH 03/69] Adds EnumGroup base class --- .../api_krita/enums/helpers/__init__.py | 3 + .../api_krita/enums/helpers/enum_group.py | 102 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 shortcut_composer/api_krita/enums/helpers/__init__.py create mode 100644 shortcut_composer/api_krita/enums/helpers/enum_group.py diff --git a/shortcut_composer/api_krita/enums/helpers/__init__.py b/shortcut_composer/api_krita/enums/helpers/__init__.py new file mode 100644 index 00000000..02be4e1a --- /dev/null +++ b/shortcut_composer/api_krita/enums/helpers/__init__.py @@ -0,0 +1,3 @@ +from .enum_group import EnumGroup + +__all__ = ["EnumGroup"] diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py new file mode 100644 index 00000000..4886f70c --- /dev/null +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -0,0 +1,102 @@ +from typing import Any, Dict, Set, Tuple, Type +from enum import Enum + + +class EnumGroupMetaclass(type): + """Metaclass for creating enum groups. See EnumGroup documentation.""" + + def __init__( + self, + name: str, + bases: Tuple[type, ...], + attrs: Dict[str, Any] + ) -> None: + super().__init__(name, bases, attrs) + + self._enums_ = MetaclassTools.get_enum_types(attrs) + """Dictionary mapping enum types to their names.""" + + self._compound_ = MetaclassTools.create_compound(self._enums_) + """Enum containing elements of all grouped enums.""" + + +class MetaclassTools: + """Organizes methods used by EnumGroupMetaclass.""" + + @staticmethod + def get_enum_types(attrs: Dict[str, Any]) -> Dict[str, Type[Enum]]: + """Filter attribute dict leaving only enum types.""" + def is_enum_type(value: Any) -> bool: + """Return True when value is a class and inherits Enum.""" + return isinstance(value, type) and issubclass(value, Enum) + + return {name: val for name, val in attrs.items() if is_enum_type(val)} + + @classmethod + def create_compound(cls, enum_types: Dict[str, Type[Enum]]) -> Type[Enum]: + """Combine passed enums types into single enum.""" + # maps enum values to their names from all the passed types + compound_attrs = {} + for enum in enum_types.values(): + + # raise an error when two enum types define the same name + if common := cls.common_keys(compound_attrs, enum._member_map_): + raise RuntimeError(f"Enums have repeated keys: {common}") + + # update dictionary + args = {name: val.value for name, val in enum._member_map_.items()} + compound_attrs.update(args) + + # dynamically create enum composite keeping the same base class + enum_base = cls.get_enum_base(enum_types) + return enum_base("Compound", compound_attrs) + + @staticmethod + def common_keys(dict_1: dict, dict_2: dict) -> Set: + """Return the set of common keys of two dictionaries.""" + keys_1 = set(dict_1.keys()) + keys_2 = set(dict_2.keys()) + return keys_1.intersection(keys_2) + + @staticmethod + def get_enum_base(enum_types: Dict[str, Type[Enum]]) -> Type[Enum]: + """ + Return common base class of given enum types. + + Raise exception when their bases are not exactly the same. + """ + enum_bases = [enum.__base__ for enum in enum_types.values()] + if not enum_bases[:-1] == enum_bases[1:]: + raise RuntimeError("All enums must have the same base.") + return enum_bases[0] if enum_bases else Enum + + +class EnumGroup(metaclass=EnumGroupMetaclass): + """ + Base class for creating enum groups. + + Intended use is to place enum definitions inside a subclass: + ``` + class CustomEnumGroup(EnumGroup): + class EnumA(Enum): + ASD = "asd" + QWE = "qwe" + + class EnumB(Enum): + Z = "z" + X = "x" + ``` + Child class automatically provides following class attributes: + - `_compound_` - dynamically created enum containing elements of + all grouped enums. + - `_enums_` - dictionary mapping enum types to their names. + + Grouped enums do not need to directly inherit from Enum. If subclass + of enum is used, the composite will also be based on the same class. + + Grouped enum types must follow those rules: + - All passed enum types must inherit directly from the same class. + - Enums cannot define the same names. + + Breaking those rules will result in exception during class creation. + """ From f8c6bca1ba078baf98296e91837b0664fb02f631 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 04/69] Rewrite EnumGroupMetaclass in a way which works better with typing --- .../api_krita/enums/helpers/enum_group.py | 136 +++++++++++------- 1 file changed, 84 insertions(+), 52 deletions(-) diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py index 4886f70c..cfaa3c34 100644 --- a/shortcut_composer/api_krita/enums/helpers/enum_group.py +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -1,9 +1,50 @@ -from typing import Any, Dict, Set, Tuple, Type +from typing import Any, Dict, Set, Iterable, Tuple, Type, Generic, TypeVar from enum import Enum +T = TypeVar("T", bound=Enum) -class EnumGroupMetaclass(type): - """Metaclass for creating enum groups. See EnumGroup documentation.""" +class EnumGroupMeta(type, Generic[T]): + """ + Metaclass for creating enum groups. + + Intended use is to place enum definitions inside a class definition: + ``` + class EnumGroup(metaclass=EnumGroupMeta[Enum]): + class EnumA(Enum): + ASD = "asd" + QWE = "qwe" + + class EnumB(Enum): + Z = "z" + X = "x" + ``` + Metaclass makes its instance act as a composite of all the grouped + enums. Although the instance technically is not an Enum subclass, + it provides the following Enum class attributes: + - `_member_map_` + - `_value2member_map_` + + Enum members can be fetched in multiple ways: + - Using the original, grouped Enum class: + - EnumGroup.EnumA.ASD + - EnumGroup.EnumA["ASD"] + - EnumGroup.EnumA("asd") + + - Using the composite class directly: + - EnumGroup.ASD + - EnumGroup["ASD"] + - EnumGroup("asd") + + Grouped enum types must follow those rules: + - All passed enum types must inherit directly from the same Enum. + - Grouped enums cannot define the same names. + + Breaking those rules will result in exception during class creation. + + Grouped enums do not need to directly inherit from Enum. + Metaclass is a Generic - for typing purposes, base class of grouped + enum types should also be passed to the metaclass. + """ def __init__( self, @@ -13,15 +54,31 @@ def __init__( ) -> None: super().__init__(name, bases, attrs) - self._enums_ = MetaclassTools.get_enum_types(attrs) - """Dictionary mapping enum types to their names.""" + self._grouped_enums_ = MetaInit.get_enum_types(attrs) + """Dictionary mapping grouped enum types to their names.""" + MetaInit.ensure_common_base(self._grouped_enums_.values()) + + self._member_map_ = MetaInit.init_member_map(self._grouped_enums_) + """Dictionary mapping enum members to their names.""" + + self._value2member_map_ = MetaInit.init_reverse_map(self._member_map_) + """Dictionary mapping enum members to their values.""" + + def __getattr__(self, name: str) -> T: + """Return enum member as attribute of the composite.""" + return self._member_map_[name] + + def __getitem__(self, name: str) -> T: + """Return enum member by its name.""" + return self._member_map_[name] - self._compound_ = MetaclassTools.create_compound(self._enums_) - """Enum containing elements of all grouped enums.""" + def __call__(self, value: str) -> T: + """Return enum member by its value.""" + return self._value2member_map_[value] -class MetaclassTools: - """Organizes methods used by EnumGroupMetaclass.""" +class MetaInit: + """Organizes methods used by EnumGroupMeta.""" @staticmethod def get_enum_types(attrs: Dict[str, Any]) -> Dict[str, Type[Enum]]: @@ -33,23 +90,30 @@ def is_enum_type(value: Any) -> bool: return {name: val for name, val in attrs.items() if is_enum_type(val)} @classmethod - def create_compound(cls, enum_types: Dict[str, Type[Enum]]) -> Type[Enum]: - """Combine passed enums types into single enum.""" + def init_member_map(cls, enum_types: Dict[str, Type[Enum]]): + """Return dict mapping enum members to their names.""" # maps enum values to their names from all the passed types - compound_attrs = {} + member_map = {} for enum in enum_types.values(): # raise an error when two enum types define the same name - if common := cls.common_keys(compound_attrs, enum._member_map_): + if common := cls.common_keys(member_map, enum._member_map_): raise RuntimeError(f"Enums have repeated keys: {common}") # update dictionary - args = {name: val.value for name, val in enum._member_map_.items()} - compound_attrs.update(args) + args = {name: val for name, val in enum._member_map_.items()} + member_map.update(args) - # dynamically create enum composite keeping the same base class - enum_base = cls.get_enum_base(enum_types) - return enum_base("Compound", compound_attrs) + return member_map + + @staticmethod + def init_reverse_map(member_map: dict): + """Return dict mapping enum members to their values.""" + reverse_map = {} + for value in member_map.values(): + if value not in reverse_map.values(): + reverse_map[value.value] = value + return reverse_map @staticmethod def common_keys(dict_1: dict, dict_2: dict) -> Set: @@ -59,44 +123,12 @@ def common_keys(dict_1: dict, dict_2: dict) -> Set: return keys_1.intersection(keys_2) @staticmethod - def get_enum_base(enum_types: Dict[str, Type[Enum]]) -> Type[Enum]: + def ensure_common_base(enum_types: Iterable[Type[Enum]]) -> None: """ Return common base class of given enum types. Raise exception when their bases are not exactly the same. """ - enum_bases = [enum.__base__ for enum in enum_types.values()] + enum_bases = [enum.__base__ for enum in enum_types] if not enum_bases[:-1] == enum_bases[1:]: raise RuntimeError("All enums must have the same base.") - return enum_bases[0] if enum_bases else Enum - - -class EnumGroup(metaclass=EnumGroupMetaclass): - """ - Base class for creating enum groups. - - Intended use is to place enum definitions inside a subclass: - ``` - class CustomEnumGroup(EnumGroup): - class EnumA(Enum): - ASD = "asd" - QWE = "qwe" - - class EnumB(Enum): - Z = "z" - X = "x" - ``` - Child class automatically provides following class attributes: - - `_compound_` - dynamically created enum containing elements of - all grouped enums. - - `_enums_` - dictionary mapping enum types to their names. - - Grouped enums do not need to directly inherit from Enum. If subclass - of enum is used, the composite will also be based on the same class. - - Grouped enum types must follow those rules: - - All passed enum types must inherit directly from the same class. - - Enums cannot define the same names. - - Breaking those rules will result in exception during class creation. - """ From 5ca90d9aa8c5be6b40122309ab5192cff82b5e56 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 05/69] Rewrite EnumGroup as a usual enum with group separators --- .../api_krita/enums/helpers/enum_group.py | 159 ++++++++---------- 1 file changed, 72 insertions(+), 87 deletions(-) diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py index cfaa3c34..95365cb0 100644 --- a/shortcut_composer/api_krita/enums/helpers/enum_group.py +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -1,10 +1,11 @@ -from typing import Any, Dict, Set, Iterable, Tuple, Type, Generic, TypeVar -from enum import Enum +from typing import Dict, List, Tuple, TypeVar, Callable +from enum import Enum, EnumMeta T = TypeVar("T", bound=Enum) -class EnumGroupMeta(type, Generic[T]): +class EnumGroupMeta(EnumMeta): """ + TODO: rewrite documentation Metaclass for creating enum groups. Intended use is to place enum definitions inside a class definition: @@ -46,89 +47,73 @@ class EnumB(Enum): enum types should also be passed to the metaclass. """ - def __init__( - self, + _groups_: Dict[str, 'Group'] + + def __new__( + cls, name: str, bases: Tuple[type, ...], - attrs: Dict[str, Any] - ) -> None: - super().__init__(name, bases, attrs) - - self._grouped_enums_ = MetaInit.get_enum_types(attrs) - """Dictionary mapping grouped enum types to their names.""" - MetaInit.ensure_common_base(self._grouped_enums_.values()) - - self._member_map_ = MetaInit.init_member_map(self._grouped_enums_) - """Dictionary mapping enum members to their names.""" - - self._value2member_map_ = MetaInit.init_reverse_map(self._member_map_) - """Dictionary mapping enum members to their values.""" - - def __getattr__(self, name: str) -> T: - """Return enum member as attribute of the composite.""" - return self._member_map_[name] - - def __getitem__(self, name: str) -> T: - """Return enum member by its name.""" - return self._member_map_[name] - - def __call__(self, value: str) -> T: - """Return enum member by its value.""" - return self._value2member_map_[value] - - -class MetaInit: - """Organizes methods used by EnumGroupMeta.""" - - @staticmethod - def get_enum_types(attrs: Dict[str, Any]) -> Dict[str, Type[Enum]]: - """Filter attribute dict leaving only enum types.""" - def is_enum_type(value: Any) -> bool: - """Return True when value is a class and inherits Enum.""" - return isinstance(value, type) and issubclass(value, Enum) - - return {name: val for name, val in attrs.items() if is_enum_type(val)} - - @classmethod - def init_member_map(cls, enum_types: Dict[str, Type[Enum]]): - """Return dict mapping enum members to their names.""" - # maps enum values to their names from all the passed types - member_map = {} - for enum in enum_types.values(): - - # raise an error when two enum types define the same name - if common := cls.common_keys(member_map, enum._member_map_): - raise RuntimeError(f"Enums have repeated keys: {common}") - - # update dictionary - args = {name: val for name, val in enum._member_map_.items()} - member_map.update(args) - - return member_map - - @staticmethod - def init_reverse_map(member_map: dict): - """Return dict mapping enum members to their values.""" - reverse_map = {} - for value in member_map.values(): - if value not in reverse_map.values(): - reverse_map[value.value] = value - return reverse_map - - @staticmethod - def common_keys(dict_1: dict, dict_2: dict) -> Set: - """Return the set of common keys of two dictionaries.""" - keys_1 = set(dict_1.keys()) - keys_2 = set(dict_2.keys()) - return keys_1.intersection(keys_2) - - @staticmethod - def ensure_common_base(enum_types: Iterable[Type[Enum]]) -> None: - """ - Return common base class of given enum types. - - Raise exception when their bases are not exactly the same. - """ - enum_bases = [enum.__base__ for enum in enum_types] - if not enum_bases[:-1] == enum_bases[1:]: - raise RuntimeError("All enums must have the same base.") + attrs + ) -> 'EnumGroupMeta': + active_separator = None + groups: Dict[str, Group] = {} + for key, value in attrs.copy().items(): + if isinstance(value, Group): + attrs._member_names.remove(key) + active_separator = value + groups[active_separator.name] = active_separator + elif not isinstance(value, Callable): + if active_separator is not None: + active_separator.keys.add(key) + + new_class = super().__new__(cls, name, bases, attrs) + new_class._groups_ = groups + for group in groups.values(): + setattr(new_class, group.name, group) + return new_class + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + for group in self._groups_.values(): + for key in group.keys: + group.append(self[key]) # type: ignore + + +class Group(List[Enum]): + def __init__(self, name: str) -> None: + self.name = name + self.keys = set() + + +class EnumGroup(Enum, metaclass=EnumGroupMeta): + pass + + +# class MyEnum(EnumGroup): +# _fruit = Group("Fruit") +# QWE = 0 +# ASD = 1 + +# _vegetable = Group("Vegetable") +# Z = 2 + +# _other = Group("Other") +# X = 3 + +# def foo(self): +# return f"{self.name}_{self.value}" + + +# print(MyEnum.QWE) +# print(MyEnum.QWE.foo()) +# print(MyEnum["QWE"]) +# print(MyEnum(0)) +# print(MyEnum._fruit) +# print(MyEnum._vegetable) +# print(MyEnum._other) +# print(MyEnum._member_map_) +# print(MyEnum._value2member_map_) +# print(MyEnum.QWE in MyEnum._fruit) # type: ignore +# print() +# for name, group in MyEnum._groups_.items(): +# print(name, group) From 7bf44a6b9c871edd399d1f1c8251210c3c44dd87 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 06/69] Improve code style of EnumGroup implementation --- .../api_krita/enums/helpers/enum_group.py | 174 +++++++++--------- 1 file changed, 91 insertions(+), 83 deletions(-) diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py index 95365cb0..de76c257 100644 --- a/shortcut_composer/api_krita/enums/helpers/enum_group.py +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -1,53 +1,13 @@ -from typing import Dict, List, Tuple, TypeVar, Callable +from typing import Dict, List, Tuple, TypeVar, Optional from enum import Enum, EnumMeta T = TypeVar("T", bound=Enum) class EnumGroupMeta(EnumMeta): - """ - TODO: rewrite documentation - Metaclass for creating enum groups. - - Intended use is to place enum definitions inside a class definition: - ``` - class EnumGroup(metaclass=EnumGroupMeta[Enum]): - class EnumA(Enum): - ASD = "asd" - QWE = "qwe" - - class EnumB(Enum): - Z = "z" - X = "x" - ``` - Metaclass makes its instance act as a composite of all the grouped - enums. Although the instance technically is not an Enum subclass, - it provides the following Enum class attributes: - - `_member_map_` - - `_value2member_map_` - - Enum members can be fetched in multiple ways: - - Using the original, grouped Enum class: - - EnumGroup.EnumA.ASD - - EnumGroup.EnumA["ASD"] - - EnumGroup.EnumA("asd") - - - Using the composite class directly: - - EnumGroup.ASD - - EnumGroup["ASD"] - - EnumGroup("asd") - - Grouped enum types must follow those rules: - - All passed enum types must inherit directly from the same Enum. - - Grouped enums cannot define the same names. - - Breaking those rules will result in exception during class creation. - - Grouped enums do not need to directly inherit from Enum. - Metaclass is a Generic - for typing purposes, base class of grouped - enum types should also be passed to the metaclass. - """ + """Metaclass for creating enum groups. See EnumGroup documentation.""" _groups_: Dict[str, 'Group'] + """Maps enum groups to their pretty names.""" def __new__( cls, @@ -55,65 +15,113 @@ def __new__( bases: Tuple[type, ...], attrs ) -> 'EnumGroupMeta': - active_separator = None - groups: Dict[str, Group] = {} - for key, value in attrs.copy().items(): + # Filter out class attributes provided by Python. + items: List[Tuple[str, Group]] + items = [i for i in attrs.items() if not i[0].startswith("__")] + + # Add keys (which will become enum members) to correct groups + current_group: Optional[Group] = None + for key, value in items: if isinstance(value, Group): - attrs._member_names.remove(key) - active_separator = value - groups[active_separator.name] = active_separator - elif not isinstance(value, Callable): - if active_separator is not None: - active_separator.keys.add(key) + current_group = value + + elif isinstance(value, (int, str)): + if current_group is None: + raise RuntimeError("Enum defined before first group") + current_group.keys.append(key) + # Remove groups from attrs, so they won't become Enum members + group_var_names = [k for k, v in items if isinstance(v, Group)] + for group_variable_name in group_var_names: + attrs._member_names.remove(group_variable_name) + + # Create Enum class. attrs emtpies itself in a process new_class = super().__new__(cls, name, bases, attrs) - new_class._groups_ = groups - for group in groups.values(): - setattr(new_class, group.name, group) - return new_class + # List of all groups + group_list = [v for _, v in items if isinstance(v, Group)] - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - for group in self._groups_.values(): + # Replace keys with their Enum objects, as they exist now + for group in group_list: for key in group.keys: - group.append(self[key]) # type: ignore + group.append(new_class[key]) # type: ignore + + # Store dict mapping groups to their pretty names to use by user + new_class._groups_ = {group.name: group for group in group_list} + + # Store groups in their respective fields + for group in group_list: + setattr(new_class, group.name, group) + + return new_class class Group(List[Enum]): + """List of enum members belonging to one Enum.""" + def __init__(self, name: str) -> None: self.name = name - self.keys = set() + self.keys = [] class EnumGroup(Enum, metaclass=EnumGroupMeta): - pass + """ + Base class for `Enums` with specified groups. + + Groups are defined by placing separators between the members. Groups + are lists that will be filled with with the members belonging to it: + ```python + class Edible(EnumGroup): + _fruit = Group("Fruit") + APPLE = 0 + ORANGE = 1 + + _vegetable = Group("Vegetable") + TOMATO = 2 + POTATO = 3 + + def format_member(self): + return f"{self.name}_{self.value}" + ``` + + Groups can be obtained as attributes, or with `_groups_` dictionary: + + ```python + assert Edible.APPLE in Edible._fruit + assert Edible.TOMATO in Edible._groups_["Vegetable"] + ``` + + Groups are class attributes, but they are not Enum members - they + will not be part of `_member_map_`. + + Every `Enum` member belongs to exactly one group. Every subclass must + start with a group separator. Otherwise, exception will be raised + during class creation. + """ -# class MyEnum(EnumGroup): -# _fruit = Group("Fruit") -# QWE = 0 -# ASD = 1 -# _vegetable = Group("Vegetable") -# Z = 2 +class Edible(EnumGroup): + _fruit = Group("Fruit") + APPLE = 0 + ORANGE = 1 -# _other = Group("Other") -# X = 3 + _vegetable = Group("Vegetable") + TOMATO = 2 + POTATO = 3 -# def foo(self): -# return f"{self.name}_{self.value}" + def format_member(self): + return f"{self.name}_{self.value}" -# print(MyEnum.QWE) -# print(MyEnum.QWE.foo()) -# print(MyEnum["QWE"]) -# print(MyEnum(0)) -# print(MyEnum._fruit) -# print(MyEnum._vegetable) -# print(MyEnum._other) -# print(MyEnum._member_map_) -# print(MyEnum._value2member_map_) -# print(MyEnum.QWE in MyEnum._fruit) # type: ignore +# print(Edible.APPLE) +# print(Edible.APPLE.format_member()) +# print(Edible["APPLE"]) +# print(Edible(0)) +# print(Edible._fruit) +# print(Edible._vegetable) +# print(Edible._member_map_) +# print(Edible._value2member_map_) +# print(Edible.APPLE in Edible._fruit) # type: ignore # print() -# for name, group in MyEnum._groups_.items(): +# for name, group in Edible._groups_.items(): # print(name, group) From 5fe0a3390a8983fb8704f33a49e79eff73c2725d Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 07/69] Implementation of EnumGroup pie settings with no inheritance --- .../api_krita/enums/helpers/__init__.py | 4 +- shortcut_composer/templates/pie_menu.py | 7 +- .../pie_menu_utils/settings_gui/__init__.py | 2 + .../settings_gui/enum_group_pie_settings.py | 110 ++++++++++++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py diff --git a/shortcut_composer/api_krita/enums/helpers/__init__.py b/shortcut_composer/api_krita/enums/helpers/__init__.py index 02be4e1a..2737fad8 100644 --- a/shortcut_composer/api_krita/enums/helpers/__init__.py +++ b/shortcut_composer/api_krita/enums/helpers/__init__.py @@ -1,3 +1,3 @@ -from .enum_group import EnumGroup +from .enum_group import EnumGroup, Group -__all__ = ["EnumGroup"] +__all__ = ["EnumGroup", "Group"] diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index c2942a62..b01d2704 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -9,12 +9,14 @@ from PyQt5.QtGui import QColor from api_krita import Krita +from api_krita.enums.helpers import EnumGroup from core_components import Controller, Instruction from .pie_menu_utils.settings_gui import ( PieSettings, NumericPieSettings, PresetPieSettings, - EnumPieSettings) + EnumPieSettings, + EnumGroupPieSettings) from .pie_menu_utils import ( NonPresetPieConfig, PresetPieConfig, @@ -122,6 +124,9 @@ def pie_settings(self) -> PieSettings: return PresetPieSettings(self._config, self._style) # type: ignore elif issubclass(self._controller.TYPE, float): return NumericPieSettings(self._config, self._style) + elif issubclass(self._controller.TYPE, EnumGroup): + return EnumGroupPieSettings( + self._controller, self._config, self._style) # type: ignore elif issubclass(self._controller.TYPE, Enum): return EnumPieSettings( self._controller, self._config, self._style) # type: ignore diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py index d6677014..5099237a 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py @@ -3,12 +3,14 @@ from .pie_settings import PieSettings from .enum_pie_settings import EnumPieSettings +from .enum_group_pie_settings import EnumGroupPieSettings from .preset_pie_settings import PresetPieSettings from .numeric_pie_settings import NumericPieSettings __all__ = [ "PieSettings", "EnumPieSettings", + "EnumGroupPieSettings", "PresetPieSettings", "NumericPieSettings" ] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py new file mode 100644 index 00000000..7d1070d7 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import List, Optional, Type +from enum import Enum + +from PyQt5.QtWidgets import QWidget + +from api_krita.enums.helpers import EnumGroup +from core_components import Controller +from config_system import Field +from config_system.ui import ConfigComboBox +from ..label import Label +from ..pie_style import PieStyle +from ..pie_config import NonPresetPieConfig +from .pie_settings import PieSettings +from .scroll_area import ScrollArea + + +class GroupComboBox(ConfigComboBox): + def __init__( + self, + config_field: Field[str], + enum_type: Type[EnumGroup], + parent: Optional[QWidget] = None, + pretty_name: Optional[str] = None, + additional_fields: List[str] = [], + ) -> None: + self._additional_fields = additional_fields + self._enum_type = enum_type + super().__init__(config_field, parent, pretty_name) + self.config_field.register_callback( + lambda: self.set(self.config_field.read())) + + def reset(self) -> None: + """Replace list of available tags with those red from database.""" + self._combo_box.clear() + self._combo_box.addItems(self._additional_fields) + self._combo_box.addItems(self._enum_type._groups_.keys()) + self.set(self.config_field.read()) + + +class GroupScrollArea(ScrollArea): + def __init__( + self, + controller: Controller[EnumGroup], + style: PieStyle, + columns: int, + field: Field, + parent=None + ) -> None: + super().__init__(style, columns, parent) + self._controller = controller + self._field = field + self.group_chooser = GroupComboBox( + self._field, + enum_type=self._controller.TYPE, + additional_fields=["All"]) + self.group_chooser.widget.currentTextChanged.connect( + self._display_group) + self._layout.insertWidget(0, self.group_chooser.widget) + self._display_group() + + def _display_group(self) -> None: + """Update preset widgets according to tag selected in combobox.""" + picked_group = self.group_chooser.widget.currentText() + if picked_group == "All": + values = list(self._controller.TYPE._member_map_.values()) + else: + values = self._controller.TYPE._groups_[picked_group] + + self.replace_handled_labels(self._create_labels(values)) + self._apply_search_bar_filter() + self.group_chooser.save() + + def _create_labels(self, values: List[Enum]) -> List[Label[Enum]]: + """Create labels from list of preset names.""" + labels = [Label.from_value(v, self._controller) for v in values] + return [label for label in labels if label is not None] + + +class EnumGroupPieSettings(PieSettings): + def __init__( + self, + controller: Controller[EnumGroup], + config: NonPresetPieConfig, + style: PieStyle, + ) -> None: + super().__init__(config, style) + + names = controller.TYPE._member_names_ + values = [controller.TYPE[name] for name in names] + labels = [Label.from_value(value, controller) for value in values] + labels = [label for label in labels if label is not None] + + self._action_values = GroupScrollArea( + controller=controller, + style=self._style, + columns=3, + field=self._config.field("Last tag selected", "All")) + self._action_values.replace_handled_labels(labels) + self._tab_holder.insertTab(1, self._action_values, "Values") + self._tab_holder.setCurrentIndex(1) + + self._config.ORDER.register_callback(self._refresh_draggable) + self._refresh_draggable() + + def _refresh_draggable(self) -> None: + """Make all values currently used in pie undraggable and disabled.""" + self._action_values.mark_used_values(self._config.values()) From 6edd4eeb6583dc7dca1636c75a9c6acce4a8d9d7 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 08/69] Partial refactor to combine scroll areas of enums and presets --- .../settings_gui/components/__init__.py | 9 +++ .../components/group_combo_box.py | 40 +++++++++++++ .../settings_gui/components/group_fetcher.py | 22 +++++++ .../settings_gui/enum_group_pie_settings.py | 32 ++-------- .../settings_gui/preset_pie_settings.py | 60 ++++++------------- 5 files changed, 93 insertions(+), 70 deletions(-) create mode 100644 shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py create mode 100644 shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py create mode 100644 shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py new file mode 100644 index 00000000..16906d33 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py @@ -0,0 +1,9 @@ +from .group_combo_box import GroupComboBox +from .group_fetcher import GroupFetcher, EnumGroupFetcher, PresetGroupFetcher + +__all__ = [ + "GroupComboBox", + "GroupFetcher", + "EnumGroupFetcher", + "PresetGroupFetcher" +] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py new file mode 100644 index 00000000..d6ef8097 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import List, Optional + +from PyQt5.QtWidgets import QWidget + +from config_system import Field +from config_system.ui import ConfigComboBox +from .group_fetcher import GroupFetcher + + +class GroupComboBox(ConfigComboBox): + """ + Combobox for picking preset tags, which can be saved in config. + + When `allow_all` flag is True, the combobox will contain "All" item + will be added above the actual tags. + """ + + def __init__( + self, + config_field: Field[str], + group_fetcher: GroupFetcher, + parent: Optional[QWidget] = None, + pretty_name: Optional[str] = None, + additional_fields: List[str] = [], + ) -> None: + self._additional_fields = additional_fields + self._group_fetcher = group_fetcher + super().__init__(config_field, parent, pretty_name) + self.config_field.register_callback( + lambda: self.set(self.config_field.read())) + + def reset(self) -> None: + """Replace list of available tags with those red from database.""" + self._combo_box.clear() + self._combo_box.addItems(self._additional_fields) + self._combo_box.addItems(self._group_fetcher.fetch_groups()) + self.set(self.config_field.read()) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py new file mode 100644 index 00000000..c0bce56d --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py @@ -0,0 +1,22 @@ +from typing import List, Type, Protocol + +from api_krita.wrappers import Database +from api_krita.enums.helpers import EnumGroup + + +class GroupFetcher(Protocol): + def fetch_groups(self) -> List[str]: ... + + +class EnumGroupFetcher: + def __init__(self, enum_type: Type[EnumGroup]) -> None: + self._enum_type = enum_type + + def fetch_groups(self) -> List[str]: + return list(self._enum_type._groups_.keys()) + + +class PresetGroupFetcher: + def fetch_groups(self) -> List[str]: + with Database() as database: + return database.get_brush_tags() diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index 7d1070d7..cd97c113 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -1,43 +1,19 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Optional, Type +from typing import List from enum import Enum -from PyQt5.QtWidgets import QWidget from api_krita.enums.helpers import EnumGroup from core_components import Controller from config_system import Field -from config_system.ui import ConfigComboBox from ..label import Label from ..pie_style import PieStyle from ..pie_config import NonPresetPieConfig from .pie_settings import PieSettings from .scroll_area import ScrollArea - - -class GroupComboBox(ConfigComboBox): - def __init__( - self, - config_field: Field[str], - enum_type: Type[EnumGroup], - parent: Optional[QWidget] = None, - pretty_name: Optional[str] = None, - additional_fields: List[str] = [], - ) -> None: - self._additional_fields = additional_fields - self._enum_type = enum_type - super().__init__(config_field, parent, pretty_name) - self.config_field.register_callback( - lambda: self.set(self.config_field.read())) - - def reset(self) -> None: - """Replace list of available tags with those red from database.""" - self._combo_box.clear() - self._combo_box.addItems(self._additional_fields) - self._combo_box.addItems(self._enum_type._groups_.keys()) - self.set(self.config_field.read()) +from .components import GroupComboBox, EnumGroupFetcher class GroupScrollArea(ScrollArea): @@ -53,8 +29,8 @@ def __init__( self._controller = controller self._field = field self.group_chooser = GroupComboBox( - self._field, - enum_type=self._controller.TYPE, + config_field=self._field, + group_fetcher=EnumGroupFetcher(self._controller.TYPE), additional_fields=["All"]) self.group_chooser.widget.currentTextChanged.connect( self._display_group) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index 8f91e768..ec1f453a 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -6,46 +6,16 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout from config_system import Field -from config_system.ui import ConfigComboBox from core_components.controllers import PresetController from data_components import Tag from api_krita import Krita -from api_krita.wrappers import Database from api_krita.pyqt import SafeConfirmButton from ..label import Label from ..pie_style import PieStyle from ..pie_config import PresetPieConfig from .pie_settings import PieSettings from .scroll_area import ScrollArea - - -class TagComboBox(ConfigComboBox): - """ - Combobox for picking preset tags, which can be saved in config. - - When `allow_all` flag is True, the combobox will contain "All" item - will be added above the actual tags. - """ - - def __init__( - self, - config_field: Field[str], - parent: Optional[QWidget] = None, - pretty_name: Optional[str] = None, - additional_fields: List[str] = [], - ) -> None: - self._additional_fields = additional_fields - super().__init__(config_field, parent, pretty_name) - self.config_field.register_callback( - lambda: self.set(self.config_field.read())) - - def reset(self) -> None: - """Replace list of available tags with those red from database.""" - self._combo_box.clear() - self._combo_box.addItems(self._additional_fields) - with Database() as database: - self._combo_box.addItems(database.get_brush_tags()) - self.set(self.config_field.read()) +from .components import GroupComboBox, PresetGroupFetcher class PresetScrollArea(ScrollArea): @@ -71,16 +41,18 @@ def __init__( ) -> None: super().__init__(style, columns, parent) self._field = field - self.tag_chooser = TagComboBox( - self._field, + self.group_chooser = GroupComboBox( + config_field=self._field, + group_fetcher=PresetGroupFetcher(), additional_fields=["---Select tag---", "All"]) - self.tag_chooser.widget.currentTextChanged.connect(self._display_tag) - self._layout.insertWidget(0, self.tag_chooser.widget) - self._display_tag() + self.group_chooser.widget.currentTextChanged.connect( + self._display_group) + self._layout.insertWidget(0, self.group_chooser.widget) + self._display_group() - def _display_tag(self) -> None: + def _display_group(self) -> None: """Update preset widgets according to tag selected in combobox.""" - picked_tag = self.tag_chooser.widget.currentText() + picked_tag = self.group_chooser.widget.currentText() if picked_tag == "All": presets = Krita.get_presets().keys() else: @@ -88,7 +60,7 @@ def _display_tag(self) -> None: self.replace_handled_labels(self._create_labels(presets)) self._apply_search_bar_filter() - self.tag_chooser.save() + self.group_chooser.save() def _create_labels(self, values: Iterable[str]) -> List[Label[str]]: """Create labels from list of preset names.""" @@ -120,7 +92,7 @@ def __init__( self._preset_scroll_area = self._init_preset_scroll_area() self._mode_button = self._init_mode_button() self._auto_combobox = self._init_auto_combobox() - self._manual_combobox = self._preset_scroll_area.tag_chooser + self._manual_combobox = self._preset_scroll_area.group_chooser self.set_tag_mode(self._config.TAG_MODE.read()) action_values = self._init_action_values() @@ -170,14 +142,18 @@ def switch_mode(): lambda: self.set_tag_mode(self._config.TAG_MODE.read(), False)) return mode_button - def _init_auto_combobox(self) -> TagComboBox: + def _init_auto_combobox(self) -> GroupComboBox: """Create tag modecombobox, which sets tag presets to the pie.""" def handle_picked_tag(): """Save used tag in config and report the values changed.""" auto_combobox.save() self._config.refresh_order() - auto_combobox = TagComboBox(self._config.TAG_NAME, self, "Tag name") + auto_combobox = GroupComboBox( + config_field=self._config.TAG_NAME, + group_fetcher=PresetGroupFetcher(), + pretty_name="Tag name") + auto_combobox.widget.currentTextChanged.connect(handle_picked_tag) return auto_combobox From bd0e9314847ee927d7d87a665ef678228fd405bf Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 09/69] WIP: ongoing refactor to make GroupScrollArea a common class --- .../settings_gui/components/group_fetcher.py | 59 ++++++++++++++++--- .../settings_gui/enum_group_pie_settings.py | 30 ++++------ .../settings_gui/preset_pie_settings.py | 33 +++-------- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py index c0bce56d..6b0b205a 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py @@ -1,22 +1,67 @@ -from typing import List, Type, Protocol +from typing import List, Dict, Protocol, Union, Iterable, Optional +from enum import Enum from api_krita.wrappers import Database -from api_krita.enums.helpers import EnumGroup +from core_components import Controller +from core_components.controllers import PresetController + +from data_components import Tag +from api_krita import Krita + +from ...label import Label class GroupFetcher(Protocol): - def fetch_groups(self) -> List[str]: ... + def fetch_groups(self) -> list: ... + def get_values(self, group: str) -> list: ... + def create_labels(self, values: List[Enum]) -> List[Label]: ... -class EnumGroupFetcher: - def __init__(self, enum_type: Type[EnumGroup]) -> None: - self._enum_type = enum_type +class EnumGroupFetcher(GroupFetcher): + def __init__(self, controller: Controller) -> None: + self._controller = controller + self._enum_type = self._controller.TYPE def fetch_groups(self) -> List[str]: return list(self._enum_type._groups_.keys()) + def get_values(self, group: str) -> List[Enum]: + if group == "All": + return list(self._enum_type._member_map_.values()) + return self._enum_type._groups_[group] + + def create_labels(self, values: List[Enum]) -> List[Label[Enum]]: + """Create labels from list of preset names.""" + labels = [Label.from_value(v, self._controller) for v in values] + return [label for label in labels if label is not None] + + +class PresetGroupFetcher(GroupFetcher): + + known_labels: Dict[str, Union[Label, None]] = {} + + def __init__(self) -> None: + self._controller = PresetController() -class PresetGroupFetcher: def fetch_groups(self) -> List[str]: with Database() as database: return database.get_brush_tags() + + def get_values(self, group: str) -> List[str]: + if group == "All": + return list(Krita.get_presets().keys()) + return Tag(group) + + def create_labels(self, values: Iterable[str]) -> List[Label[str]]: + """Create labels from list of preset names.""" + labels: list[Optional[Label]] = [] + + for preset in values: + if preset in self.known_labels: + label = self.known_labels[preset] + else: + label = Label.from_value(preset, self._controller) + self.known_labels[preset] = label + labels.append(label) + + return [label for label in labels if label is not None] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index cd97c113..4cf5b7cf 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -1,10 +1,6 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List -from enum import Enum - - from api_krita.enums.helpers import EnumGroup from core_components import Controller from config_system import Field @@ -13,13 +9,14 @@ from ..pie_config import NonPresetPieConfig from .pie_settings import PieSettings from .scroll_area import ScrollArea -from .components import GroupComboBox, EnumGroupFetcher +from .components import GroupComboBox, EnumGroupFetcher, GroupFetcher class GroupScrollArea(ScrollArea): def __init__( self, controller: Controller[EnumGroup], + fetcher: GroupFetcher, style: PieStyle, columns: int, field: Field, @@ -28,9 +25,10 @@ def __init__( super().__init__(style, columns, parent) self._controller = controller self._field = field + self._fetcher = fetcher self.group_chooser = GroupComboBox( config_field=self._field, - group_fetcher=EnumGroupFetcher(self._controller.TYPE), + group_fetcher=self._fetcher, additional_fields=["All"]) self.group_chooser.widget.currentTextChanged.connect( self._display_group) @@ -40,20 +38,11 @@ def __init__( def _display_group(self) -> None: """Update preset widgets according to tag selected in combobox.""" picked_group = self.group_chooser.widget.currentText() - if picked_group == "All": - values = list(self._controller.TYPE._member_map_.values()) - else: - values = self._controller.TYPE._groups_[picked_group] - - self.replace_handled_labels(self._create_labels(values)) + values = self._fetcher.get_values(picked_group) + self.replace_handled_labels(self._fetcher.create_labels(values)) self._apply_search_bar_filter() self.group_chooser.save() - def _create_labels(self, values: List[Enum]) -> List[Label[Enum]]: - """Create labels from list of preset names.""" - labels = [Label.from_value(v, self._controller) for v in values] - return [label for label in labels if label is not None] - class EnumGroupPieSettings(PieSettings): def __init__( @@ -64,6 +53,7 @@ def __init__( ) -> None: super().__init__(config, style) + self._controller = controller names = controller.TYPE._member_names_ values = [controller.TYPE[name] for name in names] labels = [Label.from_value(value, controller) for value in values] @@ -71,6 +61,7 @@ def __init__( self._action_values = GroupScrollArea( controller=controller, + fetcher=EnumGroupFetcher(self._controller), style=self._style, columns=3, field=self._config.field("Last tag selected", "All")) @@ -84,3 +75,8 @@ def __init__( def _refresh_draggable(self) -> None: """Make all values currently used in pie undraggable and disabled.""" self._action_values.mark_used_values(self._config.values()) + + # def create_labels(self, values: List[Enum]) -> List[Label[Enum]]: + # """Create labels from list of preset names.""" + # labels = [Label.from_value(v, self._controller) for v in values] + # return [label for label in labels if label is not None] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index ec1f453a..e6fa5d30 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -1,13 +1,12 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Dict, Union, Optional, Iterable +from typing import Dict, Union from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout from config_system import Field from core_components.controllers import PresetController -from data_components import Tag from api_krita import Krita from api_krita.pyqt import SafeConfirmButton from ..label import Label @@ -41,9 +40,10 @@ def __init__( ) -> None: super().__init__(style, columns, parent) self._field = field + self._fetcher = PresetGroupFetcher() self.group_chooser = GroupComboBox( config_field=self._field, - group_fetcher=PresetGroupFetcher(), + group_fetcher=self._fetcher, additional_fields=["---Select tag---", "All"]) self.group_chooser.widget.currentTextChanged.connect( self._display_group) @@ -53,30 +53,11 @@ def __init__( def _display_group(self) -> None: """Update preset widgets according to tag selected in combobox.""" picked_tag = self.group_chooser.widget.currentText() - if picked_tag == "All": - presets = Krita.get_presets().keys() - else: - presets = Tag(picked_tag) - - self.replace_handled_labels(self._create_labels(presets)) + values = self._fetcher.get_values(picked_tag) + self.replace_handled_labels(self._fetcher.create_labels(values)) self._apply_search_bar_filter() self.group_chooser.save() - def _create_labels(self, values: Iterable[str]) -> List[Label[str]]: - """Create labels from list of preset names.""" - controller = PresetController() - labels: list[Optional[Label]] = [] - - for preset in values: - if preset in self.known_labels: - label = self.known_labels[preset] - else: - label = Label.from_value(preset, controller) - self.known_labels[preset] = label - labels.append(label) - - return [label for label in labels if label is not None] - class PresetPieSettings(PieSettings): """Pie setting window for pie values being brush presets.""" @@ -89,6 +70,8 @@ def __init__( super().__init__(config, style) self._config: PresetPieConfig + self._fetcher = PresetGroupFetcher() + self._preset_scroll_area = self._init_preset_scroll_area() self._mode_button = self._init_mode_button() self._auto_combobox = self._init_auto_combobox() @@ -151,7 +134,7 @@ def handle_picked_tag(): auto_combobox = GroupComboBox( config_field=self._config.TAG_NAME, - group_fetcher=PresetGroupFetcher(), + group_fetcher=self._fetcher, pretty_name="Tag name") auto_combobox.widget.currentTextChanged.connect(handle_picked_tag) From def979f188d6edfad8d4ef0d8599087e816895e7 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 10/69] Made GroupScrollArea a separate class --- .../settings_gui/components/__init__.py | 4 +- .../components/group_scroll_area.py | 44 ++++++++++++++ .../settings_gui/enum_group_pie_settings.py | 44 +------------- .../settings_gui/preset_pie_settings.py | 58 +++---------------- 4 files changed, 57 insertions(+), 93 deletions(-) create mode 100644 shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py index 16906d33..e64aca07 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py @@ -1,9 +1,11 @@ from .group_combo_box import GroupComboBox from .group_fetcher import GroupFetcher, EnumGroupFetcher, PresetGroupFetcher +from .group_scroll_area import GroupScrollArea __all__ = [ "GroupComboBox", "GroupFetcher", "EnumGroupFetcher", - "PresetGroupFetcher" + "PresetGroupFetcher", + "GroupScrollArea", ] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py new file mode 100644 index 00000000..17b961eb --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import List + +from api_krita.enums.helpers import EnumGroup +from core_components import Controller +from config_system import Field +from ...pie_style import PieStyle +from ..scroll_area import ScrollArea +from ..components import GroupComboBox, GroupFetcher + + +class GroupScrollArea(ScrollArea): + def __init__( + self, + controller: Controller, + fetcher: GroupFetcher, + style: PieStyle, + columns: int, + field: Field, + additional_fields: List[str] = [], + parent=None + ) -> None: + super().__init__(style, columns, parent) + self._controller = controller + self._field = field + self._fetcher = fetcher + self.group_chooser = GroupComboBox( + config_field=self._field, + group_fetcher=self._fetcher, + additional_fields=additional_fields) + self.group_chooser.widget.currentTextChanged.connect( + self._display_group) + self._layout.insertWidget(0, self.group_chooser.widget) + self._display_group() + + def _display_group(self) -> None: + """Update preset widgets according to tag selected in combobox.""" + picked_group = self.group_chooser.widget.currentText() + values = self._fetcher.get_values(picked_group) + self.replace_handled_labels(self._fetcher.create_labels(values)) + self._apply_search_bar_filter() + self.group_chooser.save() diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index 4cf5b7cf..cac30daa 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -3,45 +3,11 @@ from api_krita.enums.helpers import EnumGroup from core_components import Controller -from config_system import Field from ..label import Label from ..pie_style import PieStyle from ..pie_config import NonPresetPieConfig from .pie_settings import PieSettings -from .scroll_area import ScrollArea -from .components import GroupComboBox, EnumGroupFetcher, GroupFetcher - - -class GroupScrollArea(ScrollArea): - def __init__( - self, - controller: Controller[EnumGroup], - fetcher: GroupFetcher, - style: PieStyle, - columns: int, - field: Field, - parent=None - ) -> None: - super().__init__(style, columns, parent) - self._controller = controller - self._field = field - self._fetcher = fetcher - self.group_chooser = GroupComboBox( - config_field=self._field, - group_fetcher=self._fetcher, - additional_fields=["All"]) - self.group_chooser.widget.currentTextChanged.connect( - self._display_group) - self._layout.insertWidget(0, self.group_chooser.widget) - self._display_group() - - def _display_group(self) -> None: - """Update preset widgets according to tag selected in combobox.""" - picked_group = self.group_chooser.widget.currentText() - values = self._fetcher.get_values(picked_group) - self.replace_handled_labels(self._fetcher.create_labels(values)) - self._apply_search_bar_filter() - self.group_chooser.save() +from .components import EnumGroupFetcher, GroupScrollArea class EnumGroupPieSettings(PieSettings): @@ -64,7 +30,8 @@ def __init__( fetcher=EnumGroupFetcher(self._controller), style=self._style, columns=3, - field=self._config.field("Last tag selected", "All")) + field=self._config.field("Last tag selected", "All"), + additional_fields=["All"]) self._action_values.replace_handled_labels(labels) self._tab_holder.insertTab(1, self._action_values, "Values") self._tab_holder.setCurrentIndex(1) @@ -75,8 +42,3 @@ def __init__( def _refresh_draggable(self) -> None: """Make all values currently used in pie undraggable and disabled.""" self._action_values.mark_used_values(self._config.values()) - - # def create_labels(self, values: List[Enum]) -> List[Label[Enum]]: - # """Create labels from list of preset names.""" - # labels = [Label.from_value(v, self._controller) for v in values] - # return [label for label in labels if label is not None] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index e6fa5d30..2c091fb2 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -1,62 +1,15 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Dict, Union - from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout -from config_system import Field from core_components.controllers import PresetController from api_krita import Krita from api_krita.pyqt import SafeConfirmButton -from ..label import Label from ..pie_style import PieStyle from ..pie_config import PresetPieConfig from .pie_settings import PieSettings -from .scroll_area import ScrollArea -from .components import GroupComboBox, PresetGroupFetcher - - -class PresetScrollArea(ScrollArea): - """ - Scroll area for holding preset pies. - - Extends usual scroll area with the combobox over the area for - picking displayed tag. The picked tag is saved to given field. - - Operates in two modes: - - Tag mode - the presets are determined by tracking krita tag - - Manual mode - the presets are manually picked by the user - """ - - known_labels: Dict[str, Union[Label, None]] = {} - - def __init__( - self, - style: PieStyle, - columns: int, - field: Field, - parent=None - ) -> None: - super().__init__(style, columns, parent) - self._field = field - self._fetcher = PresetGroupFetcher() - self.group_chooser = GroupComboBox( - config_field=self._field, - group_fetcher=self._fetcher, - additional_fields=["---Select tag---", "All"]) - self.group_chooser.widget.currentTextChanged.connect( - self._display_group) - self._layout.insertWidget(0, self.group_chooser.widget) - self._display_group() - - def _display_group(self) -> None: - """Update preset widgets according to tag selected in combobox.""" - picked_tag = self.group_chooser.widget.currentText() - values = self._fetcher.get_values(picked_tag) - self.replace_handled_labels(self._fetcher.create_labels(values)) - self._apply_search_bar_filter() - self.group_chooser.save() +from .components import GroupComboBox, PresetGroupFetcher, GroupScrollArea class PresetPieSettings(PieSettings): @@ -82,12 +35,15 @@ def __init__( self._tab_holder.insertTab(1, action_values, "Values") self._tab_holder.setCurrentIndex(1) - def _init_preset_scroll_area(self) -> PresetScrollArea: + def _init_preset_scroll_area(self) -> GroupScrollArea: """Create preset scroll area which tracks which ones are used.""" - preset_scroll_area = PresetScrollArea( + preset_scroll_area = GroupScrollArea( + controller=PresetController(), + fetcher=self._fetcher, style=self._style, columns=3, - field=self._config.field("Last tag selected", "---Select tag---")) + field=self._config.field("Last tag selected", "---Select tag---"), + additional_fields=["---Select tag---", "All"]) policy = preset_scroll_area.sizePolicy() policy.setRetainSizeWhenHidden(True) preset_scroll_area.setSizePolicy(policy) From 90478a329a4e1bed19dd067d90ffb8ca1ad51aad Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 11/69] Simplified init interfaces --- .../components/group_scroll_area.py | 15 +++++---------- .../settings_gui/enum_group_pie_settings.py | 11 +---------- .../settings_gui/preset_pie_settings.py | 18 ++++++++++-------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py index 17b961eb..424e534d 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py @@ -3,8 +3,6 @@ from typing import List -from api_krita.enums.helpers import EnumGroup -from core_components import Controller from config_system import Field from ...pie_style import PieStyle from ..scroll_area import ScrollArea @@ -14,7 +12,6 @@ class GroupScrollArea(ScrollArea): def __init__( self, - controller: Controller, fetcher: GroupFetcher, style: PieStyle, columns: int, @@ -23,22 +20,20 @@ def __init__( parent=None ) -> None: super().__init__(style, columns, parent) - self._controller = controller self._field = field self._fetcher = fetcher - self.group_chooser = GroupComboBox( + self._chooser = GroupComboBox( config_field=self._field, group_fetcher=self._fetcher, additional_fields=additional_fields) - self.group_chooser.widget.currentTextChanged.connect( - self._display_group) - self._layout.insertWidget(0, self.group_chooser.widget) + self._chooser.widget.currentTextChanged.connect(self._display_group) + self._layout.insertWidget(0, self._chooser.widget) self._display_group() def _display_group(self) -> None: """Update preset widgets according to tag selected in combobox.""" - picked_group = self.group_chooser.widget.currentText() + picked_group = self._chooser.widget.currentText() values = self._fetcher.get_values(picked_group) self.replace_handled_labels(self._fetcher.create_labels(values)) self._apply_search_bar_filter() - self.group_chooser.save() + self._chooser.save() diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index cac30daa..27b07277 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -3,7 +3,6 @@ from api_krita.enums.helpers import EnumGroup from core_components import Controller -from ..label import Label from ..pie_style import PieStyle from ..pie_config import NonPresetPieConfig from .pie_settings import PieSettings @@ -19,20 +18,12 @@ def __init__( ) -> None: super().__init__(config, style) - self._controller = controller - names = controller.TYPE._member_names_ - values = [controller.TYPE[name] for name in names] - labels = [Label.from_value(value, controller) for value in values] - labels = [label for label in labels if label is not None] - self._action_values = GroupScrollArea( - controller=controller, - fetcher=EnumGroupFetcher(self._controller), + fetcher=EnumGroupFetcher(controller), style=self._style, columns=3, field=self._config.field("Last tag selected", "All"), additional_fields=["All"]) - self._action_values.replace_handled_labels(labels) self._tab_holder.insertTab(1, self._action_values, "Values") self._tab_holder.setCurrentIndex(1) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index 2c091fb2..81fc9477 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -3,7 +3,6 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout -from core_components.controllers import PresetController from api_krita import Krita from api_krita.pyqt import SafeConfirmButton from ..pie_style import PieStyle @@ -13,7 +12,13 @@ class PresetPieSettings(PieSettings): - """Pie setting window for pie values being brush presets.""" + """ + Pie setting window for pie values being brush presets. + + Its `Values` tab operates in two modes: + - Tag mode - the presets are determined by tracking krita tag + - Manual mode - the presets are manually picked by the user + """ def __init__( self, @@ -23,12 +28,10 @@ def __init__( super().__init__(config, style) self._config: PresetPieConfig - self._fetcher = PresetGroupFetcher() - self._preset_scroll_area = self._init_preset_scroll_area() self._mode_button = self._init_mode_button() self._auto_combobox = self._init_auto_combobox() - self._manual_combobox = self._preset_scroll_area.group_chooser + self._manual_combobox = self._preset_scroll_area._chooser self.set_tag_mode(self._config.TAG_MODE.read()) action_values = self._init_action_values() @@ -38,8 +41,7 @@ def __init__( def _init_preset_scroll_area(self) -> GroupScrollArea: """Create preset scroll area which tracks which ones are used.""" preset_scroll_area = GroupScrollArea( - controller=PresetController(), - fetcher=self._fetcher, + fetcher=PresetGroupFetcher(), style=self._style, columns=3, field=self._config.field("Last tag selected", "---Select tag---"), @@ -90,7 +92,7 @@ def handle_picked_tag(): auto_combobox = GroupComboBox( config_field=self._config.TAG_NAME, - group_fetcher=self._fetcher, + group_fetcher=PresetGroupFetcher(), pretty_name="Tag name") auto_combobox.widget.currentTextChanged.connect(handle_picked_tag) From 39e5f6cbb1e928381cbc0d9c03d428fa257d2c4f Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 12/69] Changed organization of classes in files --- .../settings_gui/components/__init__.py | 6 +- .../components/group_combo_box.py | 6 -- .../settings_gui/components/group_fetcher.py | 61 +------------------ .../components/group_scroll_area.py | 2 +- .../{ => components}/offset_grid_layout.py | 2 +- .../{ => components}/scroll_area.py | 8 +-- .../settings_gui/enum_group_pie_settings.py | 27 +++++++- .../settings_gui/enum_pie_settings.py | 7 +-- .../settings_gui/pie_settings.py | 3 +- .../settings_gui/preset_pie_settings.py | 41 ++++++++++++- 10 files changed, 77 insertions(+), 86 deletions(-) rename shortcut_composer/templates/pie_menu_utils/settings_gui/{ => components}/offset_grid_layout.py (98%) rename shortcut_composer/templates/pie_menu_utils/settings_gui/{ => components}/scroll_area.py (97%) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py index e64aca07..1a97f9b7 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py @@ -1,11 +1,11 @@ from .group_combo_box import GroupComboBox -from .group_fetcher import GroupFetcher, EnumGroupFetcher, PresetGroupFetcher +from .group_fetcher import GroupFetcher from .group_scroll_area import GroupScrollArea +from .scroll_area import ScrollArea __all__ = [ "GroupComboBox", "GroupFetcher", - "EnumGroupFetcher", - "PresetGroupFetcher", "GroupScrollArea", + "ScrollArea", ] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py index d6ef8097..1abb3235 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py @@ -11,12 +11,6 @@ class GroupComboBox(ConfigComboBox): - """ - Combobox for picking preset tags, which can be saved in config. - - When `allow_all` flag is True, the combobox will contain "All" item - will be added above the actual tags. - """ def __init__( self, diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py index 6b0b205a..7cd1158c 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py @@ -1,67 +1,10 @@ -from typing import List, Dict, Protocol, Union, Iterable, Optional +from typing import List, Protocol from enum import Enum -from api_krita.wrappers import Database -from core_components import Controller -from core_components.controllers import PresetController - -from data_components import Tag -from api_krita import Krita - -from ...label import Label +from templates.pie_menu_utils import Label class GroupFetcher(Protocol): def fetch_groups(self) -> list: ... def get_values(self, group: str) -> list: ... def create_labels(self, values: List[Enum]) -> List[Label]: ... - - -class EnumGroupFetcher(GroupFetcher): - def __init__(self, controller: Controller) -> None: - self._controller = controller - self._enum_type = self._controller.TYPE - - def fetch_groups(self) -> List[str]: - return list(self._enum_type._groups_.keys()) - - def get_values(self, group: str) -> List[Enum]: - if group == "All": - return list(self._enum_type._member_map_.values()) - return self._enum_type._groups_[group] - - def create_labels(self, values: List[Enum]) -> List[Label[Enum]]: - """Create labels from list of preset names.""" - labels = [Label.from_value(v, self._controller) for v in values] - return [label for label in labels if label is not None] - - -class PresetGroupFetcher(GroupFetcher): - - known_labels: Dict[str, Union[Label, None]] = {} - - def __init__(self) -> None: - self._controller = PresetController() - - def fetch_groups(self) -> List[str]: - with Database() as database: - return database.get_brush_tags() - - def get_values(self, group: str) -> List[str]: - if group == "All": - return list(Krita.get_presets().keys()) - return Tag(group) - - def create_labels(self, values: Iterable[str]) -> List[Label[str]]: - """Create labels from list of preset names.""" - labels: list[Optional[Label]] = [] - - for preset in values: - if preset in self.known_labels: - label = self.known_labels[preset] - else: - label = Label.from_value(preset, self._controller) - self.known_labels[preset] = label - labels.append(label) - - return [label for label in labels if label is not None] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py index 424e534d..4d85fcf0 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py @@ -5,7 +5,7 @@ from config_system import Field from ...pie_style import PieStyle -from ..scroll_area import ScrollArea +from .scroll_area import ScrollArea from ..components import GroupComboBox, GroupFetcher diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/offset_grid_layout.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py similarity index 98% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/offset_grid_layout.py rename to shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py index 9680dc90..ce219595 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/offset_grid_layout.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py @@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QGridLayout -from ..label_widget import LabelWidget +from ...label_widget import LabelWidget class GridPosition(NamedTuple): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py similarity index 97% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py rename to shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py index e5e08d1e..730b668a 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py @@ -15,10 +15,10 @@ QHBoxLayout, QSizePolicy) -from ..label import Label -from ..label_widget import LabelWidget -from ..label_widget_utils import create_label_widget -from ..pie_style import PieStyle +from ...label import Label +from ...label_widget import LabelWidget +from ...label_widget_utils import create_label_widget +from ...pie_style import PieStyle from .offset_grid_layout import OffsetGridLayout diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index 27b07277..20892c20 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -1,12 +1,14 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +from typing import List +from enum import Enum + from api_krita.enums.helpers import EnumGroup from core_components import Controller -from ..pie_style import PieStyle -from ..pie_config import NonPresetPieConfig +from templates.pie_menu_utils import Label, PieStyle, NonPresetPieConfig from .pie_settings import PieSettings -from .components import EnumGroupFetcher, GroupScrollArea +from .components import GroupScrollArea, GroupFetcher class EnumGroupPieSettings(PieSettings): @@ -33,3 +35,22 @@ def __init__( def _refresh_draggable(self) -> None: """Make all values currently used in pie undraggable and disabled.""" self._action_values.mark_used_values(self._config.values()) + + +class EnumGroupFetcher(GroupFetcher): + def __init__(self, controller: Controller) -> None: + self._controller = controller + self._enum_type = self._controller.TYPE + + def fetch_groups(self) -> List[str]: + return list(self._enum_type._groups_.keys()) + + def get_values(self, group: str) -> List[Enum]: + if group == "All": + return list(self._enum_type._member_map_.values()) + return self._enum_type._groups_[group] + + def create_labels(self, values: List[Enum]) -> List[Label[Enum]]: + """Create labels from list of preset names.""" + labels = [Label.from_value(v, self._controller) for v in values] + return [label for label in labels if label is not None] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py index 89c0891c..164ffeba 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py @@ -4,11 +4,10 @@ from enum import Enum from core_components import Controller -from ..label import Label -from ..pie_style import PieStyle -from ..pie_config import NonPresetPieConfig +from templates.pie_menu_utils import Label, PieStyle, NonPresetPieConfig + from .pie_settings import PieSettings -from .scroll_area import ScrollArea +from .components import ScrollArea class EnumPieSettings(PieSettings): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py index b210a698..abf00e2c 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py @@ -16,8 +16,7 @@ from api_krita.pyqt import AnimatedWidget, BaseWidget, SafeConfirmButton from config_system.ui import ConfigFormWidget, ConfigSpinBox from composer_utils import Config -from ..pie_style import PieStyle -from ..pie_config import PieConfig +from templates.pie_menu_utils import PieStyle, PieConfig class PieSettings(AnimatedWidget, BaseWidget): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index 81fc9477..7a485a27 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -1,14 +1,18 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +from typing import List, Dict, Union, Iterable, Optional + from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout from api_krita import Krita +from api_krita.wrappers import Database from api_krita.pyqt import SafeConfirmButton -from ..pie_style import PieStyle -from ..pie_config import PresetPieConfig +from core_components.controllers import PresetController +from data_components import Tag +from templates.pie_menu_utils import Label, PieStyle, PresetPieConfig from .pie_settings import PieSettings -from .components import GroupComboBox, PresetGroupFetcher, GroupScrollArea +from .components import GroupComboBox, GroupScrollArea, GroupFetcher class PresetPieSettings(PieSettings): @@ -145,3 +149,34 @@ def set_tag_mode(self, value: bool, notify: bool = True) -> None: self._preset_scroll_area.show() self._manual_combobox.widget.show() self._auto_combobox.widget.hide() + + +class PresetGroupFetcher(GroupFetcher): + + known_labels: Dict[str, Union[Label, None]] = {} + + def __init__(self) -> None: + self._controller = PresetController() + + def fetch_groups(self) -> List[str]: + with Database() as database: + return database.get_brush_tags() + + def get_values(self, group: str) -> List[str]: + if group == "All": + return list(Krita.get_presets().keys()) + return Tag(group) + + def create_labels(self, values: Iterable[str]) -> List[Label[str]]: + """Create labels from list of preset names.""" + labels: list[Optional[Label]] = [] + + for preset in values: + if preset in self.known_labels: + label = self.known_labels[preset] + else: + label = Label.from_value(preset, self._controller) + self.known_labels[preset] = label + labels.append(label) + + return [label for label in labels if label is not None] From 57c81d14fa9ada3d381a1a910b8c22d7201b0134 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 13/69] bugfix: EnumGroup scroll area must update labels on group change --- .../pie_menu_utils/settings_gui/enum_group_pie_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index 20892c20..23258078 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -29,6 +29,7 @@ def __init__( self._tab_holder.insertTab(1, self._action_values, "Values") self._tab_holder.setCurrentIndex(1) + self._action_values.widgets_changed.connect(self._refresh_draggable) self._config.ORDER.register_callback(self._refresh_draggable) self._refresh_draggable() From dab48ea2e759a64b5c47b261dd430d9ac71b0234 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 14/69] Renamed fetcher to manager. Changed import convention --- .../mouse_tracker_utils/axis_trackers.py | 2 +- .../settings_gui/components/__init__.py | 4 +-- .../components/group_combo_box.py | 4 +-- .../{group_fetcher.py => group_manager.py} | 2 +- .../components/group_scroll_area.py | 8 +++-- .../components/offset_grid_layout.py | 2 +- .../settings_gui/components/scroll_area.py | 36 +++++++++---------- .../settings_gui/enum_group_pie_settings.py | 6 ++-- .../settings_gui/preset_pie_settings.py | 8 ++--- 9 files changed, 36 insertions(+), 36 deletions(-) rename shortcut_composer/templates/pie_menu_utils/settings_gui/components/{group_fetcher.py => group_manager.py} (89%) diff --git a/shortcut_composer/templates/mouse_tracker_utils/axis_trackers.py b/shortcut_composer/templates/mouse_tracker_utils/axis_trackers.py index fb6a0f5d..c04fa56d 100644 --- a/shortcut_composer/templates/mouse_tracker_utils/axis_trackers.py +++ b/shortcut_composer/templates/mouse_tracker_utils/axis_trackers.py @@ -6,8 +6,8 @@ from api_krita import Krita from api_krita.pyqt import Timer from core_components import Instruction +from templates.raw_instructions import RawInstructions from .slider_handler import SliderHandler -from ..raw_instructions import RawInstructions class SingleAxisTracker(RawInstructions): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py index 1a97f9b7..8b2b9ed0 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py @@ -1,11 +1,11 @@ from .group_combo_box import GroupComboBox -from .group_fetcher import GroupFetcher +from .group_manager import GroupManager from .group_scroll_area import GroupScrollArea from .scroll_area import ScrollArea __all__ = [ "GroupComboBox", - "GroupFetcher", + "GroupManager", "GroupScrollArea", "ScrollArea", ] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py index 1abb3235..a914d977 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py @@ -7,7 +7,7 @@ from config_system import Field from config_system.ui import ConfigComboBox -from .group_fetcher import GroupFetcher +from .group_manager import GroupManager class GroupComboBox(ConfigComboBox): @@ -15,7 +15,7 @@ class GroupComboBox(ConfigComboBox): def __init__( self, config_field: Field[str], - group_fetcher: GroupFetcher, + group_fetcher: GroupManager, parent: Optional[QWidget] = None, pretty_name: Optional[str] = None, additional_fields: List[str] = [], diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_manager.py similarity index 89% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py rename to shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_manager.py index 7cd1158c..44fb7ba0 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_fetcher.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_manager.py @@ -4,7 +4,7 @@ from templates.pie_menu_utils import Label -class GroupFetcher(Protocol): +class GroupManager(Protocol): def fetch_groups(self) -> list: ... def get_values(self, group: str) -> list: ... def create_labels(self, values: List[Enum]) -> List[Label]: ... diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py index 4d85fcf0..17abc55d 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py @@ -4,15 +4,17 @@ from typing import List from config_system import Field -from ...pie_style import PieStyle +from templates.pie_menu_utils import PieStyle +from templates.pie_menu_utils.settings_gui.components import ( + GroupComboBox, + GroupManager) from .scroll_area import ScrollArea -from ..components import GroupComboBox, GroupFetcher class GroupScrollArea(ScrollArea): def __init__( self, - fetcher: GroupFetcher, + fetcher: GroupManager, style: PieStyle, columns: int, field: Field, diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py index ce219595..a55c834c 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py @@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QGridLayout -from ...label_widget import LabelWidget +from templates.pie_menu_utils import LabelWidget class GridPosition(NamedTuple): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py index 730b668a..ee3833b0 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py @@ -15,28 +15,11 @@ QHBoxLayout, QSizePolicy) -from ...label import Label -from ...label_widget import LabelWidget -from ...label_widget_utils import create_label_widget -from ...pie_style import PieStyle +from templates.pie_menu_utils import Label, LabelWidget, PieStyle +from templates.pie_menu_utils.label_widget_utils import create_label_widget from .offset_grid_layout import OffsetGridLayout -class ChildInstruction: - """Logic of displaying widget text in passed QLabel.""" - - def __init__(self, display_label: QLabel) -> None: - self._display_label = display_label - - def on_enter(self, label: Label) -> None: - """Set text of label which was entered with mouse.""" - self._display_label.setText(str(label.pretty_name)) - - def on_leave(self, label: Label) -> None: - """Reset text after mouse leaves the widget.""" - self._display_label.setText("") - - class EmptySignal(Protocol): """Protocol fixing the wrong PyQt typing.""" @@ -192,3 +175,18 @@ def mark_used_values(self, used_values: list) -> None: else: widget.enabled = True widget.draggable = True + + +class ChildInstruction: + """Logic of displaying widget text in passed QLabel.""" + + def __init__(self, display_label: QLabel) -> None: + self._display_label = display_label + + def on_enter(self, label: Label) -> None: + """Set text of label which was entered with mouse.""" + self._display_label.setText(str(label.pretty_name)) + + def on_leave(self, label: Label) -> None: + """Reset text after mouse leaves the widget.""" + self._display_label.setText("") diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py index 23258078..a3abda38 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py @@ -8,7 +8,7 @@ from core_components import Controller from templates.pie_menu_utils import Label, PieStyle, NonPresetPieConfig from .pie_settings import PieSettings -from .components import GroupScrollArea, GroupFetcher +from .components import GroupScrollArea, GroupManager class EnumGroupPieSettings(PieSettings): @@ -21,7 +21,7 @@ def __init__( super().__init__(config, style) self._action_values = GroupScrollArea( - fetcher=EnumGroupFetcher(controller), + fetcher=EnumGroupManager(controller), style=self._style, columns=3, field=self._config.field("Last tag selected", "All"), @@ -38,7 +38,7 @@ def _refresh_draggable(self) -> None: self._action_values.mark_used_values(self._config.values()) -class EnumGroupFetcher(GroupFetcher): +class EnumGroupManager(GroupManager): def __init__(self, controller: Controller) -> None: self._controller = controller self._enum_type = self._controller.TYPE diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py index 7a485a27..0b607b74 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py @@ -12,7 +12,7 @@ from data_components import Tag from templates.pie_menu_utils import Label, PieStyle, PresetPieConfig from .pie_settings import PieSettings -from .components import GroupComboBox, GroupScrollArea, GroupFetcher +from .components import GroupComboBox, GroupScrollArea, GroupManager class PresetPieSettings(PieSettings): @@ -45,7 +45,7 @@ def __init__( def _init_preset_scroll_area(self) -> GroupScrollArea: """Create preset scroll area which tracks which ones are used.""" preset_scroll_area = GroupScrollArea( - fetcher=PresetGroupFetcher(), + fetcher=PresetGroupManager(), style=self._style, columns=3, field=self._config.field("Last tag selected", "---Select tag---"), @@ -96,7 +96,7 @@ def handle_picked_tag(): auto_combobox = GroupComboBox( config_field=self._config.TAG_NAME, - group_fetcher=PresetGroupFetcher(), + group_fetcher=PresetGroupManager(), pretty_name="Tag name") auto_combobox.widget.currentTextChanged.connect(handle_picked_tag) @@ -151,7 +151,7 @@ def set_tag_mode(self, value: bool, notify: bool = True) -> None: self._auto_combobox.widget.hide() -class PresetGroupFetcher(GroupFetcher): +class PresetGroupManager(GroupManager): known_labels: Dict[str, Union[Label, None]] = {} From a0399a1655d5278b7a6dcada85c15683e86c6ff9 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 15/69] Changed folder organization structure --- shortcut_composer/templates/pie_menu.py | 11 ++++++----- .../templates/pie_menu_utils/__init__.py | 6 ++++++ .../pie_menu_utils/{widget_utils => }/edit_mode.py | 2 +- .../__init__.py | 0 .../create_label_widget.py | 0 .../icon_label_widget.py | 0 .../image_label_widget.py | 0 .../text_label_widget.py | 0 .../pie_menu_utils/{widget_utils => }/pie_button.py | 4 ++-- .../templates/pie_menu_utils/pie_manager.py | 2 +- .../pie_menu_utils/{settings_gui => }/pie_settings.py | 3 ++- .../{settings_gui => pie_settings_impl}/__init__.py | 10 ++++------ .../common_utils}/__init__.py | 0 .../common_utils}/group_combo_box.py | 0 .../common_utils}/group_manager.py | 0 .../common_utils}/group_scroll_area.py | 2 +- .../common_utils}/offset_grid_layout.py | 0 .../common_utils}/scroll_area.py | 2 +- .../enum_group_pie_settings.py | 9 ++++++--- .../enum_pie_settings.py | 10 ++++++---- .../numeric_pie_settings.py | 2 +- .../preset_pie_settings.py | 9 ++++++--- .../templates/pie_menu_utils/pie_widget.py | 2 +- .../{widget_utils => pie_widget_utils}/__init__.py | 6 +----- .../circle_points.py | 0 .../label_holder.py | 2 +- .../{widget_utils => pie_widget_utils}/pie_painter.py | 0 .../widget_holder.py | 0 28 files changed, 46 insertions(+), 36 deletions(-) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => }/edit_mode.py (98%) rename shortcut_composer/templates/pie_menu_utils/{label_widget_utils => label_widget_impl}/__init__.py (100%) rename shortcut_composer/templates/pie_menu_utils/{label_widget_utils => label_widget_impl}/create_label_widget.py (100%) rename shortcut_composer/templates/pie_menu_utils/{label_widget_utils => label_widget_impl}/icon_label_widget.py (100%) rename shortcut_composer/templates/pie_menu_utils/{label_widget_utils => label_widget_impl}/image_label_widget.py (100%) rename shortcut_composer/templates/pie_menu_utils/{label_widget_utils => label_widget_impl}/text_label_widget.py (100%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => }/pie_button.py (94%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => }/pie_settings.py (99%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => pie_settings_impl}/__init__.py (83%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/__init__.py (100%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/group_combo_box.py (100%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/group_manager.py (100%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/group_scroll_area.py (95%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/offset_grid_layout.py (100%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui/components => pie_settings_impl/common_utils}/scroll_area.py (98%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => pie_settings_impl}/enum_group_pie_settings.py (92%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => pie_settings_impl}/enum_pie_settings.py (90%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => pie_settings_impl}/numeric_pie_settings.py (85%) rename shortcut_composer/templates/pie_menu_utils/{settings_gui => pie_settings_impl}/preset_pie_settings.py (97%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => pie_widget_utils}/__init__.py (70%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => pie_widget_utils}/circle_points.py (100%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => pie_widget_utils}/label_holder.py (98%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => pie_widget_utils}/pie_painter.py (100%) rename shortcut_composer/templates/pie_menu_utils/{widget_utils => pie_widget_utils}/widget_holder.py (100%) diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index b01d2704..d9eb0473 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -11,21 +11,22 @@ from api_krita import Krita from api_krita.enums.helpers import EnumGroup from core_components import Controller, Instruction -from .pie_menu_utils.settings_gui import ( - PieSettings, +from .pie_menu_utils.pie_settings_impl import ( + EnumGroupPieSettings, NumericPieSettings, PresetPieSettings, - EnumPieSettings, - EnumGroupPieSettings) + EnumPieSettings) from .pie_menu_utils import ( NonPresetPieConfig, PresetPieConfig, + PieSettings, PieManager, PieConfig, PieWidget, + PieButton, + EditMode, PieStyle, Label) -from .pie_menu_utils.widget_utils import EditMode, PieButton from .raw_instructions import RawInstructions T = TypeVar('T') diff --git a/shortcut_composer/templates/pie_menu_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/__init__.py index 083268a9..7ff8f8f9 100644 --- a/shortcut_composer/templates/pie_menu_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/__init__.py @@ -4,19 +4,25 @@ """Implementation of PieMenu main elements.""" from .pie_config import PieConfig, PresetPieConfig, NonPresetPieConfig +from .pie_settings import PieSettings from .label_widget import LabelWidget from .pie_manager import PieManager from .pie_widget import PieWidget +from .pie_button import PieButton from .pie_style import PieStyle +from .edit_mode import EditMode from .label import Label __all__ = [ "NonPresetPieConfig", "PresetPieConfig", + "PieSettings", "LabelWidget", "PieConfig", "PieManager", "PieWidget", + "PieButton", "PieStyle", + "EditMode", "Label", ] diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/edit_mode.py b/shortcut_composer/templates/pie_menu_utils/edit_mode.py similarity index 98% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/edit_mode.py rename to shortcut_composer/templates/pie_menu_utils/edit_mode.py index c2a0619f..8c625619 100644 --- a/shortcut_composer/templates/pie_menu_utils/widget_utils/edit_mode.py +++ b/shortcut_composer/templates/pie_menu_utils/edit_mode.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import QPoint if TYPE_CHECKING: - from ...pie_menu import PieMenu + from ..pie_menu import PieMenu class EditMode: diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/label_widget_utils/__init__.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_utils/create_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/create_label_widget.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/label_widget_utils/create_label_widget.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/create_label_widget.py diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_utils/icon_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/icon_label_widget.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/label_widget_utils/icon_label_widget.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/icon_label_widget.py diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_utils/image_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/label_widget_utils/image_label_widget.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_utils/text_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/label_widget_utils/text_label_widget.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/pie_button.py b/shortcut_composer/templates/pie_menu_utils/pie_button.py similarity index 94% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/pie_button.py rename to shortcut_composer/templates/pie_menu_utils/pie_button.py index e41af8a4..d7f95d92 100644 --- a/shortcut_composer/templates/pie_menu_utils/widget_utils/pie_button.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_button.py @@ -7,8 +7,8 @@ from PyQt5.QtGui import QIcon from api_krita.pyqt import RoundButton -from ..pie_style import PieStyle -from ..pie_config import PieConfig +from .pie_style import PieStyle +from .pie_config import PieConfig class PieButton(RoundButton): diff --git a/shortcut_composer/templates/pie_menu_utils/pie_manager.py b/shortcut_composer/templates/pie_menu_utils/pie_manager.py index 0c0c9bf6..04fbfed4 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_manager.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_manager.py @@ -8,8 +8,8 @@ from api_krita.pyqt import Timer from composer_utils import Config from .pie_widget import PieWidget +from .pie_widget_utils import CirclePoints from .label import Label -from .widget_utils import CirclePoints class PieManager: diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py similarity index 99% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings.py index abf00e2c..6f502677 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -16,7 +16,8 @@ from api_krita.pyqt import AnimatedWidget, BaseWidget, SafeConfirmButton from config_system.ui import ConfigFormWidget, ConfigSpinBox from composer_utils import Config -from templates.pie_menu_utils import PieStyle, PieConfig +from .pie_style import PieStyle +from .pie_config import PieConfig class PieSettings(AnimatedWidget, BaseWidget): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py similarity index 83% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py index 5099237a..0935be55 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py @@ -1,16 +1,14 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from .pie_settings import PieSettings -from .enum_pie_settings import EnumPieSettings from .enum_group_pie_settings import EnumGroupPieSettings -from .preset_pie_settings import PresetPieSettings from .numeric_pie_settings import NumericPieSettings +from .preset_pie_settings import PresetPieSettings +from .enum_pie_settings import EnumPieSettings __all__ = [ - "PieSettings", - "EnumPieSettings", "EnumGroupPieSettings", + "NumericPieSettings", "PresetPieSettings", - "NumericPieSettings" + "EnumPieSettings", ] diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/__init__.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_combo_box.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_manager.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_manager.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_scroll_area.py similarity index 95% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_scroll_area.py index 17abc55d..753f4bed 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/group_scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_scroll_area.py @@ -5,7 +5,7 @@ from config_system import Field from templates.pie_menu_utils import PieStyle -from templates.pie_menu_utils.settings_gui.components import ( +from templates.pie_menu_utils.pie_settings_impl.common_utils import ( GroupComboBox, GroupManager) from .scroll_area import ScrollArea diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/offset_grid_layout.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py similarity index 98% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py index ee3833b0..98b262e4 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/components/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py @@ -16,7 +16,7 @@ QSizePolicy) from templates.pie_menu_utils import Label, LabelWidget, PieStyle -from templates.pie_menu_utils.label_widget_utils import create_label_widget +from templates.pie_menu_utils.label_widget_impl import create_label_widget from .offset_grid_layout import OffsetGridLayout diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py similarity index 92% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py index a3abda38..d47d4110 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py @@ -6,9 +6,12 @@ from api_krita.enums.helpers import EnumGroup from core_components import Controller -from templates.pie_menu_utils import Label, PieStyle, NonPresetPieConfig -from .pie_settings import PieSettings -from .components import GroupScrollArea, GroupManager +from templates.pie_menu_utils import ( + Label, + PieStyle, + PieSettings, + NonPresetPieConfig) +from .common_utils import GroupScrollArea, GroupManager class EnumGroupPieSettings(PieSettings): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py similarity index 90% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py index 164ffeba..caf703f4 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/enum_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py @@ -4,10 +4,12 @@ from enum import Enum from core_components import Controller -from templates.pie_menu_utils import Label, PieStyle, NonPresetPieConfig - -from .pie_settings import PieSettings -from .components import ScrollArea +from templates.pie_menu_utils import ( + Label, + PieStyle, + PieSettings, + NonPresetPieConfig) +from .common_utils import ScrollArea class EnumPieSettings(PieSettings): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/numeric_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/numeric_pie_settings.py similarity index 85% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/numeric_pie_settings.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/numeric_pie_settings.py index 21be21d3..7c496ecd 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/numeric_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/numeric_pie_settings.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from .pie_settings import PieSettings +from templates.pie_menu_utils import PieSettings class NumericPieSettings(PieSettings): diff --git a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py similarity index 97% rename from shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py rename to shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py index 0b607b74..6b34d5da 100644 --- a/shortcut_composer/templates/pie_menu_utils/settings_gui/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py @@ -10,9 +10,12 @@ from api_krita.pyqt import SafeConfirmButton from core_components.controllers import PresetController from data_components import Tag -from templates.pie_menu_utils import Label, PieStyle, PresetPieConfig -from .pie_settings import PieSettings -from .components import GroupComboBox, GroupScrollArea, GroupManager +from templates.pie_menu_utils import ( + Label, + PieStyle, + PieSettings, + PresetPieConfig) +from .common_utils import GroupComboBox, GroupScrollArea, GroupManager class PresetPieSettings(PieSettings): diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget.py b/shortcut_composer/templates/pie_menu_utils/pie_widget.py index 186b1379..638c7bd3 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget.py @@ -16,7 +16,7 @@ from .label import Label from .label_widget import LabelWidget from .pie_config import PieConfig -from .widget_utils import ( +from .pie_widget_utils import ( WidgetHolder, CirclePoints, LabelHolder, diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/__init__.py similarity index 70% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/__init__.py rename to shortcut_composer/templates/pie_menu_utils/pie_widget_utils/__init__.py index bfd3d700..55fa7ead 100644 --- a/shortcut_composer/templates/pie_menu_utils/widget_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/__init__.py @@ -1,20 +1,16 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -"""Additional classes used by pie menu components.""" +"""Additional classes used by pie widget components.""" from .circle_points import CirclePoints from .widget_holder import WidgetHolder from .label_holder import LabelHolder from .pie_painter import PiePainter -from .pie_button import PieButton -from .edit_mode import EditMode __all__ = [ "CirclePoints", "WidgetHolder", "LabelHolder", "PiePainter", - "PieButton", - "EditMode", ] diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/circle_points.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/circle_points.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/circle_points.py rename to shortcut_composer/templates/pie_menu_utils/pie_widget_utils/circle_points.py diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/label_holder.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py similarity index 98% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/label_holder.py rename to shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py index acdf02fa..297f0329 100644 --- a/shortcut_composer/templates/pie_menu_utils/widget_utils/label_holder.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py @@ -8,7 +8,7 @@ from ..pie_style import PieStyle from ..label import Label from ..label_widget import LabelWidget -from ..label_widget_utils import create_label_widget +from ..label_widget_impl import create_label_widget from ..pie_config import PieConfig from .widget_holder import WidgetHolder from .circle_points import CirclePoints diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/pie_painter.py rename to shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py diff --git a/shortcut_composer/templates/pie_menu_utils/widget_utils/widget_holder.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py similarity index 100% rename from shortcut_composer/templates/pie_menu_utils/widget_utils/widget_holder.py rename to shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py From 8f65260d2ffd275fc0bf5933ce28104b6c6ec7c8 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 16/69] Use dispatchers located in the package with different implementations --- shortcut_composer/templates/pie_menu.py | 23 ++++--------------- .../label_widget_impl/__init__.py | 4 ++-- ...bel_widget.py => dispatch_label_widget.py} | 16 +++---------- .../templates/pie_menu_utils/pie_settings.py | 1 + .../pie_settings_impl/__init__.py | 14 ++++------- .../common_utils/scroll_area.py | 4 ++-- .../dispatch_pie_settings.py | 23 +++++++++++++++++++ .../enum_group_pie_settings.py | 1 + .../pie_settings_impl/enum_pie_settings.py | 1 + .../pie_settings_impl/preset_pie_settings.py | 1 + .../pie_widget_utils/label_holder.py | 6 ++--- 11 files changed, 46 insertions(+), 48 deletions(-) rename shortcut_composer/templates/pie_menu_utils/label_widget_impl/{create_label_widget.py => dispatch_label_widget.py} (64%) create mode 100644 shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index d9eb0473..9ee86ce3 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -3,19 +3,13 @@ from typing import List, Type, TypeVar, Generic, Optional from functools import cached_property -from enum import Enum from PyQt5.QtCore import QPoint from PyQt5.QtGui import QColor from api_krita import Krita -from api_krita.enums.helpers import EnumGroup from core_components import Controller, Instruction -from .pie_menu_utils.pie_settings_impl import ( - EnumGroupPieSettings, - NumericPieSettings, - PresetPieSettings, - EnumPieSettings) +from .pie_menu_utils.pie_settings_impl import dispatch_pie_settings from .pie_menu_utils import ( NonPresetPieConfig, PresetPieConfig, @@ -121,17 +115,10 @@ def pie_widget(self) -> PieWidget: @cached_property def pie_settings(self) -> PieSettings: """Create and return the right settings based on labels type.""" - if issubclass(self._controller.TYPE, str): - return PresetPieSettings(self._config, self._style) # type: ignore - elif issubclass(self._controller.TYPE, float): - return NumericPieSettings(self._config, self._style) - elif issubclass(self._controller.TYPE, EnumGroup): - return EnumGroupPieSettings( - self._controller, self._config, self._style) # type: ignore - elif issubclass(self._controller.TYPE, Enum): - return EnumPieSettings( - self._controller, self._config, self._style) # type: ignore - raise ValueError(f"Unknown pie config {self._config}") + return dispatch_pie_settings(self._controller)( + config=self._config, + style=self._style, + controller=self._controller) @cached_property def pie_manager(self) -> PieManager: diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py index 6d3e69fd..bd14b4bb 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/__init__.py @@ -3,6 +3,6 @@ """Implementation of different LabelWidget types.""" -from .create_label_widget import create_label_widget +from .dispatch_label_widget import dispatch_label_widget -__all__ = ["create_label_widget"] +__all__ = ["dispatch_label_widget"] diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/create_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/dispatch_label_widget.py similarity index 64% rename from shortcut_composer/templates/pie_menu_utils/label_widget_impl/create_label_widget.py rename to shortcut_composer/templates/pie_menu_utils/label_widget_impl/dispatch_label_widget.py index 9fbc5117..da593fa9 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/create_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/dispatch_label_widget.py @@ -7,10 +7,7 @@ QPixmap, QIcon, ) -from PyQt5.QtWidgets import QWidget - from api_krita.pyqt import Text -from ..pie_style import PieStyle from ..label import Label from ..label_widget import LabelWidget from .icon_label_widget import IconLabelWidget @@ -18,20 +15,13 @@ from .image_label_widget import ImageLabelWidget -def create_label_widget( - label: Label, - style: PieStyle, - parent: QWidget, - is_unscaled: bool = False, -) -> LabelWidget: - """Return LabelWidget which can display this label.""" +def dispatch_label_widget(label: Label) -> Type[LabelWidget]: + """Return type of LabelWidget proper for given label.""" if label.display_value is None: raise ValueError(f"Label {label} is not valid") - painter_type: Type[LabelWidget] = { + return { QPixmap: ImageLabelWidget, Text: TextLabelWidget, QIcon: IconLabelWidget, }[type(label.display_value)] - - return painter_type(label, style, parent, is_unscaled) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index 6f502677..f85bdefc 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -38,6 +38,7 @@ def __init__( self, config: PieConfig, style: PieStyle, + *args, **kwargs ) -> None: AnimatedWidget.__init__(self, None, Config.PIE_ANIMATION_TIME.read()) self.setMinimumHeight(round(style.widget_radius*2)) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py index 0935be55..31eb9eb6 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/__init__.py @@ -1,14 +1,8 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from .enum_group_pie_settings import EnumGroupPieSettings -from .numeric_pie_settings import NumericPieSettings -from .preset_pie_settings import PresetPieSettings -from .enum_pie_settings import EnumPieSettings +"""Implementation of different PieSettings.""" -__all__ = [ - "EnumGroupPieSettings", - "NumericPieSettings", - "PresetPieSettings", - "EnumPieSettings", -] +from .dispatch_pie_settings import dispatch_pie_settings + +__all__ = ["dispatch_pie_settings"] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py index 98b262e4..1c77bc35 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py @@ -16,7 +16,7 @@ QSizePolicy) from templates.pie_menu_utils import Label, LabelWidget, PieStyle -from templates.pie_menu_utils.label_widget_impl import create_label_widget +from templates.pie_menu_utils.label_widget_impl import dispatch_label_widget from .offset_grid_layout import OffsetGridLayout @@ -139,7 +139,7 @@ def _apply_search_bar_filter(self) -> None: def _create_child(self, label: Label) -> LabelWidget: """Create LabelWidget that represent the label.""" - child = create_label_widget( + child = dispatch_label_widget(label)( label=label, style=self._style, parent=self, diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py new file mode 100644 index 00000000..1521da19 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py @@ -0,0 +1,23 @@ +from typing import Type +from enum import Enum + +from api_krita.enums.helpers import EnumGroup +from core_components import Controller +from ..pie_settings import PieSettings +from .enum_group_pie_settings import EnumGroupPieSettings +from .numeric_pie_settings import NumericPieSettings +from .preset_pie_settings import PresetPieSettings +from .enum_pie_settings import EnumPieSettings + + +def dispatch_pie_settings(controller: Controller) -> Type[PieSettings]: + """Return the right settings type based on value type.""" + if issubclass(controller.TYPE, str): + return PresetPieSettings + elif issubclass(controller.TYPE, float): + return NumericPieSettings + elif issubclass(controller.TYPE, EnumGroup): + return EnumGroupPieSettings + elif issubclass(controller.TYPE, Enum): + return EnumPieSettings + raise ValueError(f"No known pie settings for type of `{controller.TYPE}`") diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py index d47d4110..11de3fb7 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py @@ -20,6 +20,7 @@ def __init__( controller: Controller[EnumGroup], config: NonPresetPieConfig, style: PieStyle, + *args, **kwargs ) -> None: super().__init__(config, style) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py index caf703f4..7a09fc3b 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py @@ -26,6 +26,7 @@ def __init__( controller: Controller[Enum], config: NonPresetPieConfig, style: PieStyle, + *args, **kwargs ) -> None: super().__init__(config, style) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py index 6b34d5da..18b96ee3 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py @@ -31,6 +31,7 @@ def __init__( self, config: PresetPieConfig, style: PieStyle, + *args, **kwargs ) -> None: super().__init__(config, style) self._config: PresetPieConfig diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py index 297f0329..34e7d685 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/label_holder.py @@ -8,7 +8,7 @@ from ..pie_style import PieStyle from ..label import Label from ..label_widget import LabelWidget -from ..label_widget_impl import create_label_widget +from ..label_widget_impl import dispatch_label_widget from ..pie_config import PieConfig from .widget_holder import WidgetHolder from .circle_points import CirclePoints @@ -114,8 +114,8 @@ def reset(self, notify: bool = True) -> None: children_widgets: List[LabelWidget] = [] for label in self._labels: - children_widgets.append( - create_label_widget(label, self._style, self._owner)) + children_widgets.append(dispatch_label_widget(label)( + label, self._style, self._owner)) circle_points = CirclePoints( center=self._owner.center, From 30d511b3219d0313ae3b96c3e3189faef486b7e8 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 17/69] Separate package and dispatcher for PieConfig --- shortcut_composer/templates/pie_menu.py | 13 +- .../templates/pie_menu_utils/__init__.py | 4 +- .../templates/pie_menu_utils/pie_config.py | 154 +----------------- .../pie_config_impl/__init__.py | 8 + .../pie_config_impl/dispatch_pie_config.py | 13 ++ .../pie_config_impl/non_preset_pie_config.py | 66 ++++++++ .../pie_config_impl/preset_pie_config.py | 101 ++++++++++++ .../enum_group_pie_settings.py | 7 +- .../pie_settings_impl/enum_pie_settings.py | 7 +- .../pie_settings_impl/preset_pie_settings.py | 7 +- 10 files changed, 199 insertions(+), 181 deletions(-) create mode 100644 shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py create mode 100644 shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py create mode 100644 shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py create mode 100644 shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 9ee86ce3..d1d28b7b 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Type, TypeVar, Generic, Optional +from typing import List, TypeVar, Generic, Optional from functools import cached_property from PyQt5.QtCore import QPoint @@ -10,12 +10,10 @@ from api_krita import Krita from core_components import Controller, Instruction from .pie_menu_utils.pie_settings_impl import dispatch_pie_settings +from .pie_menu_utils.pie_config_impl import dispatch_pie_config from .pie_menu_utils import ( - NonPresetPieConfig, - PresetPieConfig, PieSettings, PieManager, - PieConfig, PieWidget, PieButton, EditMode, @@ -85,12 +83,7 @@ def __init__( super().__init__(name, instructions, short_vs_long_press_time) self._controller = controller - def _dispatch_config_type() -> Type[PieConfig[T]]: - if issubclass(self._controller.TYPE, str): - return PresetPieConfig # type: ignore - return NonPresetPieConfig - - self._config = _dispatch_config_type()(**{ + self._config = dispatch_pie_config(self._controller)(**{ "name": f"ShortcutComposer: {name}", "values": values, "pie_radius_scale": pie_radius_scale, diff --git a/shortcut_composer/templates/pie_menu_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/__init__.py index 7ff8f8f9..b4ef880e 100644 --- a/shortcut_composer/templates/pie_menu_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/__init__.py @@ -3,7 +3,7 @@ """Implementation of PieMenu main elements.""" -from .pie_config import PieConfig, PresetPieConfig, NonPresetPieConfig +from .pie_config import PieConfig from .pie_settings import PieSettings from .label_widget import LabelWidget from .pie_manager import PieManager @@ -14,8 +14,6 @@ from .label import Label __all__ = [ - "NonPresetPieConfig", - "PresetPieConfig", "PieSettings", "LabelWidget", "PieConfig", diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index 185f838e..ef583d5c 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -2,11 +2,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later from abc import ABC, abstractmethod -from typing import List, Callable, Generic, TypeVar, Optional, Union +from typing import List, Callable, Generic, TypeVar, Optional from PyQt5.QtGui import QColor from config_system import Field, FieldGroup from config_system.fields import DualField, FieldWithEditableDefault -from data_components import Tag T = TypeVar("T") U = TypeVar("U") @@ -77,154 +76,3 @@ def _create_editable_dual_field( return FieldWithEditableDefault( DualField(self, self.SAVE_LOCAL, field_name, default, parser_type), self.field(f"{field_name} default", default, parser_type)) - - -class PresetPieConfig(PieConfig[str]): - """ - FieldGroup representing config of PieMenu of presets. - - Values are calculated according to presets belonging to handled tag - and the custom order saved by the user in kritarc. - """ - - def __init__( - self, - name: str, - values: Union[Tag, List[str]], - pie_radius_scale: float, - icon_radius_scale: float, - save_local: bool, - background_color: Optional[QColor], - active_color: QColor, - ) -> None: - super().__init__(name) - - self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) - self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) - - self.SAVE_LOCAL = self.field("Save local", save_local) - - tag_mode = isinstance(values, Tag) - tag_name = values.tag_name if isinstance(values, Tag) else "" - self.TAG_MODE = self._create_editable_dual_field("Tag mode", tag_mode) - self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) - self.ORDER = self._create_editable_dual_field("Values", [], str) - - self.background_color = background_color - self.active_color = active_color - - @property - def allow_value_edit(self) -> bool: - """Return whether user can add and remove items from the pie.""" - return not self.TAG_MODE.read() - - def values(self) -> List[str]: - """Return all presets based on mode and stored order.""" - if not self.TAG_MODE.read(): - return self.ORDER.read() - return Tag(self.TAG_NAME.read()) - - def set_values(self, values: List[str]) -> None: - """When in tag mode, remember the tag order. Then write normally.""" - if self.TAG_MODE.read(): - group = "ShortcutComposer: Tag order" - field = Field(group, self.TAG_NAME.read(), [], str) - field.write(values) - - self.ORDER.write(values) - - def refresh_order(self) -> None: - """Refresh the values in case the active document changed.""" - self.TAG_MODE.field.refresh() - self.TAG_NAME.field.refresh() - self.ORDER.write(self.values()) - - def set_current_as_default(self): - """Set current pie values as a new default list of values.""" - self.TAG_MODE.default = self.TAG_MODE.read() - self.TAG_NAME.default = self.TAG_NAME.read() - self.ORDER.default = self.ORDER.read() - - def reset_the_default(self) -> None: - """Set empty pie as a new default list of values.""" - self.TAG_MODE.default = False - self.TAG_NAME.default = "" - self.ORDER.default = [] - - def reset_to_default(self) -> None: - """Replace current list of values in pie with the default list.""" - self.TAG_MODE.reset_default() - self.TAG_NAME.reset_default() - self.ORDER.reset_default() - self.refresh_order() - - def is_order_default(self) -> bool: - """Return whether order is the same as default one.""" - return ( - self.TAG_MODE.read() == self.TAG_MODE.default - and self.TAG_NAME.read() == self.TAG_NAME.default - and self.ORDER.read() == self.ORDER.default) - - def register_to_order_related(self, callback: Callable[[], None]) -> None: - """Register callback to all fields related to value order.""" - self.TAG_MODE.register_callback(callback) - self.TAG_NAME.register_callback(callback) - self.ORDER.register_callback(callback) - - -class NonPresetPieConfig(PieConfig[T], Generic[T]): - """FieldGroup representing config of PieMenu of non-preset values.""" - - def __init__( - self, - name: str, - values: List[T], - pie_radius_scale: float, - icon_radius_scale: float, - save_local: bool, - background_color: Optional[QColor], - active_color: QColor, - ) -> None: - super().__init__(name) - - self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) - self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) - - self.SAVE_LOCAL = self.field("Save local", save_local) - self.ORDER = self._create_editable_dual_field("Values", values) - - self.background_color = background_color - self.active_color = active_color - self.allow_value_edit = True - - def values(self) -> List[T]: - """Return values defined be the user to display as icons.""" - return self.ORDER.read() - - def set_values(self, values: List[T]) -> None: - """Change current values to new ones.""" - self.ORDER.write(values) - - def refresh_order(self) -> None: - """Refresh the values in case the active document changed.""" - self.ORDER.write(self.values()) - - def set_current_as_default(self): - """Set current pie values as a new default list of values.""" - self.ORDER.default = self.ORDER.read() - - def reset_the_default(self) -> None: - """Set empty pie as a new default list of values.""" - self.ORDER.default = [] - - def reset_to_default(self) -> None: - self.ORDER.reset_default() - self.refresh_order() - - def is_order_default(self) -> bool: - """Return whether order is the same as default one.""" - return self.ORDER.read() == self.ORDER.default - - def register_to_order_related(self, callback: Callable[[], None]) -> None: - """Register callback to all fields related to value order.""" - self.ORDER.register_callback(callback) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py new file mode 100644 index 00000000..aa5b8254 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from .dispatch_pie_config import dispatch_pie_config +from .preset_pie_config import PresetPieConfig +from .non_preset_pie_config import NonPresetPieConfig + +__all__ = ["dispatch_pie_config", "PresetPieConfig", "NonPresetPieConfig"] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py new file mode 100644 index 00000000..28c9d33f --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py @@ -0,0 +1,13 @@ +from typing import Type, TypeVar +from core_components import Controller +from ..pie_config import PieConfig +from .preset_pie_config import PresetPieConfig +from .non_preset_pie_config import NonPresetPieConfig + +T = TypeVar('T') + + +def dispatch_pie_config(controller: Controller[T]) -> Type[PieConfig[T]]: + if issubclass(controller.TYPE, str): + return PresetPieConfig # type: ignore + return NonPresetPieConfig diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py new file mode 100644 index 00000000..ffbe150d --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import List, Callable, Generic, TypeVar, Optional +from PyQt5.QtGui import QColor +from ..pie_config import PieConfig + +T = TypeVar("T") + + +class NonPresetPieConfig(PieConfig[T], Generic[T]): + """FieldGroup representing config of PieMenu of non-preset values.""" + + def __init__( + self, + name: str, + values: List[T], + pie_radius_scale: float, + icon_radius_scale: float, + save_local: bool, + background_color: Optional[QColor], + active_color: QColor, + ) -> None: + super().__init__(name) + + self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) + self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) + + self.SAVE_LOCAL = self.field("Save local", save_local) + self.ORDER = self._create_editable_dual_field("Values", values) + + self.background_color = background_color + self.active_color = active_color + self.allow_value_edit = True + + def values(self) -> List[T]: + """Return values defined be the user to display as icons.""" + return self.ORDER.read() + + def set_values(self, values: List[T]) -> None: + """Change current values to new ones.""" + self.ORDER.write(values) + + def refresh_order(self) -> None: + """Refresh the values in case the active document changed.""" + self.ORDER.write(self.values()) + + def set_current_as_default(self): + """Set current pie values as a new default list of values.""" + self.ORDER.default = self.ORDER.read() + + def reset_the_default(self) -> None: + """Set empty pie as a new default list of values.""" + self.ORDER.default = [] + + def reset_to_default(self) -> None: + self.ORDER.reset_default() + self.refresh_order() + + def is_order_default(self) -> bool: + """Return whether order is the same as default one.""" + return self.ORDER.read() == self.ORDER.default + + def register_to_order_related(self, callback: Callable[[], None]) -> None: + """Register callback to all fields related to value order.""" + self.ORDER.register_callback(callback) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py new file mode 100644 index 00000000..d1b95cbb --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import List, Callable, Optional, Union +from PyQt5.QtGui import QColor +from config_system import Field +from data_components import Tag +from ..pie_config import PieConfig + + +class PresetPieConfig(PieConfig[str]): + """ + FieldGroup representing config of PieMenu of presets. + + Values are calculated according to presets belonging to handled tag + and the custom order saved by the user in kritarc. + """ + + def __init__( + self, + name: str, + values: Union[Tag, List[str]], + pie_radius_scale: float, + icon_radius_scale: float, + save_local: bool, + background_color: Optional[QColor], + active_color: QColor, + ) -> None: + super().__init__(name) + + self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) + self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) + + self.SAVE_LOCAL = self.field("Save local", save_local) + + tag_mode = isinstance(values, Tag) + tag_name = values.tag_name if isinstance(values, Tag) else "" + self.TAG_MODE = self._create_editable_dual_field("Tag mode", tag_mode) + self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) + self.ORDER = self._create_editable_dual_field("Values", [], str) + + self.background_color = background_color + self.active_color = active_color + + @property + def allow_value_edit(self) -> bool: + """Return whether user can add and remove items from the pie.""" + return not self.TAG_MODE.read() + + def values(self) -> List[str]: + """Return all presets based on mode and stored order.""" + if not self.TAG_MODE.read(): + return self.ORDER.read() + return Tag(self.TAG_NAME.read()) + + def set_values(self, values: List[str]) -> None: + """When in tag mode, remember the tag order. Then write normally.""" + if self.TAG_MODE.read(): + group = "ShortcutComposer: Tag order" + field = Field(group, self.TAG_NAME.read(), [], str) + field.write(values) + + self.ORDER.write(values) + + def refresh_order(self) -> None: + """Refresh the values in case the active document changed.""" + self.TAG_MODE.field.refresh() + self.TAG_NAME.field.refresh() + self.ORDER.write(self.values()) + + def set_current_as_default(self): + """Set current pie values as a new default list of values.""" + self.TAG_MODE.default = self.TAG_MODE.read() + self.TAG_NAME.default = self.TAG_NAME.read() + self.ORDER.default = self.ORDER.read() + + def reset_the_default(self) -> None: + """Set empty pie as a new default list of values.""" + self.TAG_MODE.default = False + self.TAG_NAME.default = "" + self.ORDER.default = [] + + def reset_to_default(self) -> None: + """Replace current list of values in pie with the default list.""" + self.TAG_MODE.reset_default() + self.TAG_NAME.reset_default() + self.ORDER.reset_default() + self.refresh_order() + + def is_order_default(self) -> bool: + """Return whether order is the same as default one.""" + return ( + self.TAG_MODE.read() == self.TAG_MODE.default + and self.TAG_NAME.read() == self.TAG_NAME.default + and self.ORDER.read() == self.ORDER.default) + + def register_to_order_related(self, callback: Callable[[], None]) -> None: + """Register callback to all fields related to value order.""" + self.TAG_MODE.register_callback(callback) + self.TAG_NAME.register_callback(callback) + self.ORDER.register_callback(callback) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py index 11de3fb7..ae23a9c5 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_group_pie_settings.py @@ -6,11 +6,8 @@ from api_krita.enums.helpers import EnumGroup from core_components import Controller -from templates.pie_menu_utils import ( - Label, - PieStyle, - PieSettings, - NonPresetPieConfig) +from templates.pie_menu_utils.pie_config_impl import NonPresetPieConfig +from templates.pie_menu_utils import Label, PieStyle, PieSettings from .common_utils import GroupScrollArea, GroupManager diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py index 7a09fc3b..d5383693 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/enum_pie_settings.py @@ -4,11 +4,8 @@ from enum import Enum from core_components import Controller -from templates.pie_menu_utils import ( - Label, - PieStyle, - PieSettings, - NonPresetPieConfig) +from templates.pie_menu_utils.pie_config_impl import NonPresetPieConfig +from templates.pie_menu_utils import Label, PieStyle, PieSettings from .common_utils import ScrollArea diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py index 18b96ee3..40713b4b 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/preset_pie_settings.py @@ -10,11 +10,8 @@ from api_krita.pyqt import SafeConfirmButton from core_components.controllers import PresetController from data_components import Tag -from templates.pie_menu_utils import ( - Label, - PieStyle, - PieSettings, - PresetPieConfig) +from templates.pie_menu_utils.pie_config_impl import PresetPieConfig +from templates.pie_menu_utils import Label, PieStyle, PieSettings from .common_utils import GroupComboBox, GroupScrollArea, GroupManager From 35a333cc7b8595ad574579049ec423107d34ec9d Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 5 Jun 2023 11:25:23 +0200 Subject: [PATCH 18/69] Changed how PieConfig is initialized --- shortcut_composer/templates/pie_menu.py | 16 ++++++++-------- .../templates/pie_menu_utils/pie_config.py | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index d1d28b7b..c332d66d 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -83,14 +83,14 @@ def __init__( super().__init__(name, instructions, short_vs_long_press_time) self._controller = controller - self._config = dispatch_pie_config(self._controller)(**{ - "name": f"ShortcutComposer: {name}", - "values": values, - "pie_radius_scale": pie_radius_scale, - "icon_radius_scale": icon_radius_scale, - "save_local": save_local, - "background_color": background_color, - "active_color": active_color}) + self._config = dispatch_pie_config(self._controller)( + name=f"ShortcutComposer: {name}", + values=values, + pie_radius_scale=pie_radius_scale, + icon_radius_scale=icon_radius_scale, + save_local=save_local, + background_color=background_color, + active_color=active_color) self._config.ORDER.register_callback(self._reset_labels) self._labels: List[Label] = [] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index ef583d5c..f1e97427 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -14,6 +14,9 @@ class PieConfig(FieldGroup, Generic[T], ABC): """Abstract FieldGroup representing config of PieMenu.""" + def __init__(self, name, *args, **kwargs) -> None: + super().__init__(name) + allow_value_edit: bool """Is it allowed to remove elements in runtime. """ From f0e8effca0dd522b94971f1165ee85bd399ba32b Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 19/69] Config package refactor --- .../config_system/common_utils/__init__.py | 4 +++ .../{ => common_utils}/api_krita.py | 0 .../{ => common_utils}/save_location.py | 0 shortcut_composer/config_system/field.py | 2 +- shortcut_composer/config_system/field_base.py | 16 +-------- .../{fields => field_base_impl}/__init__.py | 9 ++++- .../field_base_impl/common_utils/__init__.py | 3 ++ .../common_utils}/parsers.py | 13 +++++++ .../{fields => field_base_impl}/dual_field.py | 5 +-- .../field_with_editable_default.py | 3 +- .../list_field.py} | 34 ++----------------- .../field_base_impl/non_list_field.py | 34 +++++++++++++++++++ .../templates/pie_menu_utils/pie_config.py | 2 +- 13 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 shortcut_composer/config_system/common_utils/__init__.py rename shortcut_composer/config_system/{ => common_utils}/api_krita.py (100%) rename shortcut_composer/config_system/{ => common_utils}/save_location.py (100%) rename shortcut_composer/config_system/{fields => field_base_impl}/__init__.py (55%) create mode 100644 shortcut_composer/config_system/field_base_impl/common_utils/__init__.py rename shortcut_composer/config_system/{ => field_base_impl/common_utils}/parsers.py (84%) rename shortcut_composer/config_system/{fields => field_base_impl}/dual_field.py (99%) rename shortcut_composer/config_system/{fields => field_base_impl}/field_with_editable_default.py (99%) rename shortcut_composer/config_system/{field_implementations.py => field_base_impl/list_field.py} (65%) create mode 100644 shortcut_composer/config_system/field_base_impl/non_list_field.py diff --git a/shortcut_composer/config_system/common_utils/__init__.py b/shortcut_composer/config_system/common_utils/__init__.py new file mode 100644 index 00000000..d5f3ca03 --- /dev/null +++ b/shortcut_composer/config_system/common_utils/__init__.py @@ -0,0 +1,4 @@ +from .api_krita import Krita +from .save_location import SaveLocation + +__all__ = ["Krita", "SaveLocation"] diff --git a/shortcut_composer/config_system/api_krita.py b/shortcut_composer/config_system/common_utils/api_krita.py similarity index 100% rename from shortcut_composer/config_system/api_krita.py rename to shortcut_composer/config_system/common_utils/api_krita.py diff --git a/shortcut_composer/config_system/save_location.py b/shortcut_composer/config_system/common_utils/save_location.py similarity index 100% rename from shortcut_composer/config_system/save_location.py rename to shortcut_composer/config_system/common_utils/save_location.py diff --git a/shortcut_composer/config_system/field.py b/shortcut_composer/config_system/field.py index efeed33e..1132be11 100644 --- a/shortcut_composer/config_system/field.py +++ b/shortcut_composer/config_system/field.py @@ -44,7 +44,7 @@ def __new__( parser_type: Optional[type] = None, local: bool = False, ) -> 'Field[T]': - from .field_implementations import ListField, NonListField + from .field_base_impl import ListField, NonListField cls.original = super().__new__ if not isinstance(default, list): diff --git a/shortcut_composer/config_system/field_base.py b/shortcut_composer/config_system/field_base.py index bb2af0f9..c0f88993 100644 --- a/shortcut_composer/config_system/field_base.py +++ b/shortcut_composer/config_system/field_base.py @@ -5,9 +5,8 @@ from abc import ABC, abstractmethod from enum import Enum -from .parsers import Parser, BoolParser, EnumParser, BasicParser +from .common_utils import SaveLocation from .field import Field -from .save_location import SaveLocation T = TypeVar('T') E = TypeVar('E', bound=Enum) @@ -82,16 +81,3 @@ def _is_write_redundant(self, value: T) -> bool: def reset_default(self) -> None: """Write a default value to kritarc file.""" self.write(self.default) - - @staticmethod - def _get_parser(parser_type: type) -> Parser[T]: - """Return field parser.""" - if issubclass(parser_type, Enum): - return EnumParser(parser_type) # type: ignore - - return { - int: BasicParser(int), - float: BasicParser(float), - str: BasicParser(str), - bool: BoolParser() - }[parser_type] # type: ignore diff --git a/shortcut_composer/config_system/fields/__init__.py b/shortcut_composer/config_system/field_base_impl/__init__.py similarity index 55% rename from shortcut_composer/config_system/fields/__init__.py rename to shortcut_composer/config_system/field_base_impl/__init__.py index 5099fa0b..64423378 100644 --- a/shortcut_composer/config_system/fields/__init__.py +++ b/shortcut_composer/config_system/field_base_impl/__init__.py @@ -1,7 +1,14 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +from .non_list_field import NonListField +from .list_field import ListField from .dual_field import DualField from .field_with_editable_default import FieldWithEditableDefault -__all__ = ["DualField", "FieldWithEditableDefault"] +__all__ = [ + "NonListField", + "ListField", + "DualField", + "FieldWithEditableDefault" +] diff --git a/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py b/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py new file mode 100644 index 00000000..8f053684 --- /dev/null +++ b/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py @@ -0,0 +1,3 @@ +from .parsers import dispatch_parser, Parser + +__all__ = ["dispatch_parser", "Parser"] diff --git a/shortcut_composer/config_system/parsers.py b/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py similarity index 84% rename from shortcut_composer/config_system/parsers.py rename to shortcut_composer/config_system/field_base_impl/common_utils/parsers.py index 65ac1850..eba8850e 100644 --- a/shortcut_composer/config_system/parsers.py +++ b/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py @@ -9,6 +9,19 @@ EnumT = TypeVar("EnumT", bound=Enum) +def dispatch_parser(parser_type: type) -> 'Parser': + """Return a proper field parser based on given type.""" + if issubclass(parser_type, Enum): + return EnumParser(parser_type) + + return { + int: BasicParser(int), + float: BasicParser(float), + str: BasicParser(str), + bool: BoolParser() + }[parser_type] + + class Parser(Generic[T], Protocol): """Parses from string to specific type and vice-versa.""" diff --git a/shortcut_composer/config_system/fields/dual_field.py b/shortcut_composer/config_system/field_base_impl/dual_field.py similarity index 99% rename from shortcut_composer/config_system/fields/dual_field.py rename to shortcut_composer/config_system/field_base_impl/dual_field.py index 7b4e23f1..e9fd8337 100644 --- a/shortcut_composer/config_system/fields/dual_field.py +++ b/shortcut_composer/config_system/field_base_impl/dual_field.py @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +from typing import Callable, Generic, Optional, TypeVar from ..field import Field from ..field_group import FieldGroup -from typing import Callable, Generic, Optional, TypeVar - T = TypeVar("T") F = TypeVar("F", bound=Field) @@ -13,9 +12,11 @@ class DualField(Field, Generic[T]): """ Field switching save location based on passed field. + Implementation uses two identical fields, but with different save location. Each time DualField is red or written, correct field is picked from the determiner field. + NOTE: Callbacks are always stored in the global field, as they wouldn't run in local one when switching between documents. """ diff --git a/shortcut_composer/config_system/fields/field_with_editable_default.py b/shortcut_composer/config_system/field_base_impl/field_with_editable_default.py similarity index 99% rename from shortcut_composer/config_system/fields/field_with_editable_default.py rename to shortcut_composer/config_system/field_base_impl/field_with_editable_default.py index 0ce89e73..9df04b31 100644 --- a/shortcut_composer/config_system/fields/field_with_editable_default.py +++ b/shortcut_composer/config_system/field_base_impl/field_with_editable_default.py @@ -1,9 +1,8 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from ..field import Field - from typing import Callable, Generic, TypeVar +from ..field import Field T = TypeVar("T") F = TypeVar("F", bound=Field) diff --git a/shortcut_composer/config_system/field_implementations.py b/shortcut_composer/config_system/field_base_impl/list_field.py similarity index 65% rename from shortcut_composer/config_system/field_implementations.py rename to shortcut_composer/config_system/field_base_impl/list_field.py index 83e88988..0eb81f34 100644 --- a/shortcut_composer/config_system/field_implementations.py +++ b/shortcut_composer/config_system/field_base_impl/list_field.py @@ -2,39 +2,12 @@ # SPDX-License-Identifier: GPL-3.0-or-later from typing import TypeVar, Generic, Optional, List - -from .parsers import Parser -from .field_base import FieldBase +from ..field_base import FieldBase +from .common_utils import dispatch_parser T = TypeVar('T') -class NonListField(FieldBase, Generic[T]): - """Config field containing a basic, non-list value.""" - - def __init__( - self, - config_group: str, - name: str, - default: T, - parser_type: Optional[type] = None, - local: bool = False, - ) -> None: - super().__init__(config_group, name, default, parser_type, local) - self._parser: Parser[T] = self._get_parser(type(self.default)) - - def read(self) -> T: - """Return value from kritarc parsed to field type.""" - raw = self.location.read(self.config_group, self.name) - if raw is None: - return self.default - return self._parser.parse_to(raw) - - def _to_string(self, value: T) -> str: - """Parse the field value to string using parser.""" - return self._parser.parse_from(value) - - class ListField(FieldBase, Generic[T]): """Config field containing a list value.""" @@ -47,8 +20,7 @@ def __init__( local: bool = False, ) -> None: super().__init__(config_group, name, default, parser_type, local) - self._parser: Parser[T] = self._get_parser( - self._get_type(self.parser_type)) + self._parser = dispatch_parser(self._get_type(self.parser_type)) def write(self, value: List[T]) -> None: for element in value: diff --git a/shortcut_composer/config_system/field_base_impl/non_list_field.py b/shortcut_composer/config_system/field_base_impl/non_list_field.py new file mode 100644 index 00000000..614e89a2 --- /dev/null +++ b/shortcut_composer/config_system/field_base_impl/non_list_field.py @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from typing import TypeVar, Generic, Optional +from ..field_base import FieldBase +from .common_utils import dispatch_parser + +T = TypeVar('T') + + +class NonListField(FieldBase, Generic[T]): + """Config field containing a basic, non-list value.""" + + def __init__( + self, + config_group: str, + name: str, + default: T, + parser_type: Optional[type] = None, + local: bool = False, + ) -> None: + super().__init__(config_group, name, default, parser_type, local) + self._parser = dispatch_parser(type(self.default)) + + def read(self) -> T: + """Return value from kritarc parsed to field type.""" + raw = self.location.read(self.config_group, self.name) + if raw is None: + return self.default + return self._parser.parse_to(raw) + + def _to_string(self, value: T) -> str: + """Parse the field value to string using parser.""" + return self._parser.parse_from(value) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index f1e97427..bdffa302 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -5,7 +5,7 @@ from typing import List, Callable, Generic, TypeVar, Optional from PyQt5.QtGui import QColor from config_system import Field, FieldGroup -from config_system.fields import DualField, FieldWithEditableDefault +from config_system.field_base_impl import DualField, FieldWithEditableDefault T = TypeVar("T") U = TypeVar("U") From 6975dd841b36ab6d3add4b10abbd82bc0c67c3aa Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 20/69] Rewrite Tools Enum as EnumGroup --- shortcut_composer/api_krita/enums/tool.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/shortcut_composer/api_krita/enums/tool.py b/shortcut_composer/api_krita/enums/tool.py index 3d9cbb55..8bd8cfec 100644 --- a/shortcut_composer/api_krita/enums/tool.py +++ b/shortcut_composer/api_krita/enums/tool.py @@ -2,24 +2,20 @@ # SPDX-License-Identifier: GPL-3.0-or-later from krita import Krita as Api -from enum import Enum from PyQt5.QtGui import QIcon +from .helpers import EnumGroup, Group -class Tool(Enum): - """ - Contains all known tools from krita toolbox. - - Extended with modes of the transform tool. - - Example usage: `Tool.FREEHAND_BRUSH` - """ +class Tool(EnumGroup): + _vectors = Group("Vectors") SHAPE_SELECT = "InteractionTool" TEXT = "SvgTextTool" EDIT_SHAPES = "PathTool" CALLIGRAPHY = "KarbonCalligraphyTool" + + _painting = Group("Painting") FREEHAND_BRUSH = "KritaShape/KisToolBrush" LINE = "KritaShape/KisToolLine" RECTANGLE = "KritaShape/KisToolRectangle" @@ -30,6 +26,8 @@ class Tool(Enum): FREEHAND_PATH = "KisToolPencil" DYNAMIC_BRUSH = "KritaShape/KisToolDyna" MULTI_BRUSH = "KritaShape/KisToolMultiBrush" + + _editing = Group("Editing") TRANSFORM = "KisToolTransform" MOVE = "KritaTransform/KisToolMove" CROP = "KisToolCrop" @@ -39,9 +37,13 @@ class Tool(Enum): SMART_PATCH = "KritaShape/KisToolSmartPatch" FILL = "KritaFill/KisToolFill" ENCLOSE_AND_FILL = "KisToolEncloseAndFill" + + _utility = Group("Utility") ASSISTANTS = "KisAssistantTool" MEASUREMENT = "KritaShape/KisToolMeasure" REFERENCE = "ToolReferenceImages" + + _selection = Group("Selection") RECTANGULAR_SELECTION = "KisToolSelectRectangular" ELIPTICAL_SELECTION = "KisToolSelectElliptical" POLYGONAL_SELECTION = "KisToolSelectPolygonal" @@ -50,6 +52,8 @@ class Tool(Enum): SIMILAR_COLOR_SELECTION = "KisToolSelectSimilar" BEZIER_SELECTION = "KisToolSelectPath" MAGNETIC_SELECTION = "KisToolSelectMagnetic" + + _canvas_navigation = Group("Canvas navigation") ZOOM = "ZoomTool" PAN = "PanTool" From b6d8bbf2aa672e7ae3e68dc806d86c59402f4886 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 21/69] Rewrite BlendingMode as EnumGroup --- .../api_krita/enums/blending_mode.py | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/shortcut_composer/api_krita/enums/blending_mode.py b/shortcut_composer/api_krita/enums/blending_mode.py index 1afc0778..83131c74 100644 --- a/shortcut_composer/api_krita/enums/blending_mode.py +++ b/shortcut_composer/api_krita/enums/blending_mode.py @@ -1,33 +1,24 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from enum import Enum +from .helpers import EnumGroup, Group -class BlendingMode(Enum): +class BlendingMode(EnumGroup): """ Contains all known blending modes in krita. Example usage: `BlendingMode.NORMAL` """ - NORMAL = "normal" + _arithmetic = Group("Arithmetic") ADD = "add" - BURN = "burn" - COLOR = "color" - DODGE = "dodge" - DARKEN = "darken" DIVIDE = "divide" - ERASE = "erase" - LIGHTEN = "lighten" - LUMINIZE = "luminize" - MULTIPLY = "multiply" - OVERLAY = "overlay" - SATURATION = "saturation" - SCREEN = "screen" - SOFT_LIGHT_SVG = "soft_light_svg" INVERSE_SUBTRACT = "inverse_subtract" + MULTIPLY = "multiply" SUBTRACT = "subtract" + + _binary = Group("Binary") AND = "and" CONVERSE = "converse" IMPLICATION = "implication" @@ -38,12 +29,18 @@ class BlendingMode(Enum): OR = "or" XNOR = "xnor" XOR = "xor" + + _darken = Group("Darken") + BURN = "burn" + DARKEN = "darken" DARKER_COLOR = "darker color" EASY_BURN = "easy burn" FOG_DARKEN_IFS_ILLUSIONS = "fog_darken_ifs_illusions" GAMMA_DARK = "gamma_dark" - SHADE_IFS_ILLUSIONS = "shade_ifs_illusions" LINEAR_BURN = "linear_burn" + SHADE_IFS_ILLUSIONS = "shade_ifs_illusions" + + _hsi = Group("HSI") COLOR_HSI = "color_hsi" DEC_INTENSITY = "dec_intensity" DEC_SATURATION_HSI = "dec_saturation_hsi" @@ -52,14 +49,18 @@ class BlendingMode(Enum): INC_SATURATION_HSI = "inc_saturation_hsi" INTENSITY = "intensity" SATURATION_HSI = "saturation_hsi" - DEC_LIGHTNESS = "dec_lightness" + + _hsl = Group("HSL") COLOR_HSL = "color_hsl" + DEC_LIGHTNESS = "dec_lightness" DEC_SATURATION_HSL = "dec_saturation_hsl" HUE_HSL = "hue_hsl" INC_LIGHTNESS = "inc_lightness" INC_SATURATION_HSL = "inc_saturation_hsl" LIGHTNESS = "lightness" SATURATION_HSL = "saturation_hsl" + + _hsv = Group("HSV") COLOR_HSV = "color_hsv" DEC_SATURATION_HSV = "dec_saturation_hsv" DEC_VALUE = "dec_value" @@ -68,17 +69,26 @@ class BlendingMode(Enum): INC_VALUE = "inc_value" SATURATION_HSV = "saturation_hsv" VALUE = "value" - DEC_SATURATION = "dec_saturation" + + _hsy = Group("HSY") + COLOR = "color" DEC_LUMINOSITY = "dec_luminosity" + DEC_SATURATION = "dec_saturation" HUE = "hue" INC_LUMINOSITY = "inc_luminosity" INC_SATURATION = "inc_saturation" + LUMINIZE = "luminize" + SATURATION = "saturation" + + _lighten = Group("Lighten") + DODGE = "dodge" EASY_DODGE = "easy dodge" FLAT_LIGHT = "flat_light" - GAMMA_ILLUMINATION = "gamma_illumination" FOG_LIGHTEN_IFS_ILLUSIONS = "fog_lighten_ifs_illusions" + GAMMA_ILLUMINATION = "gamma_illumination" GAMMA_LIGHT = "gamma_light" HARD_LIGHT = "hard_light" + LIGHTEN = "lighten" LIGHTER_COLOR = "lighter color" LINEAR_DODGE = "linear_dodge" LINEAR_LIGHT = "linear light" @@ -89,9 +99,13 @@ class BlendingMode(Enum): SOFT_LIGHT_IFS_ILLUSIONS = "soft_light_ifs_illusions" SOFT_LIGHT_PEGTOP_DELPHI = "soft_light_pegtop_delphi" SOFT_LIGHT = "soft_light" + SOFT_LIGHT_SVG = "soft_light_svg" + SCREEN = "screen" SUPER_LIGHT = "super_light" TINT_IFS_ILLUSIONS = "tint_ifs_illusions" VIVID_LIGHT = "vivid_light" + + _misc = Group("Misc") BUMPMAP = "bumpmap" COMBINE_NORMAL = "combine_normal" COPY = "copy" @@ -100,11 +114,14 @@ class BlendingMode(Enum): COPY_RED = "copy_red" DISSOLVE = "dissolve" TANGENT_NORMALMAP = "tangent_normalmap" + + _mix = Group("Mix") ALLANON = "allanon" ALPHADARKEN = "alphadarken" BEHIND = "behind" DESTINATION_ATOP = "destination-atop" DESTINATION_IN = "destination-in" + ERASE = "erase" GEOMETRIC_MEAN = "geometric_mean" GRAIN_EXTRACT = "grain_extract" GRAIN_MERGE = "grain_merge" @@ -115,22 +132,30 @@ class BlendingMode(Enum): HARD_OVERLAY = "hard overlay" INTERPOLATION = "interpolation" INTERPOLATION_2X = "interpolation 2x" + NORMAL = "normal" + OVERLAY = "overlay" PARALLEL = "parallel" PENUMBRA_A = "penumbra a" PENUMBRA_B = "penumbra b" PENUMBRA_C = "penumbra c" PENUMBRA_D = "penumbra d" + + _modulo = Group("Modulo") DIVISIVE_MODULO = "divisive_modulo" DIVISIVE_MODULO_CONTINUOUS = "divisive_modulo_continuous" MODULO_CONTINUOUS = "modulo_continuous" MODULO_SHIFT = "modulo_shift" MODULO_SHIFT_CONTINUOUS = "modulo_shift_continuous" + + _negative = Group("Negative") ADDITIVE_SUBTRACTIVE = "additive_subtractive" ARC_TANGENT = "arc_tangent" DIFF = "diff" EQUIVALENCE = "equivalence" EXCLUSION = "exclusion" NEGATION = "negation" + + _quadratic = Group("Quadratic") FREEZE = "freeze" FREEZE_REFLECT = "freeze_reflect" GLOW = "glow" From 2a64106e625c36c61f083732da18172f0b732f09 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 22/69] Documentation and code quality changes --- shortcut_composer/actions.py | 15 +++--- .../templates/multiple_assignment.py | 49 +++++++++-------- shortcut_composer/templates/pie_menu.py | 50 ++++++++++-------- .../templates/pie_menu_utils/edit_mode.py | 5 +- .../templates/pie_menu_utils/pie_widget.py | 52 +++++++++---------- .../pie_widget_utils/pie_painter.py | 1 - shortcut_composer/templates/temporary_key.py | 4 +- 7 files changed, 93 insertions(+), 83 deletions(-) diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index f8ed3c4e..b88dde00 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -124,7 +124,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ # Scroll timeline by sliding the cursor horizontally or # animated layers by sliding it vertically - + # # Use TemporaryOn instruction to temporarily isolate active layer templates.CursorTracker( name="Scroll timeline or animated layers", @@ -141,7 +141,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ # Scroll brush sizes by sliding the cursor horizontally or # brush opacity layers by sliding it vertically - + # # Opacity is contiguous from 10% to 100%, sizes come from a list # Switch 1% of opacity every 5 px (instead of default 50 px) templates.CursorTracker( @@ -179,7 +179,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ ), ), - # Use pie menu to pick one of the sporadically used tools. + # Use pie menu to pick one of the tools. templates.PieMenu( name="Pick misc tools", controller=controllers.ToolController(), @@ -242,7 +242,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ ] ), - # Use pie menu to pick one of presets from tag specified in settings. + # Use pie menu to pick one of stored presets. # Set tool to FREEHAND BRUSH if current tool does not allow to paint templates.PieMenu( name="Pick brush presets (red)", @@ -253,7 +253,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ active_color=QColor(200, 70, 70), ), - # Use pie menu to pick one of presets from tag specified in settings. + # Use pie menu to pick one of stored presets. # Set tool to FREEHAND BRUSH if current tool does not allow to paint templates.PieMenu( name="Pick brush presets (green)", @@ -264,7 +264,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ active_color=QColor(70, 200, 70), ), - # Use pie menu to pick one of presets from tag specified in settings. + # Use pie menu to pick one of stored presets. # Set tool to FREEHAND BRUSH if current tool does not allow to paint templates.PieMenu( name="Pick brush presets (blue)", @@ -275,6 +275,9 @@ def create_actions() -> List[templates.RawInstructions]: return [ active_color=QColor(110, 160, 235), ), + # Use pie menu to pick one of stored presets. + # By default, preset names are stored in .kra document. + # Set tool to FREEHAND BRUSH if current tool does not allow to paint templates.PieMenu( name="Pick local brush presets", controller=controllers.PresetController(), diff --git a/shortcut_composer/templates/multiple_assignment.py b/shortcut_composer/templates/multiple_assignment.py index 97711f5a..8f5efc03 100644 --- a/shortcut_composer/templates/multiple_assignment.py +++ b/shortcut_composer/templates/multiple_assignment.py @@ -19,7 +19,7 @@ class MultipleAssignment(RawInstructions, Generic[T]): Action cycles the values in `values_to_cycle` list: - short key press moves to next value in list. - if current value does not belong to the list, start from beginning - - when the list is exhausted, start again + - when the list is exhausted, start from beginning - end of long press ensures `default value` ### Arguments: @@ -72,53 +72,56 @@ def __init__( super().__init__(name, instructions, short_vs_long_press_time) self._controller = controller - self._default_value = self._read_default_value(default_value) - self.config = Field( + self._config = Field( config_group=f"ShortcutComposer: {self.name}", name="Values", default=values) + self._config.register_callback(self._reset) self._settings = SettingsHandler( self.name, - self.config, + self._config, self._instructions) - self._values_to_cycle = self.config.read() - - def reset() -> None: - self._values_to_cycle = self.config.read() - self._reset_iterator() - - self.config.register_callback(reset) + self._default_value = self._read_default_value(default_value) + self._values_to_cycle = self._config.read() + self._iterator = self._reset_iterator() self._last_value: Optional[T] = None - self._iterator: Iterator[T] def on_key_press(self) -> None: - """Use key press event only for switching to first value.""" - self._controller.refresh() + """Switch to the next value when values are being cycled.""" super().on_key_press() - if self._controller.get_value() != self._last_value: - self._reset_iterator() # NOTE: When there are no values to cycle, iterator is invalid - if self._values_to_cycle: - self._set_value(next(self._iterator)) + if not self._values_to_cycle: + return + + self._controller.refresh() + if self._controller.get_value() != self._last_value: + self._iterator = self._reset_iterator() + + self._set_value(next(self._iterator)) def on_long_key_release(self) -> None: - """Long releases set default value.""" + """Set default value.""" super().on_long_key_release() self._set_value(self._default_value) - self._reset_iterator() + self._iterator = self._reset_iterator() def _set_value(self, value: T) -> None: """Set the value using the controller, and remember it.""" self._last_value = value self._controller.set_value(value) - def _reset_iterator(self) -> None: - """Replace the iterator with new cyclic iterator over cycled values.""" - self._iterator = cycle(self._values_to_cycle) + def _reset(self) -> None: + """Reload values from config and start cycling from beginning.""" + self._values_to_cycle = self._config.read() + self._iterator = self._reset_iterator() + + def _reset_iterator(self) -> Iterator[T]: + """Return a new cyclic iterator for values to cycle.""" + return cycle(self._values_to_cycle) def _read_default_value(self, value: Optional[T]) -> T: """Read value from controller if it was not given.""" diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index c332d66d..16fadda4 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -29,10 +29,10 @@ class PieMenu(RawInstructions, Generic[T]): Pick value by hovering over a pie menu widget. - Widget is displayed under the cursor between key press and release - - Moving mouse in a direction of a value activates in on key release + - Moving mouse in a direction of a value activates it on key release - When the mouse was not moved past deadzone, value is not changed - Edit button activates mode in which pie does not hide on key - release and can be configured + release and can be configured (see PieSettings) ### Arguments: @@ -99,15 +99,16 @@ def __init__( @cached_property def pie_widget(self) -> PieWidget: - """Qwidget of the Pie for selecting values.""" + """Create Qwidget of the Pie for selecting values.""" return PieWidget( style=self._style, labels=self._labels, + edit_mode=self._edit_mode, config=self._config) @cached_property def pie_settings(self) -> PieSettings: - """Create and return the right settings based on labels type.""" + """Create QWidget with pie settings right for given type of labels.""" return dispatch_pie_settings(self._controller)( config=self._config, style=self._style, @@ -115,12 +116,12 @@ def pie_settings(self) -> PieSettings: @cached_property def pie_manager(self) -> PieManager: - """Manager which shows, hides and moves Pie widget and its settings.""" + """Create Manager which shows, hides and moves the Pie.""" return PieManager(pie_widget=self.pie_widget) @cached_property def settings_button(self): - """Button with which user can enter the edit mode.""" + """Create button with which user can enter the edit mode.""" settings_button = PieButton( icon=Krita.get_icon("properties"), icon_scale=1.1, @@ -133,7 +134,7 @@ def settings_button(self): @cached_property def accept_button(self): - """Button displayed in edit mode, which allows to hide the pie.""" + """Create button displayed in edit mode, for hiding the pie.""" accept_button = PieButton( icon=Krita.get_icon("dialog-ok"), icon_scale=1.5, @@ -166,22 +167,6 @@ def on_key_press(self) -> None: self.pie_manager.start() - def on_every_key_release(self) -> None: - """ - Handle the key release event. - - Ignore if in edit mode. Otherwise, stop the manager and set the - selected value if deadzone was reached. - """ - super().on_every_key_release() - - if self._edit_mode.get(): - return - - self.pie_manager.stop() - if label := self.pie_widget.active: - self._controller.set_value(label.value) - INVALID_VALUES: 'set[T]' = set() def _reset_labels(self) -> None: @@ -207,3 +192,22 @@ def _reset_labels(self) -> None: self.INVALID_VALUES.add(value) self._config.refresh_order() + + def on_every_key_release(self) -> None: + """ + Handle the key release event. + + In normal mode: + close pie, and set selected value if deadzone was reached + In edit mode: + ignore input + + """ + super().on_every_key_release() + + if self._edit_mode.get(): + return + + self.pie_manager.stop() + if label := self.pie_widget.active: + self._controller.set_value(label.value) diff --git a/shortcut_composer/templates/pie_menu_utils/edit_mode.py b/shortcut_composer/templates/pie_menu_utils/edit_mode.py index 8c625619..0951e593 100644 --- a/shortcut_composer/templates/pie_menu_utils/edit_mode.py +++ b/shortcut_composer/templates/pie_menu_utils/edit_mode.py @@ -39,7 +39,6 @@ def set_edit_mode_true(self): """Set the edit mode on.""" self._obj.pie_manager.stop(hide=False) self._obj.pie_widget.set_draggable(True) - self._obj.pie_widget.is_edit_mode = True self._obj.pie_widget.repaint() self._obj.pie_settings.show() self._obj.pie_settings.resize(self._obj.pie_settings.sizeHint()) @@ -61,7 +60,6 @@ def set_edit_mode_false(self): """Set the edit mode off.""" self._obj.pie_widget.hide() self._obj.pie_widget.set_draggable(False) - self._obj.pie_widget.is_edit_mode = False self._obj.pie_settings.hide() self._obj.accept_button.hide() self._obj.settings_button.show() @@ -69,3 +67,6 @@ def set_edit_mode_false(self): def swap_mode(self): """Change the edit mode to the other one.""" self.set(not self._edit_mode) + + def __bool__(self) -> bool: + return self.get() diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget.py b/shortcut_composer/templates/pie_menu_utils/pie_widget.py index 638c7bd3..3632029a 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget.py @@ -12,6 +12,7 @@ from api_krita.pyqt import Painter, AnimatedWidget, BaseWidget from composer_utils import Config +from .edit_mode import EditMode from .pie_style import PieStyle from .label import Label from .label_widget import LabelWidget @@ -29,14 +30,9 @@ class PieWidget(AnimatedWidget, BaseWidget, Generic[T]): """ PyQt5 widget with icons on ring that can be selected by hovering. - Methods inherits from QWidget used by other components: - - show() - displays the widget - - hide() - hides the widget - - repaint() - updates widget display after its data was changed - - It uses LabelHolder to store children widgets representing the - values user can pick. When the pie enters the edit mode, its - children become draggable. + Uses LabelHolder to store children widgets representing available + values. When the pie enters the edit mode, its children become + draggable. By dragging children, user can change their order or remove them by moving them out of the widget. New children can be added by @@ -47,9 +43,10 @@ def __init__( self, style: PieStyle, labels: List[Label[T]], + edit_mode: EditMode, config: PieConfig, parent=None - ): + ) -> None: AnimatedWidget.__init__(self, parent, Config.PIE_ANIMATION_TIME.read()) self.setGeometry(0, 0, style.widget_radius*2, style.widget_radius*2) @@ -66,11 +63,11 @@ def __init__( self._style = style self._labels = labels + self._edit_mode = edit_mode self.config = config self.config.register_callback(self._reset) self.active: Optional[Label] = None - self.is_edit_mode = False self._last_widget = None self.label_holder = LabelHolder( @@ -95,11 +92,11 @@ def deadzone(self) -> float: def paintEvent(self, event: QPaintEvent) -> None: """Paint the entire widget using the Painter wrapper.""" with Painter(self, event) as painter: - PiePainter(painter, self._labels, self._style, self.is_edit_mode) + PiePainter(painter, self._labels, self._style) def dragEnterEvent(self, e: QDragEnterEvent) -> None: """Allow dragging the widgets while in edit mode.""" - if self.is_edit_mode: + if self._edit_mode: return e.accept() e.ignore() @@ -107,46 +104,49 @@ def dragMoveEvent(self, e: QDragMoveEvent) -> None: """Handle all children actions - order change, add and remove.""" e.accept() source_widget = e.source() - pos = e.pos() + label = source_widget.label circle_points = CirclePoints( center=self.center, radius=self._style.pie_radius) - distance = circle_points.distance(pos) + distance = circle_points.distance(e.pos()) if not isinstance(source_widget, LabelWidget): # Drag incoming from outside the PieWidget ecosystem return - if self.type and not isinstance(source_widget.label.value, self.type): + if self._type and not isinstance(label.value, self._type): # Label type does not match the type of pie menu return self._last_widget = source_widget if distance > self._style.widget_radius: # Dragged out of the PieWidget - return self.label_holder.remove(source_widget.label) + return self.label_holder.remove(label) if not self._labels: # First label dragged to empty pie - return self.label_holder.insert(0, source_widget.label) + return self.label_holder.insert(0, label) if distance < self._style.deadzone_radius: # Do nothing in deadzone return - angle = circle_points.angle_from_point(pos) + angle = circle_points.angle_from_point(e.pos()) _a = self._widget_holder.on_angle(angle) - if source_widget.label not in self.label_holder or not self._labels: + if label not in self.label_holder or not self._labels: # Dragged with unknown label index = self.label_holder.index(_a.label) - return self.label_holder.insert(index, source_widget.label) + return self.label_holder.insert(index, label) + + _b = self._widget_holder.on_label(label) + if _a == _b: + # Dragged over the same widget + return - _b = self._widget_holder.on_label(source_widget.label) - if _a != _b: - # Dragged existing label to a new location - self.label_holder.swap(_a.label, _b.label) - self.repaint() + # Dragged existing label to a new location + self.label_holder.swap(_a.label, _b.label) + self.repaint() def dragLeaveEvent(self, e: QDragLeaveEvent) -> None: """Remove the label when its widget is dragged out.""" @@ -165,7 +165,7 @@ def _widget_holder(self) -> WidgetHolder: return self.label_holder.widget_holder @property - def type(self) -> Optional[type]: + def _type(self) -> Optional[type]: """Return type of values stored in labels. None if no labels.""" if not self._labels: return None diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index fe7be481..42313531 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -19,7 +19,6 @@ class PiePainter: painter: Painter labels: List[Label] style: PieStyle - edit_mode: bool def __post_init__(self): """Paint the widget which created the passed painter.""" diff --git a/shortcut_composer/templates/temporary_key.py b/shortcut_composer/templates/temporary_key.py index 3ab2e505..c0cdcca3 100644 --- a/shortcut_composer/templates/temporary_key.py +++ b/shortcut_composer/templates/temporary_key.py @@ -72,11 +72,11 @@ def __init__( self._was_high_before_press = False def _set_low(self) -> None: - """Defines how to switch to low state.""" + """Switch to low state.""" self._controller.set_value(self._low_value) def _set_high(self) -> None: - """Defines how to switch to high state.""" + """Switch to high state.""" self._controller.set_value(self._high_value) def _is_high_state(self) -> bool: From c03591434b7dedde95bb9045d5e1e4762589b7ef Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 23/69] Removed compatibility fix of 1.2 --- shortcut_composer/__init__.py | 6 ++-- .../composer_utils/compatibility_fix.py | 36 ------------------- 2 files changed, 2 insertions(+), 40 deletions(-) delete mode 100644 shortcut_composer/composer_utils/compatibility_fix.py diff --git a/shortcut_composer/__init__.py b/shortcut_composer/__init__.py index fb94de0c..f8149c85 100755 --- a/shortcut_composer/__init__.py +++ b/shortcut_composer/__init__.py @@ -14,10 +14,8 @@ sys.path.append(directory := os.path.dirname(__file__)) -from .shortcut_composer import ShortcutComposer -from .api_krita import Krita -from .composer_utils.compatibility_fix import fix_config -fix_config() +from .shortcut_composer import ShortcutComposer # noqa +from .api_krita import Krita # noqa Krita.add_extension(ShortcutComposer) sys.path.remove(directory) diff --git a/shortcut_composer/composer_utils/compatibility_fix.py b/shortcut_composer/composer_utils/compatibility_fix.py deleted file mode 100644 index ef22ddf2..00000000 --- a/shortcut_composer/composer_utils/compatibility_fix.py +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus -# SPDX-License-Identifier: GPL-3.0-or-later - -from api_krita import Krita - - -def fix_config(): - """Rewrites config values from their position in 1.1.1 to 1.2.0.""" - def fix(group: str, old_name: str, new_name: str): - if Krita.read_setting(group, new_name, "not given") != "not given": - return - value = Krita.read_setting("ShortcutComposer", old_name, "not given") - if value != "not given": - Krita.write_setting(group, new_name, value) - - data = ( - ("Pick brush presets (red)", "Tag (red)", "Tag"), - ("Pick brush presets (green)", "Tag (green)", "Tag"), - ("Pick brush presets (blue)", "Tag (blue)", "Tag"), - - ("Pick brush presets (red)", "Tag (red) values", "Values"), - ("Pick brush presets (green)", "Tag (green) values", "Values"), - ("Pick brush presets (blue)", "Tag (blue) values", "Values"), - - ("Pick painting blending modes", "Blending modes values", "Values"), - ("Pick misc tools", "Misc tools values", "Values"), - ("Cycle selection tools", "Selection tools values", "Values"), - ("Pick transform tool modes", "Transform modes values", "Values"), - ( - "Create painting layer with blending mode", - "Create blending layer values", - "Values"), - ) - - for group, old_name, new_name in data: - fix(f"ShortcutComposer: {group}", old_name, new_name) From 9efb91958627e64aa2136db6b2c7e313e3b49287 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 24/69] WIP: deadzone strategies --- shortcut_composer/actions.py | 2 + shortcut_composer/data_components/__init__.py | 2 + .../data_components/deadzone_strategy.py | 7 ++++ shortcut_composer/templates/pie_menu.py | 17 +++++--- .../templates/pie_menu_utils/__init__.py | 2 + .../templates/pie_menu_utils/actuator.py | 41 +++++++++++++++++++ .../templates/pie_menu_utils/pie_config.py | 2 + .../pie_config_impl/non_preset_pie_config.py | 3 ++ .../pie_config_impl/preset_pie_config.py | 4 +- 9 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 shortcut_composer/data_components/deadzone_strategy.py create mode 100644 shortcut_composer/templates/pie_menu_utils/actuator.py diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index b88dde00..d5dbf6a0 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -18,6 +18,7 @@ from core_components import instructions, controllers from data_components import ( CurrentLayerStack, + DeadzoneStrategy, PickStrategy, Slider, Range, @@ -232,6 +233,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ templates.PieMenu( name="Pick transform tool modes", controller=controllers.TransformModeController(), + deadzone_strategy=DeadzoneStrategy.ACTIVATE_TOP, values=[ TransformMode.FREE, TransformMode.PERSPECTIVE, diff --git a/shortcut_composer/data_components/__init__.py b/shortcut_composer/data_components/__init__.py index db2012d5..7d660120 100644 --- a/shortcut_composer/data_components/__init__.py +++ b/shortcut_composer/data_components/__init__.py @@ -10,6 +10,7 @@ """ from .current_layer_stack import CurrentLayerStack +from .deadzone_strategy import DeadzoneStrategy from .pick_strategy import PickStrategy from .slider import Slider from .range import Range @@ -17,6 +18,7 @@ __all__ = [ "CurrentLayerStack", + "DeadzoneStrategy", "PickStrategy", "Slider", "Range", diff --git a/shortcut_composer/data_components/deadzone_strategy.py b/shortcut_composer/data_components/deadzone_strategy.py new file mode 100644 index 00000000..5be861ce --- /dev/null +++ b/shortcut_composer/data_components/deadzone_strategy.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class DeadzoneStrategy(Enum): + DO_NOTHING = "Do nothing" + ACTIVATE_TOP = "Activate top" + ACTIVATE_LAST = "Activate last" diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 16fadda4..4ded5242 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -8,6 +8,7 @@ from PyQt5.QtGui import QColor from api_krita import Krita +from data_components import DeadzoneStrategy from core_components import Controller, Instruction from .pie_menu_utils.pie_settings_impl import dispatch_pie_settings from .pie_menu_utils.pie_config_impl import dispatch_pie_config @@ -16,6 +17,7 @@ PieManager, PieWidget, PieButton, + Actuator, EditMode, PieStyle, Label) @@ -78,6 +80,7 @@ def __init__( background_color: Optional[QColor] = None, active_color: QColor = QColor(100, 150, 230, 255), save_local: bool = False, + deadzone_strategy: DeadzoneStrategy = DeadzoneStrategy.DO_NOTHING, short_vs_long_press_time: Optional[float] = None ) -> None: super().__init__(name, instructions, short_vs_long_press_time) @@ -90,12 +93,17 @@ def __init__( icon_radius_scale=icon_radius_scale, save_local=save_local, background_color=background_color, - active_color=active_color) + active_color=active_color, + deadzone_strategy=deadzone_strategy) self._config.ORDER.register_callback(self._reset_labels) self._labels: List[Label] = [] self._edit_mode = EditMode(self) self._style = PieStyle(items=self._labels, pie_config=self._config) + self._actuator = Actuator( + controller=self._controller, + strategy_field=self._config.DEADZONE_STRATEGY, + labels=self._labels) @cached_property def pie_widget(self) -> PieWidget: @@ -205,9 +213,8 @@ def on_every_key_release(self) -> None: """ super().on_every_key_release() - if self._edit_mode.get(): + if self._edit_mode: return - self.pie_manager.stop() - if label := self.pie_widget.active: - self._controller.set_value(label.value) + + self._actuator.activate(self.pie_widget.active) diff --git a/shortcut_composer/templates/pie_menu_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/__init__.py index b4ef880e..cbf214c5 100644 --- a/shortcut_composer/templates/pie_menu_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/__init__.py @@ -12,6 +12,7 @@ from .pie_style import PieStyle from .edit_mode import EditMode from .label import Label +from .actuator import Actuator __all__ = [ "PieSettings", @@ -23,4 +24,5 @@ "PieStyle", "EditMode", "Label", + "Actuator", ] diff --git a/shortcut_composer/templates/pie_menu_utils/actuator.py b/shortcut_composer/templates/pie_menu_utils/actuator.py new file mode 100644 index 00000000..d612ff52 --- /dev/null +++ b/shortcut_composer/templates/pie_menu_utils/actuator.py @@ -0,0 +1,41 @@ +from typing import Optional, List +from core_components import Controller +from config_system import Field +from .label import Label +from data_components import DeadzoneStrategy + + +class Actuator: + def __init__( + self, + controller: Controller, + strategy_field: Field, + labels: List[Label] + ) -> None: + self._controller = controller + self._last_label: Optional[Label] = None + self._labels = labels + + def update_strategy(): + self._current_strategy = strategy_field.read() + self._current_strategy: DeadzoneStrategy + strategy_field.register_callback(update_strategy) + update_strategy() + + def activate(self, active: Optional[Label]): + if active is not None: + # Out of deadzone, label picked + self._controller.set_value(active.value) + self._last_label = active + return + + if self._last_label is None: + return + + # In deadzone + if self._current_strategy == DeadzoneStrategy.DO_NOTHING: + pass + elif self._current_strategy == DeadzoneStrategy.ACTIVATE_TOP: + self._controller.set_value(self._labels[0].value) + elif self._current_strategy == DeadzoneStrategy.ACTIVATE_LAST: + self._controller.set_value(self._last_label.value) # type: ignore diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index bdffa302..71040509 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -6,6 +6,7 @@ from PyQt5.QtGui import QColor from config_system import Field, FieldGroup from config_system.field_base_impl import DualField, FieldWithEditableDefault +from data_components import DeadzoneStrategy T = TypeVar("T") U = TypeVar("U") @@ -26,6 +27,7 @@ def __init__(self, name, *args, **kwargs) -> None: active_color: QColor SAVE_LOCAL: Field[bool] + DEADZONE_STRATEGY: Field[DeadzoneStrategy] ORDER: FieldWithEditableDefault[List[T], DualField[List[T]]] PIE_RADIUS_SCALE: Field[float] ICON_RADIUS_SCALE: Field[float] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py index ffbe150d..ff073dfc 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -3,6 +3,7 @@ from typing import List, Callable, Generic, TypeVar, Optional from PyQt5.QtGui import QColor +from data_components import DeadzoneStrategy from ..pie_config import PieConfig T = TypeVar("T") @@ -20,6 +21,7 @@ def __init__( save_local: bool, background_color: Optional[QColor], active_color: QColor, + deadzone_strategy: DeadzoneStrategy ) -> None: super().__init__(name) @@ -27,6 +29,7 @@ def __init__( self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) self.SAVE_LOCAL = self.field("Save local", save_local) + self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) self.ORDER = self._create_editable_dual_field("Values", values) self.background_color = background_color diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py index d1b95cbb..4e2800de 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -4,7 +4,7 @@ from typing import List, Callable, Optional, Union from PyQt5.QtGui import QColor from config_system import Field -from data_components import Tag +from data_components import Tag, DeadzoneStrategy from ..pie_config import PieConfig @@ -25,6 +25,7 @@ def __init__( save_local: bool, background_color: Optional[QColor], active_color: QColor, + deadzone_strategy: DeadzoneStrategy ) -> None: super().__init__(name) @@ -32,6 +33,7 @@ def __init__( self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) self.SAVE_LOCAL = self.field("Save local", save_local) + self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) tag_mode = isinstance(values, Tag) tag_name = values.tag_name if isinstance(values, Tag) else "" From 026e076d4b5f01b07bf2d850adc2c766fc4408e8 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 25/69] Add combobox for picking DeadzoneStrategy --- .../config_system/ui/__init__.py | 5 +- shortcut_composer/config_system/ui/widgets.py | 46 ++++++++++++++++++- .../data_components/deadzone_strategy.py | 2 +- .../templates/pie_menu_utils/actuator.py | 2 +- .../templates/pie_menu_utils/pie_settings.py | 23 ++++++++-- 5 files changed, 68 insertions(+), 10 deletions(-) diff --git a/shortcut_composer/config_system/ui/__init__.py b/shortcut_composer/config_system/ui/__init__.py index 957f79c9..624e33dc 100644 --- a/shortcut_composer/config_system/ui/__init__.py +++ b/shortcut_composer/config_system/ui/__init__.py @@ -18,11 +18,12 @@ from .config_based_widget import ConfigBasedWidget from .config_form_widget import ConfigFormWidget -from .widgets import ConfigComboBox, ConfigSpinBox +from .widgets import ConfigComboBox, ConfigSpinBox, EnumComboBox __all__ = [ "ConfigBasedWidget", "ConfigFormWidget", "ConfigComboBox", - "ConfigSpinBox" + "ConfigSpinBox", + "EnumComboBox", ] diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index 021aef0c..08c0927e 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -1,13 +1,15 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Any, List, Final, Optional, TypeVar, Generic, Protocol +from enum import Enum +from typing import Any, List, Final, Optional, TypeVar, Generic, Protocol, Type from PyQt5.QtWidgets import QDoubleSpinBox, QComboBox, QSpinBox, QWidget from ..field import Field from .config_based_widget import ConfigBasedWidget F = TypeVar("F", bound=float) +E = TypeVar("E", bound=Enum) class SpinBox(Protocol, Generic[F]): @@ -96,7 +98,47 @@ def set(self, value: str): return self._combo_box.setCurrentText(value) def _init_combo_box(self) -> QComboBox: - """Return the spinbox widget.""" + """Return the combobox widget.""" + combo_box = QComboBox() + combo_box.setObjectName(self.config_field.name) + return combo_box + + +class EnumComboBox(ConfigBasedWidget[E]): + """ + Wrapper of Combobox linked to a Enum configutation field. + + Allows to pick one of enum members. + """ + + def __init__( + self, + config_field: Field[E], + enum_type: Type[E], + parent: Optional[QWidget] = None, + pretty_name: Optional[str] = None, + ) -> None: + super().__init__(config_field, parent, pretty_name) + self._enum_type = enum_type + self._combo_box = self._init_combo_box() + self.widget: Final[QComboBox] = self._combo_box + + keys = list(self._enum_type._value2member_map_.keys()) + self._combo_box.addItems(keys) + + self.reset() + + def read(self) -> E: + """Return Enum member selected with the combobox.""" + text = self._combo_box.currentText() + return self._enum_type(text) + + def set(self, value: E): + """Set the combobox to given Enum member.""" + return self._combo_box.setCurrentText(value.value) + + def _init_combo_box(self) -> QComboBox: + """Return the combobox widget.""" combo_box = QComboBox() combo_box.setObjectName(self.config_field.name) return combo_box diff --git a/shortcut_composer/data_components/deadzone_strategy.py b/shortcut_composer/data_components/deadzone_strategy.py index 5be861ce..d3a31a2e 100644 --- a/shortcut_composer/data_components/deadzone_strategy.py +++ b/shortcut_composer/data_components/deadzone_strategy.py @@ -4,4 +4,4 @@ class DeadzoneStrategy(Enum): DO_NOTHING = "Do nothing" ACTIVATE_TOP = "Activate top" - ACTIVATE_LAST = "Activate last" + ACTIVATE_PREVIOUS = "Activate previous" diff --git a/shortcut_composer/templates/pie_menu_utils/actuator.py b/shortcut_composer/templates/pie_menu_utils/actuator.py index d612ff52..abc47834 100644 --- a/shortcut_composer/templates/pie_menu_utils/actuator.py +++ b/shortcut_composer/templates/pie_menu_utils/actuator.py @@ -37,5 +37,5 @@ def activate(self, active: Optional[Label]): pass elif self._current_strategy == DeadzoneStrategy.ACTIVATE_TOP: self._controller.set_value(self._labels[0].value) - elif self._current_strategy == DeadzoneStrategy.ACTIVATE_LAST: + elif self._current_strategy == DeadzoneStrategy.ACTIVATE_PREVIOUS: self._controller.set_value(self._last_label.value) # type: ignore diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index f85bdefc..535f76e8 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -14,8 +14,9 @@ from api_krita import Krita from api_krita.pyqt import AnimatedWidget, BaseWidget, SafeConfirmButton -from config_system.ui import ConfigFormWidget, ConfigSpinBox +from config_system.ui import ConfigFormWidget, ConfigSpinBox, EnumComboBox from composer_utils import Config +from data_components import DeadzoneStrategy from .pie_style import PieStyle from .pie_config import PieConfig @@ -56,9 +57,23 @@ def __init__( self._tab_holder = QTabWidget() self._local_settings = ConfigFormWidget([ - ConfigSpinBox(config.PIE_RADIUS_SCALE, self, "Pie scale", 0.05, 4), - ConfigSpinBox(config.ICON_RADIUS_SCALE, self, "Icon max scale", - 0.05, 4), + EnumComboBox( + config_field=config.DEADZONE_STRATEGY, + parent=self, + pretty_name="On deadzone", + enum_type=DeadzoneStrategy), + ConfigSpinBox( + config_field=config.PIE_RADIUS_SCALE, + parent=self, + pretty_name="Pie scale", + step=0.05, + max_value=4), + ConfigSpinBox( + config_field=config.ICON_RADIUS_SCALE, + parent=self, + pretty_name="Icon max scale", + step=0.05, + max_value=4), ]) self._tab_holder.addTab(self._local_settings, "Preferences") self._tab_holder.addTab(LocationTab(self._config), "Save location") From 6edb1fea89b5a30a1c0a49d1e5bcb16294e6fc7e Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 26/69] Make label look different when it is about to be activated in deadzone --- shortcut_composer/actions.py | 2 +- .../data_components/deadzone_strategy.py | 9 ++- shortcut_composer/templates/pie_menu.py | 5 +- .../templates/pie_menu_utils/actuator.py | 59 ++++++++++++++++--- .../templates/pie_menu_utils/edit_mode.py | 1 + .../templates/pie_menu_utils/label_widget.py | 16 +++++ .../templates/pie_menu_utils/pie_widget.py | 6 +- .../pie_widget_utils/widget_holder.py | 5 ++ 8 files changed, 88 insertions(+), 15 deletions(-) diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index d5dbf6a0..a0e1714e 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -233,7 +233,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ templates.PieMenu( name="Pick transform tool modes", controller=controllers.TransformModeController(), - deadzone_strategy=DeadzoneStrategy.ACTIVATE_TOP, + deadzone_strategy=DeadzoneStrategy.PICK_TOP, values=[ TransformMode.FREE, TransformMode.PERSPECTIVE, diff --git a/shortcut_composer/data_components/deadzone_strategy.py b/shortcut_composer/data_components/deadzone_strategy.py index d3a31a2e..62e794c1 100644 --- a/shortcut_composer/data_components/deadzone_strategy.py +++ b/shortcut_composer/data_components/deadzone_strategy.py @@ -2,6 +2,11 @@ class DeadzoneStrategy(Enum): + """ + Enumeration of actions that can be done on deadzone key release. + + Values are strings meant for being displayed in the UI. + """ DO_NOTHING = "Do nothing" - ACTIVATE_TOP = "Activate top" - ACTIVATE_PREVIOUS = "Activate previous" + PICK_TOP = "Pick top" + PICK_PREVIOUS = "Pick previous" diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 4ded5242..9710e6ce 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -100,7 +100,7 @@ def __init__( self._labels: List[Label] = [] self._edit_mode = EditMode(self) self._style = PieStyle(items=self._labels, pie_config=self._config) - self._actuator = Actuator( + self.actuator = Actuator( controller=self._controller, strategy_field=self._config.DEADZONE_STRATEGY, labels=self._labels) @@ -172,6 +172,7 @@ def on_key_press(self) -> None: self._reset_labels() self.pie_widget.label_holder.reset() # HACK: should be automatic self._move_buttons() + self.actuator.mark_selected_widget(self.pie_widget.widget_holder) self.pie_manager.start() @@ -217,4 +218,4 @@ def on_every_key_release(self) -> None: return self.pie_manager.stop() - self._actuator.activate(self.pie_widget.active) + self.actuator.activate(self.pie_widget.active) diff --git a/shortcut_composer/templates/pie_menu_utils/actuator.py b/shortcut_composer/templates/pie_menu_utils/actuator.py index abc47834..c288d3b6 100644 --- a/shortcut_composer/templates/pie_menu_utils/actuator.py +++ b/shortcut_composer/templates/pie_menu_utils/actuator.py @@ -1,11 +1,31 @@ from typing import Optional, List from core_components import Controller from config_system import Field -from .label import Label from data_components import DeadzoneStrategy +from .label import Label +from .pie_widget_utils import WidgetHolder class Actuator: + """ + Activates the correct labels from the Pie. + + When a valid label is given in `activate()` method, it us activated + and also remembered. + + When label is not given in `activate()` method, it means that user + closed the pie while still being in deadzone. + Then it is handled using the currently active strategy. + + Actuator tracks selected strategy using `strategy_field` passed on + initialization. It can be changed in runtime. + + Strategies: + DeadzoneStrategy.DO_NOTHING - no action is needed + DeadzoneStrategy.PICK_TOP - first label in list is activated + DeadzoneStrategy.PICK_PREVIOUS - remembered label is activated + """ + def __init__( self, controller: Controller, @@ -22,7 +42,9 @@ def update_strategy(): strategy_field.register_callback(update_strategy) update_strategy() - def activate(self, active: Optional[Label]): + def activate(self, active: Optional[Label]) -> None: + """Activate the correct label""" + if active is not None: # Out of deadzone, label picked self._controller.set_value(active.value) @@ -33,9 +55,32 @@ def activate(self, active: Optional[Label]): return # In deadzone + if self.selected_label is not None: + self._controller.set_value(self.selected_label.value) + + @property + def selected_label(self) -> Optional[Label]: + """Return label which should be picked on deadzone.""" if self._current_strategy == DeadzoneStrategy.DO_NOTHING: - pass - elif self._current_strategy == DeadzoneStrategy.ACTIVATE_TOP: - self._controller.set_value(self._labels[0].value) - elif self._current_strategy == DeadzoneStrategy.ACTIVATE_PREVIOUS: - self._controller.set_value(self._last_label.value) # type: ignore + return None + elif self._current_strategy == DeadzoneStrategy.PICK_TOP: + if self._labels: + return self._labels[0] + return None + elif self._current_strategy == DeadzoneStrategy.PICK_PREVIOUS: + if self._last_label in self._labels: + return self._last_label + return None + + def mark_selected_widget(self, widget_holder: WidgetHolder): + """Force color of the label that is selected for being picked.""" + widget_holder.clear_forced_widgets() + + if self.selected_label is None: + return + + try: + widget = widget_holder.on_label(self.selected_label) + except ValueError: + return + widget.forced = True diff --git a/shortcut_composer/templates/pie_menu_utils/edit_mode.py b/shortcut_composer/templates/pie_menu_utils/edit_mode.py index 0951e593..f89a071b 100644 --- a/shortcut_composer/templates/pie_menu_utils/edit_mode.py +++ b/shortcut_composer/templates/pie_menu_utils/edit_mode.py @@ -39,6 +39,7 @@ def set_edit_mode_true(self): """Set the edit mode on.""" self._obj.pie_manager.stop(hide=False) self._obj.pie_widget.set_draggable(True) + self._obj.pie_widget.widget_holder.clear_forced_widgets() self._obj.pie_widget.repaint() self._obj.pie_settings.show() self._obj.pie_settings.resize(self._obj.pie_settings.sizeHint()) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 1d78ac26..53ec46c9 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -43,6 +43,7 @@ def __init__( self._draggable = True self._enabled = True self._hovered = False + self._forced = False self._instructions: list[WidgetInstructions] = [] @@ -80,6 +81,19 @@ def enabled(self, value: bool) -> None: self.draggable = False self.repaint() + @property + def forced(self): + """Return whether the widget has forced active color.""" + return self._forced + + @forced.setter + def forced(self, value: bool) -> None: + """Make the widget look as it is active even if it is not.""" + if self._forced == value: + return + self._forced = value + self.repaint() + def move_to_label(self) -> None: """Move the widget according to current center of label it holds.""" self.move_center(self.label.center) @@ -117,6 +131,8 @@ def leaveEvent(self, e: QEvent) -> None: @property def _border_color(self): """Return border color which differs when enabled or hovered.""" + if self.forced: + return self._style.active_color if not self.enabled: return self._style.active_color_dark if self._hovered and self.draggable: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget.py b/shortcut_composer/templates/pie_menu_utils/pie_widget.py index 3632029a..a8735c14 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget.py @@ -132,14 +132,14 @@ def dragMoveEvent(self, e: QDragMoveEvent) -> None: return angle = circle_points.angle_from_point(e.pos()) - _a = self._widget_holder.on_angle(angle) + _a = self.widget_holder.on_angle(angle) if label not in self.label_holder or not self._labels: # Dragged with unknown label index = self.label_holder.index(_a.label) return self.label_holder.insert(index, label) - _b = self._widget_holder.on_label(label) + _b = self.widget_holder.on_label(label) if _a == _b: # Dragged over the same widget return @@ -160,7 +160,7 @@ def set_draggable(self, draggable: bool): widget.draggable = draggable @property - def _widget_holder(self) -> WidgetHolder: + def widget_holder(self) -> WidgetHolder: """Return the holder with child widgets.""" return self.label_holder.widget_holder diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py index 6bf779b8..1d3bda7a 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/widget_holder.py @@ -69,3 +69,8 @@ def __iter__(self) -> Iterator[LabelWidget]: def __len__(self) -> int: """Return amount of held LabelWidgets.""" return len(self._widgets) + + def clear_forced_widgets(self): + """Clear the forced colors of all held widgets. Helper method.""" + for widget in self._widgets.values(): + widget.forced = False From 4feb0749d9411bf4912fd9beb612645b4d657a7b Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:09:53 +0200 Subject: [PATCH 27/69] bugfix: allow PICK_TOP strategy to operate without last_value --- shortcut_composer/templates/pie_menu_utils/actuator.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/actuator.py b/shortcut_composer/templates/pie_menu_utils/actuator.py index c288d3b6..b08125c5 100644 --- a/shortcut_composer/templates/pie_menu_utils/actuator.py +++ b/shortcut_composer/templates/pie_menu_utils/actuator.py @@ -44,16 +44,12 @@ def update_strategy(): def activate(self, active: Optional[Label]) -> None: """Activate the correct label""" - if active is not None: # Out of deadzone, label picked self._controller.set_value(active.value) self._last_label = active return - if self._last_label is None: - return - # In deadzone if self.selected_label is not None: self._controller.set_value(self.selected_label.value) From 3077ca24f3b3adb057aacffd3651b1315e26f4fe Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:10:30 +0200 Subject: [PATCH 28/69] Remove __main__ code from EnumGroup file --- .../api_krita/enums/helpers/enum_group.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py index de76c257..c64e8bad 100644 --- a/shortcut_composer/api_krita/enums/helpers/enum_group.py +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -98,30 +98,3 @@ def format_member(self): start with a group separator. Otherwise, exception will be raised during class creation. """ - - -class Edible(EnumGroup): - _fruit = Group("Fruit") - APPLE = 0 - ORANGE = 1 - - _vegetable = Group("Vegetable") - TOMATO = 2 - POTATO = 3 - - def format_member(self): - return f"{self.name}_{self.value}" - - -# print(Edible.APPLE) -# print(Edible.APPLE.format_member()) -# print(Edible["APPLE"]) -# print(Edible(0)) -# print(Edible._fruit) -# print(Edible._vegetable) -# print(Edible._member_map_) -# print(Edible._value2member_map_) -# print(Edible.APPLE in Edible._fruit) # type: ignore -# print() -# for name, group in Edible._groups_.items(): -# print(name, group) From 7e6afefd84bbcd89be03e1f51960cf260f4d8c60 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:10:30 +0200 Subject: [PATCH 29/69] Read active_color from krita theme --- shortcut_composer/api_krita/core_api.py | 12 ++++++++++-- shortcut_composer/templates/pie_menu.py | 2 +- .../templates/pie_menu_utils/pie_config.py | 2 +- .../templates/pie_menu_utils/pie_style.py | 13 ++++++++----- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/shortcut_composer/api_krita/core_api.py b/shortcut_composer/api_krita/core_api.py index 08358070..ea47c9b6 100644 --- a/shortcut_composer/api_krita/core_api.py +++ b/shortcut_composer/api_krita/core_api.py @@ -9,7 +9,7 @@ QDesktopWidget, QWidgetAction, QMdiArea) -from PyQt5.QtGui import QKeySequence, QColor, QIcon +from PyQt5.QtGui import QKeySequence, QColor, QIcon, QPalette from PyQt5.QtCore import QTimer from .wrappers import ( @@ -129,10 +129,18 @@ def connect_callback(): self.main_window.themeChanged.connect(callback) QTimer.singleShot(1000, connect_callback) + def get_main_color_from_theme(self) -> QColor: + """Return main color of the current theme.""" + return qApp.palette().color(QPalette.Window) + + def get_active_color_from_theme(self) -> QColor: + """Return active color of the current theme.""" + return qApp.palette().color(QPalette.Highlight) + @property def is_light_theme_active(self) -> bool: """Return if currently set theme is light using it's main color.""" - main_color: QColor = qApp.palette().window().color() + main_color = self.get_main_color_from_theme() return main_color.value() > 128 diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 9710e6ce..3c24379c 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -78,7 +78,7 @@ def __init__( pie_radius_scale: float = 1.0, icon_radius_scale: float = 1.0, background_color: Optional[QColor] = None, - active_color: QColor = QColor(100, 150, 230, 255), + active_color: Optional[QColor] = QColor(100, 150, 230, 255), save_local: bool = False, deadzone_strategy: DeadzoneStrategy = DeadzoneStrategy.DO_NOTHING, short_vs_long_press_time: Optional[float] = None diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index 71040509..06d90d78 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -24,7 +24,7 @@ def __init__(self, name, *args, **kwargs) -> None: name: str """Name of field group.""" background_color: Optional[QColor] - active_color: QColor + active_color: Optional[QColor] SAVE_LOCAL: Field[bool] DEADZONE_STRATEGY: Field[DeadzoneStrategy] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 4be4d10e..d1be2b61 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -125,18 +125,21 @@ def accept_button_radius(self) -> int: * Config.PIE_DEADZONE_GLOBAL_SCALE.read()) @property - def active_color(self): + def active_color(self) -> QColor: """Color of active element.""" - return self._pie_config.active_color + if self._pie_config.active_color is not None: + return self._pie_config.active_color + else: + return Krita.get_active_color_from_theme() @property def background_color(self) -> QColor: """Color of base area. Depends on the app theme lightness""" if self._pie_config.background_color is not None: return self._pie_config.background_color - if Krita.is_light_theme_active: - return QColor(210, 210, 210, 190) - return QColor(75, 75, 75, 190) + bg_color = Krita.get_main_color_from_theme() + bg_color.setAlpha(190) + return bg_color @property def active_color_dark(self): From a2f8e30f66c4bf1d640fc24156de99b29ab07498 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:10:30 +0200 Subject: [PATCH 30/69] Button for selecting color stored in config --- .../field_base_impl/common_utils/parsers.py | 19 ++++++- .../config_system/ui/__init__.py | 3 +- shortcut_composer/config_system/ui/widgets.py | 50 ++++++++++++++++--- shortcut_composer/templates/pie_menu.py | 4 +- .../templates/pie_menu_utils/pie_config.py | 4 +- .../pie_config_impl/non_preset_pie_config.py | 8 +-- .../pie_config_impl/preset_pie_config.py | 8 +-- .../templates/pie_menu_utils/pie_settings.py | 14 +++++- .../templates/pie_menu_utils/pie_style.py | 10 ++-- 9 files changed, 94 insertions(+), 26 deletions(-) diff --git a/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py b/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py index eba8850e..06e3cf9d 100644 --- a/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py +++ b/shortcut_composer/config_system/field_base_impl/common_utils/parsers.py @@ -3,6 +3,7 @@ from typing import Generic, TypeVar, Type, Protocol from enum import Enum +from PyQt5.QtGui import QColor T = TypeVar("T") Basic = TypeVar("Basic", str, int, float) @@ -18,7 +19,8 @@ def dispatch_parser(parser_type: type) -> 'Parser': int: BasicParser(int), float: BasicParser(float), str: BasicParser(str), - bool: BoolParser() + bool: BoolParser(), + QColor: ColorParser() }[parser_type] @@ -80,3 +82,18 @@ def parse_to(self, value: str) -> EnumT: def parse_from(self, value: EnumT) -> str: """Parse from enum to string.""" return str(value.name) + + +class ColorParser(Parser[QColor]): + """Parses from string to QColor and vice-versa.""" + + type = QColor + + def parse_to(self, value: str) -> QColor: + """Parses from string to QColor.""" + element_list = value.split(",") + return QColor(*map(int, element_list)) + + def parse_from(self, value: QColor) -> str: + """Parses from QColor to string.""" + return f"{value.red()},{value.green()},{value.blue()},{value.alpha()}" diff --git a/shortcut_composer/config_system/ui/__init__.py b/shortcut_composer/config_system/ui/__init__.py index 624e33dc..c0c9b021 100644 --- a/shortcut_composer/config_system/ui/__init__.py +++ b/shortcut_composer/config_system/ui/__init__.py @@ -18,7 +18,7 @@ from .config_based_widget import ConfigBasedWidget from .config_form_widget import ConfigFormWidget -from .widgets import ConfigComboBox, ConfigSpinBox, EnumComboBox +from .widgets import ConfigComboBox, ConfigSpinBox, EnumComboBox, ColorButton __all__ = [ "ConfigBasedWidget", @@ -26,4 +26,5 @@ "ConfigComboBox", "ConfigSpinBox", "EnumComboBox", + "ColorButton", ] diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index 08c0927e..65ed607d 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -3,7 +3,14 @@ from enum import Enum from typing import Any, List, Final, Optional, TypeVar, Generic, Protocol, Type -from PyQt5.QtWidgets import QDoubleSpinBox, QComboBox, QSpinBox, QWidget +from PyQt5.QtWidgets import ( + QDoubleSpinBox, + QComboBox, + QSpinBox, + QWidget, + QPushButton, + QColorDialog) +from PyQt5.QtGui import QColor from ..field import Field from .config_based_widget import ConfigBasedWidget @@ -46,7 +53,7 @@ def read(self) -> F: """Return the current value of the spinbox widget.""" return self._spin_box.value() - def set(self, value: F): + def set(self, value: F) -> None: """Replace the value of the spinbox widget with passed one.""" self._spin_box.setValue(value) @@ -93,9 +100,9 @@ def read(self) -> str: """Return the current value of the ComboBox.""" return self._combo_box.currentText() - def set(self, value: str): + def set(self, value: str) -> None: """Replace the value of the ComboBox with passed one.""" - return self._combo_box.setCurrentText(value) + self._combo_box.setCurrentText(value) def _init_combo_box(self) -> QComboBox: """Return the combobox widget.""" @@ -133,12 +140,43 @@ def read(self) -> E: text = self._combo_box.currentText() return self._enum_type(text) - def set(self, value: E): + def set(self, value: E) -> None: """Set the combobox to given Enum member.""" - return self._combo_box.setCurrentText(value.value) + self._combo_box.setCurrentText(value.value) def _init_combo_box(self) -> QComboBox: """Return the combobox widget.""" combo_box = QComboBox() combo_box.setObjectName(self.config_field.name) return combo_box + + +class ColorButton(ConfigBasedWidget[QColor]): + def __init__( + self, + config_field: Field[QColor], + parent: Optional[QWidget] = None, + pretty_name: Optional[str] = None, + ) -> None: + super().__init__(config_field, parent, pretty_name) + self._button = self._init_button() + self._color = self.config_field.read() + self.widget: Final[QPushButton] = self._button + + self.reset() + + def read(self) -> QColor: + return self._color + + def set(self, value: QColor) -> None: + self._color = value + self._button.setStyleSheet( + f"background-color: {self._color.name()}; border: none") + + def _init_button(self) -> QPushButton: + def on_click(): + self.set(QColorDialog.getColor(self._color)) + + button = QPushButton("") + button.clicked.connect(on_click) + return button diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 3c24379c..015955c3 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -77,8 +77,8 @@ def __init__( instructions: Optional[List[Instruction]] = None, pie_radius_scale: float = 1.0, icon_radius_scale: float = 1.0, - background_color: Optional[QColor] = None, - active_color: Optional[QColor] = QColor(100, 150, 230, 255), + background_color: QColor = QColor(70, 70, 70, 190), + active_color: QColor = QColor(100, 150, 230, 255), save_local: bool = False, deadzone_strategy: DeadzoneStrategy = DeadzoneStrategy.DO_NOTHING, short_vs_long_press_time: Optional[float] = None diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index 06d90d78..805265ba 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -23,8 +23,8 @@ def __init__(self, name, *args, **kwargs) -> None: name: str """Name of field group.""" - background_color: Optional[QColor] - active_color: Optional[QColor] + BACKGROUND_COLOR: Field[QColor] + ACTIVE_COLOR: Field[QColor] SAVE_LOCAL: Field[bool] DEADZONE_STRATEGY: Field[DeadzoneStrategy] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py index ff073dfc..483c192f 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Callable, Generic, TypeVar, Optional +from typing import List, Callable, Generic, TypeVar from PyQt5.QtGui import QColor from data_components import DeadzoneStrategy from ..pie_config import PieConfig @@ -19,7 +19,7 @@ def __init__( pie_radius_scale: float, icon_radius_scale: float, save_local: bool, - background_color: Optional[QColor], + background_color: QColor, active_color: QColor, deadzone_strategy: DeadzoneStrategy ) -> None: @@ -32,8 +32,8 @@ def __init__( self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) self.ORDER = self._create_editable_dual_field("Values", values) - self.background_color = background_color - self.active_color = active_color + self.BACKGROUND_COLOR = self.field("Bg color", background_color) + self.ACTIVE_COLOR = self.field("Active color", active_color) self.allow_value_edit = True def values(self) -> List[T]: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py index 4e2800de..b62b82c1 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Callable, Optional, Union +from typing import List, Callable, Union from PyQt5.QtGui import QColor from config_system import Field from data_components import Tag, DeadzoneStrategy @@ -23,7 +23,7 @@ def __init__( pie_radius_scale: float, icon_radius_scale: float, save_local: bool, - background_color: Optional[QColor], + background_color: QColor, active_color: QColor, deadzone_strategy: DeadzoneStrategy ) -> None: @@ -41,8 +41,8 @@ def __init__( self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) self.ORDER = self._create_editable_dual_field("Values", [], str) - self.background_color = background_color - self.active_color = active_color + self.BACKGROUND_COLOR = self.field("Bg color", background_color) + self.ACTIVE_COLOR = self.field("Active color", active_color) @property def allow_value_edit(self) -> bool: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index 535f76e8..1598e18a 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -14,7 +14,11 @@ from api_krita import Krita from api_krita.pyqt import AnimatedWidget, BaseWidget, SafeConfirmButton -from config_system.ui import ConfigFormWidget, ConfigSpinBox, EnumComboBox +from config_system.ui import ( + ConfigFormWidget, + ConfigSpinBox, + EnumComboBox, + ColorButton) from composer_utils import Config from data_components import DeadzoneStrategy from .pie_style import PieStyle @@ -74,6 +78,14 @@ def __init__( pretty_name="Icon max scale", step=0.05, max_value=4), + ColorButton( + config_field=config.BACKGROUND_COLOR, + parent=self, + pretty_name="Background color"), + ColorButton( + config_field=config.ACTIVE_COLOR, + parent=self, + pretty_name="Active color"), ]) self._tab_holder.addTab(self._local_settings, "Preferences") self._tab_holder.addTab(LocationTab(self._config), "Save location") diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index d1be2b61..93b0ce38 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -127,16 +127,16 @@ def accept_button_radius(self) -> int: @property def active_color(self) -> QColor: """Color of active element.""" - if self._pie_config.active_color is not None: - return self._pie_config.active_color + if self._pie_config.ACTIVE_COLOR is not None: + return self._pie_config.ACTIVE_COLOR.read() else: return Krita.get_active_color_from_theme() @property def background_color(self) -> QColor: """Color of base area. Depends on the app theme lightness""" - if self._pie_config.background_color is not None: - return self._pie_config.background_color + if self._pie_config.BACKGROUND_COLOR is not None: + return self._pie_config.BACKGROUND_COLOR.read() bg_color = Krita.get_main_color_from_theme() bg_color.setAlpha(190) return bg_color @@ -175,4 +175,4 @@ def font_multiplier(self): "Windows": 0.11, "Darwin": 0.265, "": 0.125} - """Scale to fix different font sizes each OS..""" + """Scale to fix different font sizes each OS.""" From b9897a597791b249aa4c4924e576c3848a786909 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 31/69] Allow to set pie colors, use default, krita, change opacity --- .../composer_utils/global_config.py | 31 +++++++ .../composer_utils/settings_dialog.py | 86 ++++++++++++++++--- .../config_system/ui/__init__.py | 13 ++- shortcut_composer/config_system/ui/widgets.py | 61 +++++++++---- shortcut_composer/templates/pie_menu.py | 10 ++- .../templates/pie_menu_utils/pie_config.py | 3 + .../pie_config_impl/non_preset_pie_config.py | 10 +++ .../pie_config_impl/preset_pie_config.py | 16 +++- .../templates/pie_menu_utils/pie_settings.py | 24 +++++- .../common_utils/group_combo_box.py | 4 +- .../templates/pie_menu_utils/pie_style.py | 29 +++++-- 11 files changed, 235 insertions(+), 52 deletions(-) diff --git a/shortcut_composer/composer_utils/global_config.py b/shortcut_composer/composer_utils/global_config.py index b169b8d0..c16442c5 100644 --- a/shortcut_composer/composer_utils/global_config.py +++ b/shortcut_composer/composer_utils/global_config.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later from config_system import FieldGroup +from PyQt5.QtGui import QColor +from api_krita import Krita class GlobalConfig(FieldGroup): @@ -35,10 +37,39 @@ def __init__(self, name: str) -> None: "Pie deadzone global scale", 1.0) self.PIE_ANIMATION_TIME = self.field("Pie animation time", 0.2) + if not Krita.is_light_theme_active: + default_bg_color = QColor(75, 75, 75) + else: + default_bg_color = QColor(210, 210, 210) + self.USE_KRITA_THEME = self.field("Use krita theme", False) + self.DEFAULT_BACKGROUND_COLOR = self.field( + "Global bg color", default_bg_color) + self.DEFAULT_ACTIVE_COLOR = self.field( + "Global active color", QColor(100, 150, 230)) + self.DEFAULT_PIE_OPACITY = self.field("Global pie opacity", 75) + def get_sleep_time(self) -> int: """Read sleep time from FPS_LIMIT config field.""" fps_limit = self.FPS_LIMIT.read() return round(1000/fps_limit) if fps_limit else 1 + @property + def default_background_color(self) -> QColor: + """Color of pies, when the pie does not specify a custom one.""" + if self.USE_KRITA_THEME.read(): + bg_color = Krita.get_main_color_from_theme() + else: + bg_color = self.DEFAULT_BACKGROUND_COLOR.read() + opacity = self.DEFAULT_PIE_OPACITY.read() * 255 / 100 + bg_color.setAlpha(round(opacity)) + return bg_color + + @property + def default_active_color(self) -> QColor: + """Pie highlight color, when the pie does not specify a custom one.""" + if self.USE_KRITA_THEME.read(): + return Krita.get_active_color_from_theme() + return self.DEFAULT_ACTIVE_COLOR.read() + Config = GlobalConfig("ShortcutComposer") diff --git a/shortcut_composer/composer_utils/settings_dialog.py b/shortcut_composer/composer_utils/settings_dialog.py index 6ec5e36e..51530898 100644 --- a/shortcut_composer/composer_utils/settings_dialog.py +++ b/shortcut_composer/composer_utils/settings_dialog.py @@ -7,7 +7,7 @@ from INFO import __version__, __author__, __license__ from api_krita import Krita -from config_system.ui import ConfigFormWidget, ConfigSpinBox +from config_system.ui import ConfigFormWidget, SpinBox, ColorButton, Checkbox from .global_config import Config from .buttons_layout import ButtonsLayout @@ -25,19 +25,79 @@ def __init__(self) -> None: self._general_tab = ConfigFormWidget([ "Common settings", - ConfigSpinBox( - Config.SHORT_VS_LONG_PRESS_TIME, self, None, 0.05, 4), - ConfigSpinBox(Config.FPS_LIMIT, self, None, 5, 500), + SpinBox( + config_field=Config.SHORT_VS_LONG_PRESS_TIME, + parent=self, + pretty_name="Short vs long press time", + step=0.05, + max_value=4), + SpinBox( + config_field=Config.FPS_LIMIT, + parent=self, + pretty_name="FPS limit", + step=5, + max_value=50), + "Cursor trackers", - ConfigSpinBox( - Config.TRACKER_SENSITIVITY_SCALE, self, None, 0.05, 400), - ConfigSpinBox(Config.TRACKER_DEADZONE, self, None, 1, 200), - "Pie menus display", - ConfigSpinBox(Config.PIE_GLOBAL_SCALE, self, None, 0.05, 4), - ConfigSpinBox(Config.PIE_ICON_GLOBAL_SCALE, self, None, 0.05, 4), - ConfigSpinBox( - Config.PIE_DEADZONE_GLOBAL_SCALE, self, None, 0.05, 4), - ConfigSpinBox(Config.PIE_ANIMATION_TIME, self, None, 0.01, 1), + SpinBox( + Config.TRACKER_SENSITIVITY_SCALE, + parent=self, + pretty_name="Tracker sensitivity scale", + step=0.05, + max_value=400), + SpinBox( + Config.TRACKER_DEADZONE, + parent=self, + pretty_name="Tracker deadzone", + step=1, + max_value=20), + + "Pie menu size", + SpinBox( + Config.PIE_GLOBAL_SCALE, + parent=self, + pretty_name="Pie global scale", + step=0.05, + max_value=4), + SpinBox( + Config.PIE_ICON_GLOBAL_SCALE, + parent=self, + pretty_name="Pie icon global scale", + step=0.05, + max_value=4), + SpinBox( + Config.PIE_DEADZONE_GLOBAL_SCALE, + parent=self, + pretty_name="Pie deadzone global scale", + step=0.05, + max_value=4), + + "Pie menu style", + Checkbox( + config_field=Config.USE_KRITA_THEME, + parent=self, + pretty_name="Use krita theme"), + ColorButton( + config_field=Config.DEFAULT_BACKGROUND_COLOR, + parent=self, + pretty_name="Default background color"), + ColorButton( + config_field=Config.DEFAULT_ACTIVE_COLOR, + parent=self, + pretty_name="Default active color"), + SpinBox( + config_field=Config.DEFAULT_PIE_OPACITY, + parent=self, + pretty_name="Default pie opacity", + step=1, + max_value=100), + SpinBox( + config_field=Config.PIE_ANIMATION_TIME, + parent=self, + pretty_name="Pie anumation time", + step=0.01, + max_value=1), + f"Shortcut Composer v{__version__}\n" f"Maintainer: {__author__}\n" f"License: {__license__}", diff --git a/shortcut_composer/config_system/ui/__init__.py b/shortcut_composer/config_system/ui/__init__.py index c0c9b021..a732785e 100644 --- a/shortcut_composer/config_system/ui/__init__.py +++ b/shortcut_composer/config_system/ui/__init__.py @@ -18,13 +18,20 @@ from .config_based_widget import ConfigBasedWidget from .config_form_widget import ConfigFormWidget -from .widgets import ConfigComboBox, ConfigSpinBox, EnumComboBox, ColorButton +from .widgets import ( + StringComboBox, + EnumComboBox, + ColorButton, + Checkbox, + SpinBox, +) __all__ = [ "ConfigBasedWidget", "ConfigFormWidget", - "ConfigComboBox", - "ConfigSpinBox", + "StringComboBox", "EnumComboBox", "ColorButton", + "Checkbox", + "SpinBox", ] diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index 65ed607d..b8016091 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -2,12 +2,13 @@ # SPDX-License-Identifier: GPL-3.0-or-later from enum import Enum -from typing import Any, List, Final, Optional, TypeVar, Generic, Protocol, Type +from typing import List, Final, Optional, TypeVar, Generic, Protocol, Type from PyQt5.QtWidgets import ( + QWidget, QDoubleSpinBox, - QComboBox, QSpinBox, - QWidget, + QComboBox, + QCheckBox, QPushButton, QColorDialog) from PyQt5.QtGui import QColor @@ -19,16 +20,16 @@ E = TypeVar("E", bound=Enum) -class SpinBox(Protocol, Generic[F]): +class SpinBoxInterface(Protocol, Generic[F]): """Representation of both Qt spinboxes as one generic class.""" def value(self) -> F: ... def setValue(self, val: F) -> None: ... -class ConfigSpinBox(ConfigBasedWidget[F]): +class SpinBox(ConfigBasedWidget[F]): """ - Wrapper of SpinBox linked to a configutation field. + Wrapper of SpinBox linked to a `float` configutation field. Based on QSpinBox or QDoubleSpinBox depending on the config type. Works only for fields of type: `int` or `float`. @@ -46,7 +47,7 @@ def __init__( self._step = step self._max_value = max_value self._spin_box = self._init_spin_box() - self.widget: Final[SpinBox[F]] = self._spin_box + self.widget: Final[SpinBoxInterface[F]] = self._spin_box self.reset() def read(self) -> F: @@ -57,7 +58,7 @@ def set(self, value: F) -> None: """Replace the value of the spinbox widget with passed one.""" self._spin_box.setValue(value) - def _init_spin_box(self) -> SpinBox: + def _init_spin_box(self) -> SpinBoxInterface: """Return the spinbox widget of type based on config field type.""" spin_box: QDoubleSpinBox = {int: QSpinBox, float: QDoubleSpinBox}[ type(self.config_field.default)]() @@ -70,19 +71,15 @@ def _init_spin_box(self) -> SpinBox: return spin_box -class ConfigComboBox(ConfigBasedWidget[str]): - """ - Wrapper of Combobox linked to a configutation field. - - Works only for fields of type: `str`. - """ +class StringComboBox(ConfigBasedWidget[str]): + """Wrapper of Combobox linked to a `str` configutation field.""" def __init__( self, config_field: Field[str], parent: Optional[QWidget] = None, pretty_name: Optional[str] = None, - allowed_values: List[Any] = [], + allowed_values: List[str] = [], ) -> None: super().__init__(config_field, parent, pretty_name) self._allowed_values = allowed_values @@ -113,7 +110,7 @@ def _init_combo_box(self) -> QComboBox: class EnumComboBox(ConfigBasedWidget[E]): """ - Wrapper of Combobox linked to a Enum configutation field. + Wrapper of Combobox linked to a `Enum` configutation field. Allows to pick one of enum members. """ @@ -152,6 +149,13 @@ def _init_combo_box(self) -> QComboBox: class ColorButton(ConfigBasedWidget[QColor]): + """ + Wrapper of QPushButton linked to a `QColor` configutation field. + + Button displays currently selected color, and clicking activates a + color picker for changing it. + """ + def __init__( self, config_field: Field[QColor], @@ -166,17 +170,42 @@ def __init__( self.reset() def read(self) -> QColor: + """Return QColor displayed in the button.""" return self._color def set(self, value: QColor) -> None: + """Remember given color, and replace current button color with it.""" self._color = value self._button.setStyleSheet( f"background-color: {self._color.name()}; border: none") def _init_button(self) -> QPushButton: + """Return the QPushButton widget.""" def on_click(): self.set(QColorDialog.getColor(self._color)) button = QPushButton("") button.clicked.connect(on_click) return button + + +class Checkbox(ConfigBasedWidget[bool]): + """Wrapper of QCheckBox linked to a `bool` configutation field.""" + def __init__( + self, + config_field: Field[bool], + parent: Optional[QWidget] = None, + pretty_name: Optional[str] = None, + ) -> None: + super().__init__(config_field, parent, pretty_name) + self._checkbox = QCheckBox() + self.widget: Final[QCheckBox] = self._checkbox + self.reset() + + def read(self) -> bool: + """Return checkbox state.""" + return self._checkbox.isChecked() + + def set(self, value: bool) -> None: + """Set checkbox state.""" + self._checkbox.setChecked(value) diff --git a/shortcut_composer/templates/pie_menu.py b/shortcut_composer/templates/pie_menu.py index 015955c3..5298ae2a 100644 --- a/shortcut_composer/templates/pie_menu.py +++ b/shortcut_composer/templates/pie_menu.py @@ -48,6 +48,10 @@ class PieMenu(RawInstructions, Generic[T]): - `icon_radius_scale` -- (optional) default icons size multiplier - `background_color` -- (optional) default rgba color of background - `active_color` -- (optional) default rgba color of active pie + - `pie opacity` -- (optional) default opacity of the pie + - `save local` -- (optional) default save location + - `deadzone strategy` -- (optional) default strategy what to do, + when mouse does not leave deadzone - `short_vs_long_press_time` -- (optional) time [s] that specifies if key press is short or long @@ -77,8 +81,9 @@ def __init__( instructions: Optional[List[Instruction]] = None, pie_radius_scale: float = 1.0, icon_radius_scale: float = 1.0, - background_color: QColor = QColor(70, 70, 70, 190), - active_color: QColor = QColor(100, 150, 230, 255), + background_color: Optional[QColor] = None, + active_color: Optional[QColor] = None, + pie_opacity: int = 75, save_local: bool = False, deadzone_strategy: DeadzoneStrategy = DeadzoneStrategy.DO_NOTHING, short_vs_long_press_time: Optional[float] = None @@ -94,6 +99,7 @@ def __init__( save_local=save_local, background_color=background_color, active_color=active_color, + pie_opacity=pie_opacity, deadzone_strategy=deadzone_strategy) self._config.ORDER.register_callback(self._reset_labels) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index 805265ba..f9ccb5cb 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -23,8 +23,11 @@ def __init__(self, name, *args, **kwargs) -> None: name: str """Name of field group.""" + + USE_DEFAULT_THEME: Field[bool] BACKGROUND_COLOR: Field[QColor] ACTIVE_COLOR: Field[QColor] + PIE_OPACITY: Field[float] SAVE_LOCAL: Field[bool] DEADZONE_STRATEGY: Field[DeadzoneStrategy] diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py index 483c192f..4c26b1c2 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -3,7 +3,9 @@ from typing import List, Callable, Generic, TypeVar from PyQt5.QtGui import QColor + from data_components import DeadzoneStrategy +from api_krita import Krita from ..pie_config import PieConfig T = TypeVar("T") @@ -21,6 +23,7 @@ def __init__( save_local: bool, background_color: QColor, active_color: QColor, + pie_opacity: int, deadzone_strategy: DeadzoneStrategy ) -> None: super().__init__(name) @@ -32,8 +35,15 @@ def __init__( self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) self.ORDER = self._create_editable_dual_field("Values", values) + use_default = active_color is None and background_color is None + if background_color is None: + background_color = Krita.get_main_color_from_theme() + if active_color is None: + active_color = Krita.get_active_color_from_theme() + self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) self.BACKGROUND_COLOR = self.field("Bg color", background_color) self.ACTIVE_COLOR = self.field("Active color", active_color) + self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) self.allow_value_edit = True def values(self) -> List[T]: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py index b62b82c1..f4941721 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -1,10 +1,12 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Callable, Union +from typing import List, Callable, Union, Optional from PyQt5.QtGui import QColor + from config_system import Field from data_components import Tag, DeadzoneStrategy +from api_krita import Krita from ..pie_config import PieConfig @@ -23,8 +25,9 @@ def __init__( pie_radius_scale: float, icon_radius_scale: float, save_local: bool, - background_color: QColor, - active_color: QColor, + background_color: Optional[QColor], + active_color: Optional[QColor], + pie_opacity: int, deadzone_strategy: DeadzoneStrategy ) -> None: super().__init__(name) @@ -41,8 +44,15 @@ def __init__( self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) self.ORDER = self._create_editable_dual_field("Values", [], str) + use_default = active_color is None and background_color is None + if background_color is None: + background_color = Krita.get_main_color_from_theme() + if active_color is None: + active_color = Krita.get_active_color_from_theme() + self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) self.BACKGROUND_COLOR = self.field("Bg color", background_color) self.ACTIVE_COLOR = self.field("Active color", active_color) + self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) @property def allow_value_edit(self) -> bool: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index 1598e18a..788d9798 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -16,9 +16,10 @@ from api_krita.pyqt import AnimatedWidget, BaseWidget, SafeConfirmButton from config_system.ui import ( ConfigFormWidget, - ConfigSpinBox, + SpinBox, EnumComboBox, - ColorButton) + ColorButton, + Checkbox) from composer_utils import Config from data_components import DeadzoneStrategy from .pie_style import PieStyle @@ -61,23 +62,32 @@ def __init__( self._tab_holder = QTabWidget() self._local_settings = ConfigFormWidget([ + "Behaviour", EnumComboBox( config_field=config.DEADZONE_STRATEGY, parent=self, pretty_name="On deadzone", enum_type=DeadzoneStrategy), - ConfigSpinBox( + + "Size", + SpinBox( config_field=config.PIE_RADIUS_SCALE, parent=self, pretty_name="Pie scale", step=0.05, max_value=4), - ConfigSpinBox( + SpinBox( config_field=config.ICON_RADIUS_SCALE, parent=self, pretty_name="Icon max scale", step=0.05, max_value=4), + + "Style", + Checkbox( + config_field=config.USE_DEFAULT_THEME, + parent=self, + pretty_name="Use default theme"), ColorButton( config_field=config.BACKGROUND_COLOR, parent=self, @@ -86,6 +96,12 @@ def __init__( config_field=config.ACTIVE_COLOR, parent=self, pretty_name="Active color"), + SpinBox( + config_field=config.PIE_OPACITY, + parent=self, + pretty_name="Pie opacity", + step=1, + max_value=100), ]) self._tab_holder.addTab(self._local_settings, "Preferences") self._tab_holder.addTab(LocationTab(self._config), "Save location") diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py index a914d977..456e31d5 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_combo_box.py @@ -6,11 +6,11 @@ from PyQt5.QtWidgets import QWidget from config_system import Field -from config_system.ui import ConfigComboBox +from config_system.ui import StringComboBox from .group_manager import GroupManager -class GroupComboBox(ConfigComboBox): +class GroupComboBox(StringComboBox): def __init__( self, diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 93b0ce38..e1a45ac3 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -126,20 +126,31 @@ def accept_button_radius(self) -> int: @property def active_color(self) -> QColor: - """Color of active element.""" - if self._pie_config.ACTIVE_COLOR is not None: + """ + Color of highlight, when label is active. + + If custom one is not specified, use the default one. + """ + if not self._pie_config.USE_DEFAULT_THEME.read(): return self._pie_config.ACTIVE_COLOR.read() else: - return Krita.get_active_color_from_theme() + return Config.default_active_color @property def background_color(self) -> QColor: - """Color of base area. Depends on the app theme lightness""" - if self._pie_config.BACKGROUND_COLOR is not None: - return self._pie_config.BACKGROUND_COLOR.read() - bg_color = Krita.get_main_color_from_theme() - bg_color.setAlpha(190) - return bg_color + """ + Color of pie background area. + + If custom one is not specified, use the default one. + Opacity is stored in a separate field in <0-100> range + """ + if self._pie_config.USE_DEFAULT_THEME.read(): + return Config.default_background_color + + background_color = self._pie_config.BACKGROUND_COLOR.read() + opacity = self._pie_config.PIE_OPACITY.read() * 255 / 100 + background_color.setAlpha(round(opacity)) + return background_color @property def active_color_dark(self): From 87f14f877b1425e0e3afa0d721c94a73930d6aa1 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 32/69] Deduplicate code in PieConfig implementations --- .../templates/pie_menu_utils/pie_config.py | 45 +++++++++++++------ .../pie_config_impl/non_preset_pie_config.py | 37 +++++++-------- .../pie_config_impl/preset_pie_config.py | 28 +++++------- 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index f9ccb5cb..4baa71c9 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -4,7 +4,8 @@ from abc import ABC, abstractmethod from typing import List, Callable, Generic, TypeVar, Optional from PyQt5.QtGui import QColor -from config_system import Field, FieldGroup +from api_krita import Krita +from config_system import FieldGroup from config_system.field_base_impl import DualField, FieldWithEditableDefault from data_components import DeadzoneStrategy @@ -15,25 +16,43 @@ class PieConfig(FieldGroup, Generic[T], ABC): """Abstract FieldGroup representing config of PieMenu.""" - def __init__(self, name, *args, **kwargs) -> None: + def __init__( + self, + name: str, + values: List[T], + pie_radius_scale: float, + icon_radius_scale: float, + save_local: bool, + background_color: Optional[QColor], + active_color: Optional[QColor], + pie_opacity: int, + deadzone_strategy: DeadzoneStrategy + ) -> None: super().__init__(name) + self._values = values + + self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) + self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) + + self.SAVE_LOCAL = self.field("Save local", save_local) + self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) + + use_default = active_color is None and background_color is None + if background_color is None: + background_color = Krita.get_main_color_from_theme() + if active_color is None: + active_color = Krita.get_active_color_from_theme() + self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) + self.BACKGROUND_COLOR = self.field("Bg color", background_color) + self.ACTIVE_COLOR = self.field("Active color", active_color) + self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) allow_value_edit: bool """Is it allowed to remove elements in runtime. """ - name: str """Name of field group.""" - - USE_DEFAULT_THEME: Field[bool] - BACKGROUND_COLOR: Field[QColor] - ACTIVE_COLOR: Field[QColor] - PIE_OPACITY: Field[float] - - SAVE_LOCAL: Field[bool] - DEADZONE_STRATEGY: Field[DeadzoneStrategy] ORDER: FieldWithEditableDefault[List[T], DualField[List[T]]] - PIE_RADIUS_SCALE: Field[float] - ICON_RADIUS_SCALE: Field[float] + """Values displayed in the pie. Used to synchronize pie elements.""" @abstractmethod def values(self) -> List[T]: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py index 4c26b1c2..be33de5c 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import List, Callable, Generic, TypeVar +from typing import List, Callable, Generic, TypeVar, Optional from PyQt5.QtGui import QColor from data_components import DeadzoneStrategy -from api_krita import Krita from ..pie_config import PieConfig T = TypeVar("T") @@ -21,29 +20,23 @@ def __init__( pie_radius_scale: float, icon_radius_scale: float, save_local: bool, - background_color: QColor, - active_color: QColor, + background_color: Optional[QColor], + active_color: Optional[QColor], pie_opacity: int, deadzone_strategy: DeadzoneStrategy ) -> None: - super().__init__(name) - - self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) - self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) - - self.SAVE_LOCAL = self.field("Save local", save_local) - self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) - self.ORDER = self._create_editable_dual_field("Values", values) - - use_default = active_color is None and background_color is None - if background_color is None: - background_color = Krita.get_main_color_from_theme() - if active_color is None: - active_color = Krita.get_active_color_from_theme() - self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) - self.BACKGROUND_COLOR = self.field("Bg color", background_color) - self.ACTIVE_COLOR = self.field("Active color", active_color) - self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) + super().__init__( + name=name, + values=values, + pie_radius_scale=pie_radius_scale, + icon_radius_scale=icon_radius_scale, + save_local=save_local, + background_color=background_color, + active_color=active_color, + pie_opacity=pie_opacity, + deadzone_strategy=deadzone_strategy) + + self.ORDER = self._create_editable_dual_field("Values", self._values) self.allow_value_edit = True def values(self) -> List[T]: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py index f4941721..8017a70c 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -6,7 +6,6 @@ from config_system import Field from data_components import Tag, DeadzoneStrategy -from api_krita import Krita from ..pie_config import PieConfig @@ -30,13 +29,16 @@ def __init__( pie_opacity: int, deadzone_strategy: DeadzoneStrategy ) -> None: - super().__init__(name) - - self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) - self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) - - self.SAVE_LOCAL = self.field("Save local", save_local) - self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) + super().__init__( + name=name, + values=values, + pie_radius_scale=pie_radius_scale, + icon_radius_scale=icon_radius_scale, + save_local=save_local, + background_color=background_color, + active_color=active_color, + pie_opacity=pie_opacity, + deadzone_strategy=deadzone_strategy) tag_mode = isinstance(values, Tag) tag_name = values.tag_name if isinstance(values, Tag) else "" @@ -44,16 +46,6 @@ def __init__( self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) self.ORDER = self._create_editable_dual_field("Values", [], str) - use_default = active_color is None and background_color is None - if background_color is None: - background_color = Krita.get_main_color_from_theme() - if active_color is None: - active_color = Krita.get_active_color_from_theme() - self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) - self.BACKGROUND_COLOR = self.field("Bg color", background_color) - self.ACTIVE_COLOR = self.field("Active color", active_color) - self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) - @property def allow_value_edit(self) -> bool: """Return whether user can add and remove items from the pie.""" From ee0fcea199bc05e4797cb326ce4b35b5af6da2ac Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 33/69] Hide color buttons when they are not taken into consideration --- .../composer_utils/global_config.py | 14 ++++++------ .../composer_utils/settings_dialog.py | 18 ++++++++++----- shortcut_composer/config_system/ui/widgets.py | 2 ++ .../templates/pie_menu_utils/pie_config.py | 5 +++-- .../templates/pie_menu_utils/pie_settings.py | 22 ++++++++++++++----- .../templates/pie_menu_utils/pie_style.py | 4 ++-- 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/shortcut_composer/composer_utils/global_config.py b/shortcut_composer/composer_utils/global_config.py index c16442c5..b547dc77 100644 --- a/shortcut_composer/composer_utils/global_config.py +++ b/shortcut_composer/composer_utils/global_config.py @@ -41,7 +41,7 @@ def __init__(self, name: str) -> None: default_bg_color = QColor(75, 75, 75) else: default_bg_color = QColor(210, 210, 210) - self.USE_KRITA_THEME = self.field("Use krita theme", False) + self.OVERRIDE_KRITA_THEME = self.field("Override krita theme", True) self.DEFAULT_BACKGROUND_COLOR = self.field( "Global bg color", default_bg_color) self.DEFAULT_ACTIVE_COLOR = self.field( @@ -56,10 +56,10 @@ def get_sleep_time(self) -> int: @property def default_background_color(self) -> QColor: """Color of pies, when the pie does not specify a custom one.""" - if self.USE_KRITA_THEME.read(): - bg_color = Krita.get_main_color_from_theme() - else: + if self.OVERRIDE_KRITA_THEME.read(): bg_color = self.DEFAULT_BACKGROUND_COLOR.read() + else: + bg_color = Krita.get_main_color_from_theme() opacity = self.DEFAULT_PIE_OPACITY.read() * 255 / 100 bg_color.setAlpha(round(opacity)) return bg_color @@ -67,9 +67,9 @@ def default_background_color(self) -> QColor: @property def default_active_color(self) -> QColor: """Pie highlight color, when the pie does not specify a custom one.""" - if self.USE_KRITA_THEME.read(): - return Krita.get_active_color_from_theme() - return self.DEFAULT_ACTIVE_COLOR.read() + if self.OVERRIDE_KRITA_THEME.read(): + return self.DEFAULT_ACTIVE_COLOR.read() + return Krita.get_active_color_from_theme() Config = GlobalConfig("ShortcutComposer") diff --git a/shortcut_composer/composer_utils/settings_dialog.py b/shortcut_composer/composer_utils/settings_dialog.py index 51530898..f96da2e3 100644 --- a/shortcut_composer/composer_utils/settings_dialog.py +++ b/shortcut_composer/composer_utils/settings_dialog.py @@ -73,15 +73,15 @@ def __init__(self) -> None: max_value=4), "Pie menu style", - Checkbox( - config_field=Config.USE_KRITA_THEME, + theme_checkbox := Checkbox( + config_field=Config.OVERRIDE_KRITA_THEME, parent=self, - pretty_name="Use krita theme"), - ColorButton( + pretty_name="Override krita theme"), + bg_button := ColorButton( config_field=Config.DEFAULT_BACKGROUND_COLOR, parent=self, pretty_name="Default background color"), - ColorButton( + active_button := ColorButton( config_field=Config.DEFAULT_ACTIVE_COLOR, parent=self, pretty_name="Default active color"), @@ -103,6 +103,14 @@ def __init__(self) -> None: f"License: {__license__}", ]) + def update_theme_state(): + """Hide color buttons when not taken into consideration.""" + enable_state = theme_checkbox.widget.isChecked() + bg_button.widget.setVisible(enable_state) + active_button.widget.setVisible(enable_state) + theme_checkbox.widget.stateChanged.connect(update_theme_state) + update_theme_state() + full_layout = QVBoxLayout(self) full_layout.addWidget(self._general_tab) full_layout.addLayout(ButtonsLayout( diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index b8016091..8262e744 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -25,6 +25,7 @@ class SpinBoxInterface(Protocol, Generic[F]): def value(self) -> F: ... def setValue(self, val: F) -> None: ... + def setEnabled(self, a0: bool) -> None: ... class SpinBox(ConfigBasedWidget[F]): @@ -191,6 +192,7 @@ def on_click(): class Checkbox(ConfigBasedWidget[bool]): """Wrapper of QCheckBox linked to a `bool` configutation field.""" + def __init__( self, config_field: Field[bool], diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index 4baa71c9..f8b88ca6 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -37,12 +37,13 @@ def __init__( self.SAVE_LOCAL = self.field("Save local", save_local) self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) - use_default = active_color is None and background_color is None + override_default = bool(active_color or background_color) if background_color is None: background_color = Krita.get_main_color_from_theme() if active_color is None: active_color = Krita.get_active_color_from_theme() - self.USE_DEFAULT_THEME = self.field("Use default theme", use_default) + self.OVERRIDE_DEFAULT_THEME = self.field( + "Override default theme", override_default) self.BACKGROUND_COLOR = self.field("Bg color", background_color) self.ACTIVE_COLOR = self.field("Active color", active_color) self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index 788d9798..aac0f3b5 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -84,25 +84,35 @@ def __init__( max_value=4), "Style", - Checkbox( - config_field=config.USE_DEFAULT_THEME, + theme_checkbox := Checkbox( + config_field=config.OVERRIDE_DEFAULT_THEME, parent=self, - pretty_name="Use default theme"), - ColorButton( + pretty_name="Override default theme"), + bg_button := ColorButton( config_field=config.BACKGROUND_COLOR, parent=self, pretty_name="Background color"), - ColorButton( + active_button := ColorButton( config_field=config.ACTIVE_COLOR, parent=self, pretty_name="Active color"), - SpinBox( + opacity_picker := SpinBox( config_field=config.PIE_OPACITY, parent=self, pretty_name="Pie opacity", step=1, max_value=100), ]) + + def update_theme_state(): + """Hide color buttons when not taken into consideration.""" + enable_state = theme_checkbox.widget.isChecked() + bg_button.widget.setVisible(enable_state) + active_button.widget.setVisible(enable_state) + opacity_picker.widget.setEnabled(enable_state) + theme_checkbox.widget.stateChanged.connect(update_theme_state) + update_theme_state() + self._tab_holder.addTab(self._local_settings, "Preferences") self._tab_holder.addTab(LocationTab(self._config), "Save location") diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index e1a45ac3..1fa7bba1 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -131,7 +131,7 @@ def active_color(self) -> QColor: If custom one is not specified, use the default one. """ - if not self._pie_config.USE_DEFAULT_THEME.read(): + if self._pie_config.OVERRIDE_DEFAULT_THEME.read(): return self._pie_config.ACTIVE_COLOR.read() else: return Config.default_active_color @@ -144,7 +144,7 @@ def background_color(self) -> QColor: If custom one is not specified, use the default one. Opacity is stored in a separate field in <0-100> range """ - if self._pie_config.USE_DEFAULT_THEME.read(): + if not self._pie_config.OVERRIDE_DEFAULT_THEME.read(): return Config.default_background_color background_color = self._pie_config.BACKGROUND_COLOR.read() From 02fc5c6723372fab568c7e666f458726895c6b7f Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 34/69] bugfix: update pie button colors on color change --- shortcut_composer/api_krita/pyqt/round_button.py | 13 +++++++------ .../templates/pie_menu_utils/pie_button.py | 10 +++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/shortcut_composer/api_krita/pyqt/round_button.py b/shortcut_composer/api_krita/pyqt/round_button.py index 991a6176..63243560 100644 --- a/shortcut_composer/api_krita/pyqt/round_button.py +++ b/shortcut_composer/api_krita/pyqt/round_button.py @@ -26,6 +26,7 @@ def __init__( self.setCursor(Qt.ArrowCursor) self._icon_scale = icon_scale + self._radius = initial_radius self._background_color = background_color self._active_color = active_color @@ -38,21 +39,21 @@ def __init__( self.setAttribute(Qt.WA_TranslucentBackground) self.setStyleSheet("background: transparent;") - self.resize(initial_radius) + self.refresh() self.show() - def resize(self, radius: int) -> None: + def refresh(self) -> None: """Change the size and repaint the button.""" - self.setGeometry(0, 0, radius*2, radius*2) + self.setGeometry(0, 0, self._radius*2, self._radius*2) self.setStyleSheet(f""" QPushButton [ - border: {round(radius*0.06)}px + border: {round(self._radius*0.06)}px {self._color_to_str(self._border_color)}; - border-radius: {radius}px; + border-radius: {self._radius}px; border-style: outset; background: {self._color_to_str(self._background_color)}; - qproperty-iconSize:{round(radius*self._icon_scale)}px; + qproperty-iconSize:{round(self._radius*self._icon_scale)}px; ] QPushButton:hover [ background: {self._color_to_str(self._active_color)}; diff --git a/shortcut_composer/templates/pie_menu_utils/pie_button.py b/shortcut_composer/templates/pie_menu_utils/pie_button.py index d7f95d92..99cb3c41 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_button.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_button.py @@ -28,6 +28,10 @@ def __init__( config: PieConfig, parent: Optional[QWidget] = None, ) -> None: + self._radius_callback = radius_callback + self._style = style + config.register_callback(self.refresh) + super().__init__( icon=icon, icon_scale=icon_scale, @@ -36,4 +40,8 @@ def __init__( active_color=style.active_color, parent=parent) - config.register_callback(lambda: self.resize(radius_callback())) + def refresh(self) -> None: + self._radius = self._radius_callback() + self._background_color = self._style.background_color + self._active_color = self._style.active_color + super().refresh() From 25c07fab378a536560ce5a0a34282ef3c08cb5d6 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 35/69] Separate overriding of krita theme colors in global settings --- .../composer_utils/global_config.py | 18 ++++++++++-------- .../composer_utils/settings_dialog.py | 18 +++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/shortcut_composer/composer_utils/global_config.py b/shortcut_composer/composer_utils/global_config.py index b547dc77..9dffbb14 100644 --- a/shortcut_composer/composer_utils/global_config.py +++ b/shortcut_composer/composer_utils/global_config.py @@ -31,21 +31,23 @@ def __init__(self, name: str) -> None: "Tracker sensitivity scale", 1.0) self.TRACKER_DEADZONE = self.field("Tracker deadzone", 0) self.FPS_LIMIT = self.field("FPS limit", 60) + self.PIE_GLOBAL_SCALE = self.field("Pie global scale", 1.0) self.PIE_ICON_GLOBAL_SCALE = self.field("Pie icon global scale", 1.0) self.PIE_DEADZONE_GLOBAL_SCALE = self.field( "Pie deadzone global scale", 1.0) self.PIE_ANIMATION_TIME = self.field("Pie animation time", 0.2) - if not Krita.is_light_theme_active: - default_bg_color = QColor(75, 75, 75) - else: - default_bg_color = QColor(210, 210, 210) - self.OVERRIDE_KRITA_THEME = self.field("Override krita theme", True) + self.OVERRIDE_BACKGROUND_THEME_COLOR = self.field( + "Override background theme color", False) self.DEFAULT_BACKGROUND_COLOR = self.field( - "Global bg color", default_bg_color) + "Global bg color", Krita.get_main_color_from_theme()) + + self.OVERRIDE_ACTIVE_THEME_COLOR = self.field( + "Override active theme color", True) self.DEFAULT_ACTIVE_COLOR = self.field( "Global active color", QColor(100, 150, 230)) + self.DEFAULT_PIE_OPACITY = self.field("Global pie opacity", 75) def get_sleep_time(self) -> int: @@ -56,7 +58,7 @@ def get_sleep_time(self) -> int: @property def default_background_color(self) -> QColor: """Color of pies, when the pie does not specify a custom one.""" - if self.OVERRIDE_KRITA_THEME.read(): + if self.OVERRIDE_BACKGROUND_THEME_COLOR.read(): bg_color = self.DEFAULT_BACKGROUND_COLOR.read() else: bg_color = Krita.get_main_color_from_theme() @@ -67,7 +69,7 @@ def default_background_color(self) -> QColor: @property def default_active_color(self) -> QColor: """Pie highlight color, when the pie does not specify a custom one.""" - if self.OVERRIDE_KRITA_THEME.read(): + if self.OVERRIDE_ACTIVE_THEME_COLOR.read(): return self.DEFAULT_ACTIVE_COLOR.read() return Krita.get_active_color_from_theme() diff --git a/shortcut_composer/composer_utils/settings_dialog.py b/shortcut_composer/composer_utils/settings_dialog.py index f96da2e3..03ebd703 100644 --- a/shortcut_composer/composer_utils/settings_dialog.py +++ b/shortcut_composer/composer_utils/settings_dialog.py @@ -73,14 +73,18 @@ def __init__(self) -> None: max_value=4), "Pie menu style", - theme_checkbox := Checkbox( - config_field=Config.OVERRIDE_KRITA_THEME, + bg_checkbox := Checkbox( + config_field=Config.OVERRIDE_BACKGROUND_THEME_COLOR, parent=self, - pretty_name="Override krita theme"), + pretty_name="Override background theme color"), bg_button := ColorButton( config_field=Config.DEFAULT_BACKGROUND_COLOR, parent=self, pretty_name="Default background color"), + active_checkbox := Checkbox( + config_field=Config.OVERRIDE_ACTIVE_THEME_COLOR, + parent=self, + pretty_name="Override active theme color"), active_button := ColorButton( config_field=Config.DEFAULT_ACTIVE_COLOR, parent=self, @@ -105,10 +109,10 @@ def __init__(self) -> None: def update_theme_state(): """Hide color buttons when not taken into consideration.""" - enable_state = theme_checkbox.widget.isChecked() - bg_button.widget.setVisible(enable_state) - active_button.widget.setVisible(enable_state) - theme_checkbox.widget.stateChanged.connect(update_theme_state) + bg_button.widget.setVisible(bg_checkbox.widget.isChecked()) + active_button.widget.setVisible(active_checkbox.widget.isChecked()) + bg_checkbox.widget.stateChanged.connect(update_theme_state) + active_checkbox.widget.stateChanged.connect(update_theme_state) update_theme_state() full_layout = QVBoxLayout(self) From 7db871b355bf94aaa8e84f95978d2a8b47c195a6 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 36/69] bugfix: do not set color when dialog was cancelled, display borders on ColorButton --- shortcut_composer/config_system/ui/widgets.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index 8262e744..61884a14 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -177,13 +177,15 @@ def read(self) -> QColor: def set(self, value: QColor) -> None: """Remember given color, and replace current button color with it.""" self._color = value - self._button.setStyleSheet( - f"background-color: {self._color.name()}; border: none") + self._button.setStyleSheet(f"background-color: {self._color.name()}") def _init_button(self) -> QPushButton: """Return the QPushButton widget.""" def on_click(): - self.set(QColorDialog.getColor(self._color)) + """Set the selected color, if the dialog was not cancelled.""" + fetched_color = QColorDialog.getColor(self._color) + if fetched_color.isValid(): + self.set(fetched_color) button = QPushButton("") button.clicked.connect(on_click) From e670a46bc556c3373f6fda2f20850267142e4a97 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 37/69] Improves code quality of global_config --- .../composer_utils/global_config.py | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/shortcut_composer/composer_utils/global_config.py b/shortcut_composer/composer_utils/global_config.py index 9dffbb14..33a17fe7 100644 --- a/shortcut_composer/composer_utils/global_config.py +++ b/shortcut_composer/composer_utils/global_config.py @@ -15,40 +15,54 @@ class GlobalConfig(FieldGroup): - read current value from krita config file in correct type - write given value to krita config file - Class holds a staticmethod which resets all config files. - - VALUES configs are string representations of lists. They hold values - to use in given action with elements separated with tabulators. - These are needed to be further parsed using TagConfigValues or - EnumConfigValues. + Class inherits a method which resets all config files. """ def __init__(self, name: str) -> None: super().__init__(name) self.SHORT_VS_LONG_PRESS_TIME = self.field( - "Short vs long press time", 0.3) + name="Short vs long press time", + default=0.3) self.TRACKER_SENSITIVITY_SCALE = self.field( - "Tracker sensitivity scale", 1.0) - self.TRACKER_DEADZONE = self.field("Tracker deadzone", 0) - self.FPS_LIMIT = self.field("FPS limit", 60) - - self.PIE_GLOBAL_SCALE = self.field("Pie global scale", 1.0) - self.PIE_ICON_GLOBAL_SCALE = self.field("Pie icon global scale", 1.0) + name="Tracker sensitivity scale", + default=1.0) + self.TRACKER_DEADZONE = self.field( + name="Tracker deadzone", + default=0) + self.FPS_LIMIT = self.field( + name="FPS limit", + default=60) + + self.PIE_GLOBAL_SCALE = self.field( + name="Pie global scale", + default=1.0) + self.PIE_ICON_GLOBAL_SCALE = self.field( + name="Pie icon global scale", + default=1.0) self.PIE_DEADZONE_GLOBAL_SCALE = self.field( - "Pie deadzone global scale", 1.0) - self.PIE_ANIMATION_TIME = self.field("Pie animation time", 0.2) + name="Pie deadzone global scale", + default=1.0) + self.PIE_ANIMATION_TIME = self.field( + name="Pie animation time", + default=0.2) self.OVERRIDE_BACKGROUND_THEME_COLOR = self.field( - "Override background theme color", False) + name="Override background theme color", + default=False) self.DEFAULT_BACKGROUND_COLOR = self.field( - "Global bg color", Krita.get_main_color_from_theme()) + name="Global background color", + default=Krita.get_main_color_from_theme()) self.OVERRIDE_ACTIVE_THEME_COLOR = self.field( - "Override active theme color", True) + name="Override active theme color", + default=True) self.DEFAULT_ACTIVE_COLOR = self.field( - "Global active color", QColor(100, 150, 230)) + name="Global active color", + default=QColor(100, 150, 230)) - self.DEFAULT_PIE_OPACITY = self.field("Global pie opacity", 75) + self.DEFAULT_PIE_OPACITY = self.field( + name="Global pie opacity", + default=75) def get_sleep_time(self) -> int: """Read sleep time from FPS_LIMIT config field.""" From f2521e1fedf9544469da65b4de7b3b4dbc6e05b4 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 38/69] Improve formatting in pie_configs --- .../templates/pie_menu_utils/pie_config.py | 34 ++++++++++++++----- .../pie_config_impl/non_preset_pie_config.py | 4 ++- .../pie_config_impl/preset_pie_config.py | 13 +++++-- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config.py index f8b88ca6..6f8d9f38 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config.py @@ -31,22 +31,38 @@ def __init__( super().__init__(name) self._values = values - self.PIE_RADIUS_SCALE = self.field("Pie scale", pie_radius_scale) - self.ICON_RADIUS_SCALE = self.field("Icon scale", icon_radius_scale) - - self.SAVE_LOCAL = self.field("Save local", save_local) - self.DEADZONE_STRATEGY = self.field("deadzone", deadzone_strategy) + self.PIE_RADIUS_SCALE = self.field( + name="Pie scale", + default=pie_radius_scale) + self.ICON_RADIUS_SCALE = self.field( + name="Icon scale", + default=icon_radius_scale) + + self.SAVE_LOCAL = self.field( + name="Save local", + default=save_local) + self.DEADZONE_STRATEGY = self.field( + name="deadzone", + default=deadzone_strategy) override_default = bool(active_color or background_color) if background_color is None: background_color = Krita.get_main_color_from_theme() if active_color is None: active_color = Krita.get_active_color_from_theme() + self.OVERRIDE_DEFAULT_THEME = self.field( - "Override default theme", override_default) - self.BACKGROUND_COLOR = self.field("Bg color", background_color) - self.ACTIVE_COLOR = self.field("Active color", active_color) - self.PIE_OPACITY = self.field("Pie opacity", pie_opacity) + name="Override default theme", + default=override_default) + self.BACKGROUND_COLOR = self.field( + name="Background color", + default=background_color) + self.ACTIVE_COLOR = self.field( + name="Active color", + default=active_color) + self.PIE_OPACITY = self.field( + name="Pie opacity", + default=pie_opacity) allow_value_edit: bool """Is it allowed to remove elements in runtime. """ diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py index be33de5c..15a289bf 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/non_preset_pie_config.py @@ -36,7 +36,9 @@ def __init__( pie_opacity=pie_opacity, deadzone_strategy=deadzone_strategy) - self.ORDER = self._create_editable_dual_field("Values", self._values) + self.ORDER = self._create_editable_dual_field( + field_name="Values", + default=self._values) self.allow_value_edit = True def values(self) -> List[T]: diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py index 8017a70c..e8b99e26 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/preset_pie_config.py @@ -42,9 +42,16 @@ def __init__( tag_mode = isinstance(values, Tag) tag_name = values.tag_name if isinstance(values, Tag) else "" - self.TAG_MODE = self._create_editable_dual_field("Tag mode", tag_mode) - self.TAG_NAME = self._create_editable_dual_field("Tag", tag_name) - self.ORDER = self._create_editable_dual_field("Values", [], str) + self.TAG_MODE = self._create_editable_dual_field( + field_name="Tag mode", + default=tag_mode) + self.TAG_NAME = self._create_editable_dual_field( + field_name="Tag", + default=tag_name) + self.ORDER = self._create_editable_dual_field( + field_name="Values", + default=[], + parser_type=str) @property def allow_value_edit(self) -> bool: From 8107aad3ff308f193f4a4f1484fcdc29d55dd1dd Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 39/69] Button on pie preferences to reset them to default --- .../config_system/ui/config_form_widget.py | 8 +++--- .../templates/pie_menu_utils/pie_settings.py | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/shortcut_composer/config_system/ui/config_form_widget.py b/shortcut_composer/config_system/ui/config_form_widget.py index 4679e56e..2636061d 100644 --- a/shortcut_composer/config_system/ui/config_form_widget.py +++ b/shortcut_composer/config_system/ui/config_form_widget.py @@ -36,7 +36,7 @@ def __init__(self, elements: List[Union[ConfigBasedWidget, str]]) -> None: Qt.AlignHCenter | Qt.AlignTop) # type: ignore self.setLayout(self._layout) - self._widgets: List[ConfigBasedWidget] = [] + self.widgets: List[ConfigBasedWidget] = [] for element in elements: if isinstance(element, str): self.add_title(element) @@ -47,7 +47,7 @@ def __init__(self, elements: List[Union[ConfigBasedWidget, str]]) -> None: def add_row(self, element: ConfigBasedWidget) -> None: """Add a ConfigBasedWidget along with a label.""" - self._widgets.append(element) + self.widgets.append(element) self._layout.addRow(f"{element.pretty_name}:", element.widget) def add_title(self, text: str) -> None: @@ -59,10 +59,10 @@ def add_title(self, text: str) -> None: def refresh(self) -> None: """Read values from krita config and apply them to stored boxes.""" - for element in self._widgets: + for element in self.widgets: element.reset() def apply(self) -> None: """Write values from stored spin boxes to krita config file.""" - for element in self._widgets: + for element in self.widgets: element.save() diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings.py index aac0f3b5..350193fa 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings.py @@ -113,13 +113,28 @@ def update_theme_state(): theme_checkbox.widget.stateChanged.connect(update_theme_state) update_theme_state() - self._tab_holder.addTab(self._local_settings, "Preferences") + preferences_widget = QWidget() + preferences = QVBoxLayout(self) + preferences.addWidget(self._local_settings) + preferences.addStretch() + preferences.addWidget(self._init_full_reset_button()) + preferences_widget.setLayout(preferences) + + self._tab_holder.addTab(preferences_widget, "Preferences") self._tab_holder.addTab(LocationTab(self._config), "Save location") layout = QVBoxLayout(self) layout.addWidget(self._tab_holder) self.setLayout(layout) + def _init_full_reset_button(self) -> SafeConfirmButton: + button = SafeConfirmButton( + text="Reset pie preferences", + icon=Krita.get_icon("edit-delete")) + button.clicked.connect(self._reset_config_to_default) + button.setFixedHeight(button.sizeHint().height()*2) + return button + def show(self) -> None: """Show the window after its settings are refreshed.""" self._local_settings.refresh() @@ -134,6 +149,15 @@ def _reset(self) -> None: """React to change in pie size.""" self.setMinimumHeight(self._style.widget_radius*2) + def _reset_config_to_default(self): + """ + Reset widgets from preferences layout to default values. + + Does not write to config yet, to prevent artifacts on pie. + """ + for widget in self._local_settings.widgets: + widget.set(widget.config_field.default) + class LocationTab(QWidget): """PieSettings tab for changing location in which values are saved.""" From 5b472e4b24584eae940e71d08e38116e8e682fc6 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 40/69] bugfix: keep space when ColorButtons are hidden --- shortcut_composer/config_system/ui/widgets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shortcut_composer/config_system/ui/widgets.py b/shortcut_composer/config_system/ui/widgets.py index 61884a14..4ba3fffa 100644 --- a/shortcut_composer/config_system/ui/widgets.py +++ b/shortcut_composer/config_system/ui/widgets.py @@ -188,6 +188,9 @@ def on_click(): self.set(fetched_color) button = QPushButton("") + policy = button.sizePolicy() + policy.setRetainSizeWhenHidden(True) + button.setSizePolicy(policy) button.clicked.connect(on_click) return button From 7143b8e5a1a80e27cc6a6f1aed5d9cfe5e267ce4 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Fri, 30 Jun 2023 17:11:51 +0200 Subject: [PATCH 41/69] Improvements to code style and documentation --- .../api_krita/actions/transform_actions.py | 7 +++---- .../api_krita/enums/helpers/__init__.py | 5 +++++ .../api_krita/enums/helpers/enum_group.py | 3 +++ .../api_krita/enums/node_types.py | 20 +----------------- shortcut_composer/api_krita/enums/tool.py | 18 +++------------- .../api_krita/pyqt/safe_confirm_button.py | 3 +++ .../config_system/common_utils/__init__.py | 5 +++++ .../common_utils/save_location.py | 3 +++ .../config_system/field_base_impl/__init__.py | 2 ++ .../field_base_impl/common_utils/__init__.py | 5 +++++ .../core_components/controllers/__init__.py | 21 +------------------ .../core_components/instructions/__init__.py | 11 ---------- .../data_components/deadzone_strategy.py | 3 +++ .../templates/pie_menu_utils/__init__.py | 2 +- .../templates/pie_menu_utils/actuator.py | 3 +++ .../pie_config_impl/__init__.py | 2 ++ .../pie_config_impl/dispatch_pie_config.py | 1 + .../common_utils/__init__.py | 5 +++++ .../common_utils/group_manager.py | 3 +++ .../dispatch_pie_settings.py | 3 +++ 20 files changed, 55 insertions(+), 70 deletions(-) diff --git a/shortcut_composer/api_krita/actions/transform_actions.py b/shortcut_composer/api_krita/actions/transform_actions.py index 9e0b5b0f..05bb2504 100644 --- a/shortcut_composer/api_krita/actions/transform_actions.py +++ b/shortcut_composer/api_krita/actions/transform_actions.py @@ -38,15 +38,13 @@ def _create_actions(self, window) -> None: TransformMode.WARP: self.set_warp, TransformMode.CAGE: self.set_cage, TransformMode.LIQUIFY: self.set_liquify, - TransformMode.MESH: self.set_mesh, - } + TransformMode.MESH: self.set_mesh} for action, implementation in _ACTION_MAP.items(): self._actions[action] = Krita.create_action( window=window, name=action.value, - callback=implementation - ) + callback=implementation) def _set_mode(self, mode: TransformMode) -> None: """Set a passed mode. Implementation of the new krita tool.""" @@ -59,6 +57,7 @@ def _set_mode(self, mode: TransformMode) -> None: self._delayed_click(mode) def _delayed_click(self, mode: TransformMode) -> None: + """Trigger an mode after short period of time to workaround a bug.""" method = partial(self._finder.activate_mode, mode=mode, apply=False) QTimer.singleShot(40, method) diff --git a/shortcut_composer/api_krita/enums/helpers/__init__.py b/shortcut_composer/api_krita/enums/helpers/__init__.py index 2737fad8..a4c76080 100644 --- a/shortcut_composer/api_krita/enums/helpers/__init__.py +++ b/shortcut_composer/api_krita/enums/helpers/__init__.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Components used in Enum definitions.""" + from .enum_group import EnumGroup, Group __all__ = ["EnumGroup", "Group"] diff --git a/shortcut_composer/api_krita/enums/helpers/enum_group.py b/shortcut_composer/api_krita/enums/helpers/enum_group.py index c64e8bad..c1470637 100644 --- a/shortcut_composer/api_krita/enums/helpers/enum_group.py +++ b/shortcut_composer/api_krita/enums/helpers/enum_group.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import Dict, List, Tuple, TypeVar, Optional from enum import Enum, EnumMeta T = TypeVar("T", bound=Enum) diff --git a/shortcut_composer/api_krita/enums/node_types.py b/shortcut_composer/api_krita/enums/node_types.py index 4d15091e..ecd1e74f 100644 --- a/shortcut_composer/api_krita/enums/node_types.py +++ b/shortcut_composer/api_krita/enums/node_types.py @@ -30,27 +30,9 @@ class NodeType(Enum): @property def icon(self) -> QIcon: """Return the icon of this node type.""" - icon_name = _ICON_NAME_MAP.get(self, "edit-delete") - return Api.instance().icon(icon_name) + return Api.instance().icon(self.value) @property def pretty_name(self) -> str: """Format node type name like: `Paint layer`.""" return f"{self.name[0]}{self.name[1:].lower().replace('_', ' ')}" - - -_ICON_NAME_MAP = { - NodeType.PAINT_LAYER: "paintLayer", - NodeType.GROUP_LAYER: "groupLayer", - NodeType.FILE_LAYER: "fileLayer", - NodeType.FILTER_LAYER: "filterLayer", - NodeType.FILL_LAYER: "fillLayer", - NodeType.CLONE_LAYER: "cloneLayer", - NodeType.VECTOR_LAYER: "vectorLayer", - NodeType.TRANSPARENCY_MASK: "transparencyMask", - NodeType.FILTER_MASK: "filterMask", - NodeType.TRANSFORM_MASK: "transformMask", - NodeType.SELECTION_MASK: "selectionMask", - NodeType.COLORIZE_MASK: "colorizeMask" -} -"""Maps node types to names of their icons.""" diff --git a/shortcut_composer/api_krita/enums/tool.py b/shortcut_composer/api_krita/enums/tool.py index 8bd8cfec..1c5f68ef 100644 --- a/shortcut_composer/api_krita/enums/tool.py +++ b/shortcut_composer/api_krita/enums/tool.py @@ -60,10 +60,10 @@ class Tool(EnumGroup): def activate(self): Api.instance().action(self.value).trigger() - @staticmethod - def is_paintable(tool: 'Tool') -> bool: + @classmethod + def is_paintable(cls, tool: 'Tool') -> bool: """Is the user able to paint when the given tool is activated.""" - return tool in _PAINTABLE + return tool in cls._painting # type: ignore @property def icon(self) -> QIcon: @@ -74,15 +74,3 @@ def icon(self) -> QIcon: def pretty_name(self) -> str: """Format tool name like: `Shape select tool`.""" return f"{self.name.replace('_', ' ').capitalize()} tool" - - -_PAINTABLE = { - Tool.FREEHAND_BRUSH, - Tool.LINE, - Tool.ELLIPSE, - Tool.DYNAMIC_BRUSH, - Tool.RECTANGLE, - Tool.MULTI_BRUSH, - Tool.POLYLINE, -} -"""Set of tools that are used to paint on the canvas.""" diff --git a/shortcut_composer/api_krita/pyqt/safe_confirm_button.py b/shortcut_composer/api_krita/pyqt/safe_confirm_button.py index ab26fa2a..b9e8dc7e 100644 --- a/shortcut_composer/api_krita/pyqt/safe_confirm_button.py +++ b/shortcut_composer/api_krita/pyqt/safe_confirm_button.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import Optional from PyQt5.QtGui import QIcon diff --git a/shortcut_composer/config_system/common_utils/__init__.py b/shortcut_composer/config_system/common_utils/__init__.py index d5f3ca03..ebb1d8c2 100644 --- a/shortcut_composer/config_system/common_utils/__init__.py +++ b/shortcut_composer/config_system/common_utils/__init__.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Components used by the core of the config system.""" + from .api_krita import Krita from .save_location import SaveLocation diff --git a/shortcut_composer/config_system/common_utils/save_location.py b/shortcut_composer/config_system/common_utils/save_location.py index 0762c1ab..45395d58 100644 --- a/shortcut_composer/config_system/common_utils/save_location.py +++ b/shortcut_composer/config_system/common_utils/save_location.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import Any, Optional, Protocol from enum import Enum from .api_krita import Krita diff --git a/shortcut_composer/config_system/field_base_impl/__init__.py b/shortcut_composer/config_system/field_base_impl/__init__.py index 64423378..53193d40 100644 --- a/shortcut_composer/config_system/field_base_impl/__init__.py +++ b/shortcut_composer/config_system/field_base_impl/__init__.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +"""Implementations of Field.""" + from .non_list_field import NonListField from .list_field import ListField from .dual_field import DualField diff --git a/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py b/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py index 8f053684..77e47c44 100644 --- a/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py +++ b/shortcut_composer/config_system/field_base_impl/common_utils/__init__.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Components used by the implementations of Field.""" + from .parsers import dispatch_parser, Parser __all__ = ["dispatch_parser", "Parser"] diff --git a/shortcut_composer/core_components/controllers/__init__.py b/shortcut_composer/core_components/controllers/__init__.py index 4736ebf7..fa65f4e2 100644 --- a/shortcut_composer/core_components/controllers/__init__.py +++ b/shortcut_composer/core_components/controllers/__init__.py @@ -1,26 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -""" -Components that allow to get and set a specific property of krita. - -Available controllers: - - `ToolController` - - `BrushSizeController` - - `BlendingModeController` - - `OpacityController` - - `FlowController` - - `PresetController` - - `TimeController` - - `ActiveLayerController` - - `LayerVisibilityController` - - `LayerBlendingModeController`, - - `LayerOpacityController`, - - `CanvasRotationController` - - `CanvasZoomController` - - `ToggleController` - - `CreateLayerWithBlendingController` -""" +"""Components that allow to get and set a specific property of krita.""" from .document_controllers import ( ActiveLayerController, diff --git a/shortcut_composer/core_components/instructions/__init__.py b/shortcut_composer/core_components/instructions/__init__.py index ff84e377..c8577066 100644 --- a/shortcut_composer/core_components/instructions/__init__.py +++ b/shortcut_composer/core_components/instructions/__init__.py @@ -6,17 +6,6 @@ Depending on the picked instruction, tasks can be performed on key press, release, or in a loop while the key is pressed. - -Available instructions: - - `SetBrush` - - `SetBrushOnNonPaintable` - - `ToggleLayerVisibility` - - `ToggleVisibilityAbove` - - `UndoOnPress` - - `EnsureOn` - - `EnsureOff` - - `TemporaryOn` - - `TemporaryOff` """ from .layer_hide import ToggleLayerVisibility, ToggleVisibilityAbove diff --git a/shortcut_composer/data_components/deadzone_strategy.py b/shortcut_composer/data_components/deadzone_strategy.py index 62e794c1..df3d1247 100644 --- a/shortcut_composer/data_components/deadzone_strategy.py +++ b/shortcut_composer/data_components/deadzone_strategy.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from enum import Enum diff --git a/shortcut_composer/templates/pie_menu_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/__init__.py index cbf214c5..5e988a8d 100644 --- a/shortcut_composer/templates/pie_menu_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/__init__.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -"""Implementation of PieMenu main elements.""" +"""Components used by PieMenu action.""" from .pie_config import PieConfig from .pie_settings import PieSettings diff --git a/shortcut_composer/templates/pie_menu_utils/actuator.py b/shortcut_composer/templates/pie_menu_utils/actuator.py index b08125c5..43bd8186 100644 --- a/shortcut_composer/templates/pie_menu_utils/actuator.py +++ b/shortcut_composer/templates/pie_menu_utils/actuator.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import Optional, List from core_components import Controller from config_system import Field diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py index aa5b8254..f33633c0 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/__init__.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +"""Implementations of PieConfig.""" + from .dispatch_pie_config import dispatch_pie_config from .preset_pie_config import PresetPieConfig from .non_preset_pie_config import NonPresetPieConfig diff --git a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py index 28c9d33f..4354f1e0 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_config_impl/dispatch_pie_config.py @@ -8,6 +8,7 @@ def dispatch_pie_config(controller: Controller[T]) -> Type[PieConfig[T]]: + """Return type of PieConfig specialisation based on controller type.""" if issubclass(controller.TYPE, str): return PresetPieConfig # type: ignore return NonPresetPieConfig diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py index 8b2b9ed0..d22c772b 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/__init__.py @@ -1,3 +1,8 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Components used by implementations of PieSettins.""" + from .group_combo_box import GroupComboBox from .group_manager import GroupManager from .group_scroll_area import GroupScrollArea diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py index 44fb7ba0..ae0c5a26 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/group_manager.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import List, Protocol from enum import Enum diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py index 1521da19..3844f724 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/dispatch_pie_settings.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + from typing import Type from enum import Enum From 7411e3fd9be1b7821873b4b14af7b831d8a337a2 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Sun, 2 Jul 2023 13:08:18 +0200 Subject: [PATCH 42/69] bugfix: fix a typo in global settings dialog --- shortcut_composer/composer_utils/settings_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut_composer/composer_utils/settings_dialog.py b/shortcut_composer/composer_utils/settings_dialog.py index 03ebd703..ff5f7a09 100644 --- a/shortcut_composer/composer_utils/settings_dialog.py +++ b/shortcut_composer/composer_utils/settings_dialog.py @@ -98,7 +98,7 @@ def __init__(self) -> None: SpinBox( config_field=Config.PIE_ANIMATION_TIME, parent=self, - pretty_name="Pie anumation time", + pretty_name="Pie animation time", step=0.01, max_value=1), From 13b66be90019b05192f6f7b142dd25e0ebacfcf4 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 3 Jul 2023 15:30:03 +0200 Subject: [PATCH 43/69] style: improved borders of pie and icons --- .../label_widget_impl/image_label_widget.py | 10 +++++++--- .../templates/pie_menu_utils/pie_style.py | 14 +++++++++++++- .../pie_menu_utils/pie_widget_utils/pie_painter.py | 7 +++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py index 7ac52b93..adeb00e6 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py @@ -38,12 +38,16 @@ def paintEvent(self, event: QPaintEvent) -> None: outer_radius=self.icon_radius, color=self._style.icon_color) + if self._is_unscaled: + thickness = self._style.unscaled_border_thickness + else: + thickness = self._style.border_thickness painter.paint_wheel( center=self.center, outer_radius=( - self.icon_radius-self._style.border_thickness//2), + self.icon_radius-thickness//3), color=self._border_color, - thickness=self._style.border_thickness) + thickness=thickness) painter.paint_pixmap(self.center, self.ready_image) def _prepare_image(self) -> QPixmap: @@ -56,4 +60,4 @@ def _prepare_image(self) -> QPixmap: rounded_image = PixmapTransform.make_pixmap_round(to_display) return PixmapTransform.scale_pixmap( pixmap=rounded_image, - size_px=round(self.icon_radius*1.8)) + size_px=round(self.icon_radius*1.725)) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 1fa7bba1..793b3a5e 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -95,7 +95,12 @@ def widget_radius(self) -> int: @property def border_thickness(self): """Thickness of border around icons.""" - return round(self.unscaled_icon_radius*0.06) + return round(self.icon_radius*0.09) + + @property + def unscaled_border_thickness(self): + """Thickness of border of the pie.""" + return round(self.unscaled_icon_radius*0.09) @property def area_thickness(self): @@ -176,6 +181,13 @@ def border_color(self): min(self.icon_color.blue()+15, 255), 255) + @property + def pie_border_color(self): + """Color of dark border of the pie.""" + color = self.icon_color + color = QColor(color.red()-5, color.green()-5, color.blue()-5, 120) + return color + @property def font_multiplier(self): """Multiplier to apply to the font depending on the used OS.""" diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index 42313531..d687179d 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -72,6 +72,13 @@ def _paint_base_border(self) -> None: color=self.style.border_color, thickness=self.style.border_thickness) + thickness = self.style.border_thickness + self.painter.paint_wheel( + center=self._center, + outer_radius=self.style.inner_edge_radius + thickness, + color=self.style.pie_border_color, + thickness=thickness) + def _paint_active_pie(self) -> None: """Paint a pie behind a label which is active or during animation.""" for label in self.labels: From befd5dddcfbcb74c10e8710e4fcb4f1e929c20a7 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Mon, 3 Jul 2023 15:43:21 +0200 Subject: [PATCH 44/69] bugfix: allow ScrollArea to freely expand vertically --- .../pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py | 1 + 1 file changed, 1 insertion(+) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py index 1c77bc35..3fffa436 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/scroll_area.py @@ -96,6 +96,7 @@ def _init_active_label_display(self): label.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Expanding) + label.setMaximumHeight(label.sizeHint().height()*2) label.setWordWrap(True) return label From 0e8c775ac4ab78ae7c1ff335b618db10833e9d2c Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 5 Jul 2023 15:14:16 +0200 Subject: [PATCH 45/69] Use names from krita GUI as pretty enum names --- .../api_krita/enums/blending_mode.py | 145 ++++++++++-------- .../api_krita/enums/node_types.py | 2 +- shortcut_composer/api_krita/enums/toggle.py | 2 +- shortcut_composer/api_krita/enums/tool.py | 29 ++-- 4 files changed, 105 insertions(+), 73 deletions(-) diff --git a/shortcut_composer/api_krita/enums/blending_mode.py b/shortcut_composer/api_krita/enums/blending_mode.py index 83131c74..5283be47 100644 --- a/shortcut_composer/api_krita/enums/blending_mode.py +++ b/shortcut_composer/api_krita/enums/blending_mode.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later +from typing import Optional from .helpers import EnumGroup, Group @@ -12,71 +13,75 @@ class BlendingMode(EnumGroup): """ _arithmetic = Group("Arithmetic") - ADD = "add" + ADD = "add", "Addition" DIVIDE = "divide" INVERSE_SUBTRACT = "inverse_subtract" MULTIPLY = "multiply" SUBTRACT = "subtract" _binary = Group("Binary") - AND = "and" - CONVERSE = "converse" - IMPLICATION = "implication" - NAND = "nand" - NOR = "nor" - NOT_CONVERSE = "not_converse" - NOT_IMPLICATION = "not_implication" - OR = "or" - XNOR = "xnor" - XOR = "xor" + AND = "and", "AND" + CONVERSE = "converse", "CONVERSE" + IMPLICATION = "implication", "IMPLICATION" + NAND = "nand", "NAND" + NOR = "nor", "NOR" + NOT_CONVERSE = "not_converse", "NOT_CONVERSE" + NOT_IMPLICATION = "not_implication", "NOT_IMPLICATION" + OR = "or", "OR" + XNOR = "xnor", "XNOR" + XOR = "xor", "XOR" _darken = Group("Darken") BURN = "burn" DARKEN = "darken" DARKER_COLOR = "darker color" EASY_BURN = "easy burn" - FOG_DARKEN_IFS_ILLUSIONS = "fog_darken_ifs_illusions" + FOG_DARKEN_IFS_ILLUSIONS = ( + "fog_darken_ifs_illusions", + "Fog Darken (IFS Illusions)") GAMMA_DARK = "gamma_dark" LINEAR_BURN = "linear_burn" - SHADE_IFS_ILLUSIONS = "shade_ifs_illusions" + SHADE_IFS_ILLUSIONS = ( + "shade_ifs_illusions" + "Shade (IFS Illusions)") _hsi = Group("HSI") - COLOR_HSI = "color_hsi" - DEC_INTENSITY = "dec_intensity" - DEC_SATURATION_HSI = "dec_saturation_hsi" - HUE_HSI = "hue_hsi" - INC_INTENSITY = "inc_intensity" - INC_SATURATION_HSI = "inc_saturation_hsi" + COLOR_HSI = "color_hsi", "Color HSI" + DEC_INTENSITY = "dec_intensity", "Decrease Intensity" + DEC_SATURATION_HSI = "dec_saturation_hsi", "Decrease Saturation HSI" + HUE_HSI = "hue_hsi", "Hue HSI" + INC_INTENSITY = "inc_intensity", "Increase Intensity" + INC_SATURATION_HSI = "inc_saturation_hsi", "Increase Saturation HSI" INTENSITY = "intensity" - SATURATION_HSI = "saturation_hsi" + SATURATION_HSI = "saturation_hsi", "Saturation HSI" _hsl = Group("HSL") - COLOR_HSL = "color_hsl" - DEC_LIGHTNESS = "dec_lightness" - DEC_SATURATION_HSL = "dec_saturation_hsl" - HUE_HSL = "hue_hsl" - INC_LIGHTNESS = "inc_lightness" - INC_SATURATION_HSL = "inc_saturation_hsl" + COLOR_HSL = "color_hsl", "Color HSL" + DEC_LIGHTNESS = "dec_lightness", "Decrease Lightness" + DEC_SATURATION_HSL = "dec_saturation_hsl", "Decrease Saturation HSL" + HUE_HSL = "hue_hsl", "Hue HSL" + INC_LIGHTNESS = "inc_lightness", "Increase Lightness" + INC_SATURATION_HSL = "inc_saturation_hsl", "Increase Saturation HSL" LIGHTNESS = "lightness" - SATURATION_HSL = "saturation_hsl" + SATURATION_HSL = "saturation_hsl", "Saturation HSL" _hsv = Group("HSV") - COLOR_HSV = "color_hsv" - DEC_SATURATION_HSV = "dec_saturation_hsv" - DEC_VALUE = "dec_value" - HUE_HSV = "hue_hsv" - INC_SATURATION_HSV = "inc_saturation_hsv" - INC_VALUE = "inc_value" - SATURATION_HSV = "saturation_hsv" + COLOR_HSV = "color_hsv", "Color HSV" + DEC_SATURATION_HSV = "dec_saturation_hsv", "Decrease Saturation HSV" + DEC_VALUE = "dec_value", "Decrease Value" + HUE_HSV = "hue_hsv", "Hue HSV" + INC_SATURATION_HSV = "inc_saturation_hsv", "Increase Saturation HSV" + INC_VALUE = "inc_value", "Increase Value" + SATURATION_HSV = "saturation_hsv", "Saturation HSV" VALUE = "value" _hsy = Group("HSY") COLOR = "color" - DEC_LUMINOSITY = "dec_luminosity" - DEC_SATURATION = "dec_saturation" + DEC_LUMINOSITY = "dec_luminosity", "Decrease Luminosity" + DEC_SATURATION = "dec_saturation", "Decrease Saturation" HUE = "hue" - INC_LUMINOSITY = "inc_luminosity" - INC_SATURATION = "inc_saturation" + INC_LUMINOSITY = "inc_luminosity", "Increase Luminosity" + INC_SATURATION = "inc_saturation", "Increase Saturation" LUMINIZE = "luminize" SATURATION = "saturation" @@ -84,7 +89,9 @@ class BlendingMode(EnumGroup): DODGE = "dodge" EASY_DODGE = "easy dodge" FLAT_LIGHT = "flat_light" - FOG_LIGHTEN_IFS_ILLUSIONS = "fog_lighten_ifs_illusions" + FOG_LIGHTEN_IFS_ILLUSIONS = ( + "fog_lighten_ifs_illusions", + "Fog Lighten (IFS Illusions)") GAMMA_ILLUMINATION = "gamma_illumination" GAMMA_LIGHT = "gamma_light" HARD_LIGHT = "hard_light" @@ -93,21 +100,25 @@ class BlendingMode(EnumGroup): LINEAR_DODGE = "linear_dodge" LINEAR_LIGHT = "linear light" LUMINOSITY_SAI = "luminosity_sai" - PNORM_A = "pnorm_a" - PNORM_B = "pnorm_b" + PNORM_A = "pnorm_a", "P-Norm A" + PNORM_B = "pnorm_b", "P-Norm B" PIN_LIGHT = "pin_light" - SOFT_LIGHT_IFS_ILLUSIONS = "soft_light_ifs_illusions" - SOFT_LIGHT_PEGTOP_DELPHI = "soft_light_pegtop_delphi" - SOFT_LIGHT = "soft_light" - SOFT_LIGHT_SVG = "soft_light_svg" SCREEN = "screen" + SOFT_LIGHT_IFS_ILLUSIONS = ( + "soft_light_ifs_illusions", + "Soft Light (IFS Illusions)") + SOFT_LIGHT_PEGTOP_DELPHI = ( + "soft_light_pegtop_delphi" + "Soft Light (Pegtio-Delphi)") + SOFT_LIGHT = "soft_light", "Soft Light (Photoshop)" + SOFT_LIGHT_SVG = "soft_light_svg", "Soft Light (SVG)" SUPER_LIGHT = "super_light" - TINT_IFS_ILLUSIONS = "tint_ifs_illusions" + TINT_IFS_ILLUSIONS = "tint_ifs_illusions", "Tint (IFS Illusions)" VIVID_LIGHT = "vivid_light" _misc = Group("Misc") BUMPMAP = "bumpmap" - COMBINE_NORMAL = "combine_normal" + COMBINE_NORMAL = "combine_normal", "Combine Normal Map" COPY = "copy" COPY_BLUE = "copy_blue" COPY_GREEN = "copy_green" @@ -127,11 +138,13 @@ class BlendingMode(EnumGroup): GRAIN_MERGE = "grain_merge" GREATER = "greater" HARD_MIX = "hard mix" - HARD_MIX_PHOTOSHOP = "hard_mix_photoshop" - HARD_MIX_SOFTER_PHOTOSHOP = "hard_mix_softer_photoshop" + HARD_MIX_PHOTOSHOP = "hard_mix_photoshop", "Hard Mix (Photoshop)" + HARD_MIX_SOFTER_PHOTOSHOP = ( + "hard_mix_softer_photoshop", + "Hard Mix Softer (Photoshop)") HARD_OVERLAY = "hard overlay" INTERPOLATION = "interpolation" - INTERPOLATION_2X = "interpolation 2x" + INTERPOLATION_2X = "interpolation 2x", "Interpolation - 2X" NORMAL = "normal" OVERLAY = "overlay" PARALLEL = "parallel" @@ -142,10 +155,14 @@ class BlendingMode(EnumGroup): _modulo = Group("Modulo") DIVISIVE_MODULO = "divisive_modulo" - DIVISIVE_MODULO_CONTINUOUS = "divisive_modulo_continuous" - MODULO_CONTINUOUS = "modulo_continuous" + DIVISIVE_MODULO_CONTINUOUS = ( + "divisive_modulo_continuous", + "Divisive Modulo - Continuous") + MODULO_CONTINUOUS = "modulo_continuous", "Modulo - Continuous" MODULO_SHIFT = "modulo_shift" - MODULO_SHIFT_CONTINUOUS = "modulo_shift_continuous" + MODULO_SHIFT_CONTINUOUS = ( + "modulo_shift_continuous", + "Modulo Shift - Continuous") _negative = Group("Negative") ADDITIVE_SUBTRACTIVE = "additive_subtractive" @@ -157,16 +174,24 @@ class BlendingMode(EnumGroup): _quadratic = Group("Quadratic") FREEZE = "freeze" - FREEZE_REFLECT = "freeze_reflect" + FREEZE_REFLECT = "freeze_reflect", "Freeze-Reflect" GLOW = "glow" - GLOW_HEAT = "glow_heat" + GLOW_HEAT = "glow_heat", "Glow-Heat" HEAT = "heat" - HEAT_GLOW = "heat_glow" - HEAT_GLOW_FREEZE_REFLECT_HYBRID = "heat_glow_freeze_reflect_hybrid" + HEAT_GLOW = "heat_glow", "Heat-Glow" + HEAT_GLOW_FREEZE_REFLECT_HYBRID = ( + "heat_glow_freeze_reflect_hybrid" + "Heat-Glow & Freeze-Reflect Hybrid") REFLECT = "reflect" - REFLECT_FREEZE = "reflect_freeze" + REFLECT_FREEZE = "reflect_freeze", "Reflect-Freeze" + + def __init__(self, value: str, pretty_name: Optional[str] = None): + self._value_ = value + self._custom_pretty_name = pretty_name @property def pretty_name(self) -> str: - """Format blending mode name like: `Darker color`.""" - return self.name.replace("_", " ").capitalize() + """Format blending mode name as in Krita Blending Mode combobox.""" + if self._custom_pretty_name is not None: + return self._custom_pretty_name + return self.name.replace("_", " ").title() diff --git a/shortcut_composer/api_krita/enums/node_types.py b/shortcut_composer/api_krita/enums/node_types.py index ecd1e74f..6e4c77ca 100644 --- a/shortcut_composer/api_krita/enums/node_types.py +++ b/shortcut_composer/api_krita/enums/node_types.py @@ -35,4 +35,4 @@ def icon(self) -> QIcon: @property def pretty_name(self) -> str: """Format node type name like: `Paint layer`.""" - return f"{self.name[0]}{self.name[1:].lower().replace('_', ' ')}" + return f"{self.name.replace('_', ' ').capitalize()}" diff --git a/shortcut_composer/api_krita/enums/toggle.py b/shortcut_composer/api_krita/enums/toggle.py index 41bca388..1fdea91d 100644 --- a/shortcut_composer/api_krita/enums/toggle.py +++ b/shortcut_composer/api_krita/enums/toggle.py @@ -29,7 +29,7 @@ class Toggle(Enum): @property def pretty_name(self) -> str: """Format toggle name like: `Preserve alpha`.""" - return f"{self.name[0]}{self.name[1:].lower().replace('_', ' ')}" + return f"{self.name.replace('_', ' ').capitalize()}" @property def state(self) -> bool: diff --git a/shortcut_composer/api_krita/enums/tool.py b/shortcut_composer/api_krita/enums/tool.py index 1c5f68ef..c7f8a477 100644 --- a/shortcut_composer/api_krita/enums/tool.py +++ b/shortcut_composer/api_krita/enums/tool.py @@ -3,6 +3,7 @@ from krita import Krita as Api +from typing import Optional from PyQt5.QtGui import QIcon from .helpers import EnumGroup, Group @@ -10,10 +11,10 @@ class Tool(EnumGroup): _vectors = Group("Vectors") - SHAPE_SELECT = "InteractionTool" + SHAPE_SELECT = "InteractionTool", "Select Shapes Tool" TEXT = "SvgTextTool" EDIT_SHAPES = "PathTool" - CALLIGRAPHY = "KarbonCalligraphyTool" + CALLIGRAPHY = "KarbonCalligraphyTool", "Calligraphy" _painting = Group("Painting") FREEHAND_BRUSH = "KritaShape/KisToolBrush" @@ -32,16 +33,16 @@ class Tool(EnumGroup): MOVE = "KritaTransform/KisToolMove" CROP = "KisToolCrop" GRADIENT = "KritaFill/KisToolGradient" - COLOR_SAMPLER = "KritaSelected/KisToolColorSampler" + COLOR_SAMPLER = "KritaSelected/KisToolColorSampler", "Color Sampler" COLORIZE_MASK = "KritaShape/KisToolLazyBrush" SMART_PATCH = "KritaShape/KisToolSmartPatch" FILL = "KritaFill/KisToolFill" - ENCLOSE_AND_FILL = "KisToolEncloseAndFill" + ENCLOSE_AND_FILL = "KisToolEncloseAndFill", "Enclose and Fill Tool" _utility = Group("Utility") - ASSISTANTS = "KisAssistantTool" + ASSISTANTS = "KisAssistantTool", "Assistant Tool" MEASUREMENT = "KritaShape/KisToolMeasure" - REFERENCE = "ToolReferenceImages" + REFERENCE = "ToolReferenceImages", "Reference Images Tool" _selection = Group("Selection") RECTANGULAR_SELECTION = "KisToolSelectRectangular" @@ -57,6 +58,17 @@ class Tool(EnumGroup): ZOOM = "ZoomTool" PAN = "PanTool" + def __init__(self, value: str, pretty_name: Optional[str] = None): + self._value_ = value + self._custom_pretty_name = pretty_name + + @property + def pretty_name(self) -> str: + """Format tool name as in Krita Blending Mode combobox.""" + if self._custom_pretty_name is not None: + return self._custom_pretty_name + return f"{self.name.replace('_', ' ').title()} Tool" + def activate(self): Api.instance().action(self.value).trigger() @@ -69,8 +81,3 @@ def is_paintable(cls, tool: 'Tool') -> bool: def icon(self) -> QIcon: """Return the icon of this tool.""" return Api.instance().action(self.value).icon() - - @property - def pretty_name(self) -> str: - """Format tool name like: `Shape select tool`.""" - return f"{self.name.replace('_', ' ').capitalize()} tool" From 444e5d66f7d77052210eeac31d2b7df1a4ed44e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Go=C5=82=C4=85b=20=28SirPigeonz=29?= Date: Wed, 28 Jun 2023 18:10:22 +0200 Subject: [PATCH 46/69] Created Enum with actions Contains most of actions except tool, those can be found int Tool enum and removed some script actions --- shortcut_composer/api_krita/enums/action.py | 581 ++++++++++++++++++++ 1 file changed, 581 insertions(+) create mode 100644 shortcut_composer/api_krita/enums/action.py diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py new file mode 100644 index 00000000..30095cb2 --- /dev/null +++ b/shortcut_composer/api_krita/enums/action.py @@ -0,0 +1,581 @@ +# SPDX-FileCopyrightText: © 2022 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +from krita import Krita as Api +from enum import Enum + +from PyQt5.QtGui import QIcon + + +class Action(Enum): + """ + Contains all known actions of Krita. + + Example usage: `Tool.FREEHAND_BRUSH` + + Available actions: + - `TBA` + """ + + # Filters + FILTER_APPLY_AGAIN = "filter_apply_again" # Apply Filter Again + FILTER_APPLY_REPROMPT = "filter_apply_reprompt" # Apply Filter Again (Reprompt) + ADJUST_FILTERS = "adjust_filters" # Adjust + KRITA_FILTER_ASC_CDL = "krita_filter_asc-cdl" # Slope, Offset, Power... + KRITA_FILTER_AUTOCONTRAST = "krita_filter_autocontrast" # Auto Contrast + BLUR_FILTERS = "blur_filters" # Blur + KRITA_FILTER_BLUR = "krita_filter_blur" # Blur... + KRITA_FILTER_BURN = "krita_filter_burn" # Burn... + KRITA_FILTER_COLORBALANCE = "krita_filter_colorbalance" # Colour Balance... + COLOR_FILTERS = "color_filters" # Colours + KRITA_FILTER_COLORTOALPHA = "krita_filter_colortoalpha" # Colour to Alpha... + KRITA_FILTER_COLORTRANSFER = "krita_filter_colortransfer" # Colour Transfer... + KRITA_FILTER_CROSSCHANNEL = "krita_filter_crosschannel" # Cross-channel adjustment curves... + KRITA_FILTER_DESATURATE = "krita_filter_desaturate" # Desaturate... + KRITA_FILTER_DODGE = "krita_filter_dodge" # Dodge... + EDGE_FILTERS = "edge_filters" # Edge Detection + KRITA_FILTER_EDGE_DETECTION = "krita_filter_edge detection" # Edge Detection... + EMBOSS_FILTERS = "emboss_filters" # Emboss + KRITA_FILTER_EMBOSS = "krita_filter_emboss" # Emboss with Variable Depth... + KRITA_FILTER_EMBOSS_ALL_DIRECTIONS = "krita_filter_emboss all directions" # Emboss in All Directions + KRITA_FILTER_EMBOSS_HORIZONTAL_AND_VERTICAL = "krita_filter_emboss horizontal and vertical" # Emboss Horizontal Vertical + KRITA_FILTER_EMBOSS_HORIZONTAL_ONLY = "krita_filter_emboss horizontal only" # Emboss Horizontal Only + KRITA_FILTER_EMBOSS_LAPLASCIAN = "krita_filter_emboss laplascian" # Emboss (Laplacian) + KRITA_FILTER_EMBOSS_VERTICAL_ONLY = "krita_filter_emboss vertical only" # Emboss Vertical Only + KRITA_FILTER_GAUSSIAN_BLUR = "krita_filter_gaussian blur" # Gaussian Blur... + KRITA_FILTER_GAUSSIANHIGHPASS = "krita_filter_gaussianhighpass" # Gaussian High Pass... + ENHANCE_FILTERS = "enhance_filters" # Enhance + KRITA_FILTER_GAUSSIANNOISEREDUCER = "krita_filter_gaussiannoisereducer" # Gaussian Noise Reduction... + MAP_FILTERS = "map_filters" # Map + KRITA_FILTER_GRADIENTMAP = "krita_filter_gradientmap" # Gradient Map... + ARTISTIC_FILTERS = "artistic_filters" # Artistic + KRITA_FILTER_HALFTONE = "krita_filter_halftone" # Halftone... + KRITA_FILTER_HEIGHT_TO_NORMAL = "krita_filter_height to normal" # Height to Normal Map... + KRITA_FILTER_HSVADJUSTMENT = "krita_filter_hsvadjustment" # HSV Adjustment... + KRITA_FILTER_INDEXCOLORS = "krita_filter_indexcolors" # Index Colours... + KRITA_FILTER_INVERT = "krita_filter_invert" # Invert + KRITA_FILTER_LENS_BLUR = "krita_filter_lens blur" # Lens Blur... + KRITA_FILTER_LEVELS = "krita_filter_levels" # Levels... + KRITA_FILTER_MAXIMIZE = "krita_filter_maximize" # Maximise Channel + KRITA_FILTER_MEAN_REMOVAL = "krita_filter_mean removal" # Mean Removal + KRITA_FILTER_MINIMIZE = "krita_filter_minimize" # Minimise Channel + KRITA_FILTER_MOTION_BLUR = "krita_filter_motion blur" # Motion Blur... + OTHER_FILTERS = "other_filters" # Other + KRITA_FILTER_NOISE = "krita_filter_noise" # Random Noise... + KRITA_FILTER_NORMALIZE = "krita_filter_normalize" # Normalise + KRITA_FILTER_OILPAINT = "krita_filter_oilpaint" # Oilpaint... + KRITA_FILTER_PALETTIZE = "krita_filter_palettize" # Palettise... + KRITA_FILTER_PERCHANNEL = "krita_filter_perchannel" # Colour Adjustment curves... + KRITA_FILTER_PHONGBUMPMAP = "krita_filter_phongbumpmap" # Phong Bumpmap... + KRITA_FILTER_PIXELIZE = "krita_filter_pixelize" # Pixelise... + KRITA_FILTER_POSTERIZE = "krita_filter_posterize" # Posterise... + KRITA_FILTER_RAINDROPS = "krita_filter_raindrops" # Raindrops... + KRITA_FILTER_RANDOMPICK = "krita_filter_randompick" # Random Pick... + KRITA_FILTER_ROUNDCORNERS = "krita_filter_roundcorners" # Round Corners... + KRITA_FILTER_SHARPEN = "krita_filter_sharpen" # Sharpen + KRITA_FILTER_SMALLTILES = "krita_filter_smalltiles" # Small Tiles... + KRITA_FILTER_THRESHOLD = "krita_filter_threshold" # Threshold... + KRITA_FILTER_UNSHARP = "krita_filter_unsharp" # Unsharp Mask... + KRITA_FILTER_WAVE = "krita_filter_wave" # Wave... + KRITA_FILTER_WAVELETNOISEREDUCER = "krita_filter_waveletnoisereducer" # Wavelet Noise Reducer... + Q_MIC = "QMic" # Start G'MIC-Qt + Q_MIC_AGAIN = "QMicAgain" # Re-apply the last G'MIC filter + + # Untagged + SAVE_INCREMENTAL_VERSION = "save_incremental_version" # Save Incremental Version + SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" # Save Incremental Backup + TABLET_DEBUGGER = "tablet_debugger" # Toggle Tablet Debugger + CREATE_TEMPLATE = "create_template" # Create Template From Image... + CREATE_COPY = "create_copy" # Create Copy From Current Image + OPEN_RESOURCES_DIRECTORY = "open_resources_directory" # Open Resources Folder + ROTATE_CANVAS_RIGHT = "rotate_canvas_right" # Rotate Canvas Right + ROTATE_CANVAS_LEFT = "rotate_canvas_left" # Rotate Canvas Left + RESET_CANVAS_ROTATION = "reset_canvas_rotation" # Reset Canvas Rotation + WRAP_AROUND_MODE = "wrap_around_mode" # Wrap Around Mode + LEVEL_OF_DETAIL_MODE = "level_of_detail_mode" # Instant Preview Mode + SOFT_PROOF = "softProof" # Soft Proofing + GAMUT_CHECK = "gamutCheck" # Out of Gamut Warnings + SHOW_STATUS_BAR = "showStatusBar" # Show Status Bar + VIEW_SHOW_CANVAS_ONLY = "view_show_canvas_only" # Show Canvas Only + RULER_PIXEL_MULTIPLE2 = "ruler_pixel_multiple2" # Use multiple of 2 for pixel scale + VIEW_RULER = "view_ruler" # Show Rulers + RULERS_TRACK_MOUSE = "rulers_track_mouse" # Rulers Track Pointer + ZOOM_TO_100PCT = "zoom_to_100pct" # Reset zoom + VIEW_ZOOM_IN = "view_zoom_in" # Zoom In + VIEW_ZOOM_OUT = "view_zoom_out" # Zoom Out + TOGGLE_ZOOM_TO_FIT = "toggle_zoom_to_fit" # Toggle Zoom Fit to Page + SETTINGS_ACTIVE_AUTHOR = "settings_active_author" # Active Author Profile + VIEW_PIXEL_GRID = "view_pixel_grid" # Show Pixel Grid + TOGGLE_FG_BG = "toggle_fg_bg" # Swap Foreground and Background Colours + RESET_FG_BG = "reset_fg_bg" # Set Foreground and Background Colours to Black and White + TOGGLE_BRUSH_OUTLINE = "toggle_brush_outline" # Toggle Brush Outline + EDIT_CUT = "edit_cut" # Cut + EDIT_COPY = "edit_copy" # Copy + EDIT_PASTE = "edit_paste" # Paste + COPY_SHARP = "copy_sharp" # Copy (sharp) + CUT_SHARP = "cut_sharp" # Cut (sharp) + PASTE_NEW = "paste_new" # Paste into New Image + PASTE_AT = "paste_at" # Paste at Cursor + PASTE_INTO = "paste_into" # Paste into Active Layer + PASTE_AS_REFERENCE = "paste_as_reference" # Paste as Reference Image + PASTE_SHAPE_STYLE = "paste_shape_style" # Paste Shape Style + COPY_MERGED = "copy_merged" # Copy merged + SELECT_ALL = "select_all" # Select All + DESELECT = "deselect" # Deselect + CLEAR = "clear" # Clear + RESELECT = "reselect" # Reselect + INVERT_SELECTION = "invert_selection" # Invert Selection + COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" # Copy Selection to New Layer + CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" # Cut Selection to New Layer + FILL_SELECTION_FOREGROUND_COLOR = "fill_selection_foreground_color" # Fill with Foreground Colour + FILL_SELECTION_BACKGROUND_COLOR = "fill_selection_background_color" # Fill with Background Colour + FILL_SELECTION_PATTERN = "fill_selection_pattern" # Fill with Pattern + FILL_SELECTION_FOREGROUND_COLOR_OPACITY = "fill_selection_foreground_color_opacity" # Fill with Foreground Colour (Opacity) + FILL_SELECTION_BACKGROUND_COLOR_OPACITY = "fill_selection_background_color_opacity" # Fill with Background Colour (Opacity) + FILL_SELECTION_PATTERN_OPACITY = "fill_selection_pattern_opacity" # Fill with Pattern (Opacity) + STROKE_SHAPES = "stroke_shapes" # Stroke selected shapes + TOGGLE_DISPLAY_SELECTION = "toggle_display_selection" # Display Selection + RESIZEIMAGETOSELECTION = "resizeimagetoselection" # Trim to Selection + EDIT_SELECTION = "edit_selection" # Edit Selection + CONVERT_TO_VECTOR_SELECTION = "convert_to_vector_selection" # Convert to Vector Selection + CONVERT_TO_RASTER_SELECTION = "convert_to_raster_selection" # Convert to Raster Selection + CONVERT_SHAPES_TO_VECTOR_SELECTION = "convert_shapes_to_vector_selection" # Convert Shapes to Vector Selection + CONVERT_SELECTION_TO_SHAPE = "convert_selection_to_shape" # Convert to Shape + TOGGLE_SELECTION_OVERLAY_MODE = "toggle-selection-overlay-mode" # Toggle Selection Display Mode + STROKE_SELECTION = "stroke_selection" # Stroke Selection... + VIEW_SHOW_GUIDES = "view_show_guides" # Show Guides + VIEW_LOCK_GUIDES = "view_lock_guides" # Lock Guides + VIEW_SNAP_TO_GUIDES = "view_snap_to_guides" # Snap to Guides + SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" # Show Snap Options Popup + VIEW_SNAP_ORTHOGONAL = "view_snap_orthogonal" # Snap Orthogonal + VIEW_SNAP_NODE = "view_snap_node" # Snap Node + VIEW_SNAP_EXTENSION = "view_snap_extension" # Snap Extension + VIEW_SNAP_INTERSECTION = "view_snap_intersection" # Snap Intersection + VIEW_SNAP_BOUNDING_BOX = "view_snap_bounding_box" # Snap Bounding Box + VIEW_SNAP_IMAGE_BOUNDS = "view_snap_image_bounds" # Snap Image Bounds + VIEW_SNAP_IMAGE_CENTER = "view_snap_image_center" # Snap Image Centre + VIEW_SNAP_TO_PIXEL = "view_snap_to_pixel" # Snap Pixel + FLATTEN_IMAGE = "flatten_image" # Flatten image + MERGE_LAYER = "merge_layer" # Merge with Layer Below + FLATTEN_LAYER = "flatten_layer" # Flatten Layer + SAVE_GROUPS_AS_IMAGES = "save_groups_as_images" # Save Group Layers... + CONVERT_GROUP_TO_ANIMATED = "convert_group_to_animated" # Convert group to animated layer + RESIZEIMAGETOLAYER = "resizeimagetolayer" # Trim to Current Layer + TRIM_TO_IMAGE = "trim_to_image" # Trim to Image Size + LAYER_STYLE = "layer_style" # Layer Style... + COPY_LAYER_STYLE = "copy_layer_style" # Copy Layer Style + PASTE_LAYER_STYLE = "paste_layer_style" # Paste Layer Style + MIRROR_NODE_X = "mirrorNodeX" # Mirror Layer Horizontally + MIRROR_NODE_Y = "mirrorNodeY" # Mirror Layer Vertically + MIRROR_ALL_NODES_X = "mirrorAllNodesX" # Mirror All Layers Horizontally + MIRROR_ALL_NODES_Y = "mirrorAllNodesY" # Mirror All Layers Vertically + ACTIVATE_NEXT_LAYER = "activateNextLayer" # Activate next layer + ACTIVATE_NEXT_SIBLING_LAYER = "activateNextSiblingLayer" # Activate next sibling layer, skipping over groups. + ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" # Activate previous layer + ACTIVATE_PREVIOUS_SIBLING_LAYER = "activatePreviousSiblingLayer" # Activate previous sibling layer, skipping over groups. + SWITCH_TO_PREVIOUSLY_ACTIVE_NODE = "switchToPreviouslyActiveNode" # Activate previously selected layer + SAVE_NODE_AS_IMAGE = "save_node_as_image" # Save Layer/Mask... + SAVE_VECTOR_NODE_TO_SVG = "save_vector_node_to_svg" # Save Vector Layer as SVG... + DUPLICATELAYER = "duplicatelayer" # Duplicate Layer or Mask + COPY_LAYER_CLIPBOARD = "copy_layer_clipboard" # Copy Layer + CUT_LAYER_CLIPBOARD = "cut_layer_clipboard" # Cut Layer + PASTE_LAYER_FROM_CLIPBOARD = "paste_layer_from_clipboard" # Paste Layer + CREATE_QUICK_GROUP = "create_quick_group" # Quick Group + CREATE_QUICK_CLIPPING_GROUP = "create_quick_clipping_group" # Quick Clipping Group + QUICK_UNGROUP = "quick_ungroup" # Quick Ungroup + SELECT_ALL_LAYERS = "select_all_layers" # All Layers + SELECT_VISIBLE_LAYERS = "select_visible_layers" # Visible Layers + SELECT_LOCKED_LAYERS = "select_locked_layers" # Locked Layers + SELECT_INVISIBLE_LAYERS = "select_invisible_layers" # Invisible Layers + SELECT_UNLOCKED_LAYERS = "select_unlocked_layers" # Unlocked Layers + NEW_FROM_VISIBLE = "new_from_visible" # New Layer From Visible + PIN_TO_TIMELINE = "pin_to_timeline" # Pin to Timeline + ADD_NEW_PAINT_LAYER = "add_new_paint_layer" # Add Paint Layer + ADD_NEW_GROUP_LAYER = "add_new_group_layer" # Add Group Layer + ADD_NEW_CLONE_LAYER = "add_new_clone_layer" # Add Clone Layer + ADD_NEW_SHAPE_LAYER = "add_new_shape_layer" # Add Vector Layer + ADD_NEW_ADJUSTMENT_LAYER = "add_new_adjustment_layer" # Add Filter Layer... + ADD_NEW_FILL_LAYER = "add_new_fill_layer" # Add Fill Layer... + ADD_NEW_FILE_LAYER = "add_new_file_layer" # Add File Layer... + ADD_NEW_TRANSPARENCY_MASK = "add_new_transparency_mask" # Add Transparency Mask + ADD_NEW_FILTER_MASK = "add_new_filter_mask" # Add Filter Mask... + ADD_NEW_COLORIZE_MASK = "add_new_colorize_mask" # Add Colourise Mask + ADD_NEW_TRANSFORM_MASK = "add_new_transform_mask" # Add Transform Mask + ADD_NEW_SELECTION_MASK = "add_new_selection_mask" # Add Local Selection + CONVERT_TO_PAINT_LAYER = "convert_to_paint_layer" # Convert to Paint Layer + CONVERT_TO_SELECTION_MASK = "convert_to_selection_mask" # Convert to Selection Mask + CONVERT_TO_FILTER_MASK = "convert_to_filter_mask" # Convert to Filter Mask... + CONVERT_TO_TRANSPARENCY_MASK = "convert_to_transparency_mask" # Convert to Transparency Mask + CONVERT_TO_ANIMATED = "convert_to_animated" # Convert to animated layer + CONVERT_TO_FILE_LAYER = "convert_to_file_layer" # to File Layer... + ISOLATE_ACTIVE_LAYER = "isolate_active_layer" # Isolate Active Layer + ISOLATE_ACTIVE_GROUP = "isolate_active_group" # Isolate Active Group + TOGGLE_LAYER_VISIBILITY = "toggle_layer_visibility" # Toggle layer visibility + TOGGLE_LAYER_LOCK = "toggle_layer_lock" # Toggle layer lock + TOGGLE_LAYER_INHERIT_ALPHA = "toggle_layer_inherit_alpha" # Toggle layer alpha inheritance + TOGGLE_LAYER_ALPHA_LOCK = "toggle_layer_alpha_lock" # Toggle layer alpha + SPLIT_ALPHA_INTO_MASK = "split_alpha_into_mask" # Alpha into Mask + SPLIT_ALPHA_WRITE = "split_alpha_write" # Write as Alpha + SPLIT_ALPHA_SAVE_MERGED = "split_alpha_save_merged" # Save Merged... + IMPORT_LAYER_FROM_FILE = "import_layer_from_file" # Import Layer... + IMAGE_PROPERTIES = "image_properties" # Properties... + IMPORT_LAYER_AS_PAINT_LAYER = "import_layer_as_paint_layer" # as Paint Layer... + IMPORT_LAYER_AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" # as Transparency Mask... + IMPORT_LAYER_AS_FILTER_MASK = "import_layer_as_filter_mask" # as Filter Mask... + IMPORT_LAYER_AS_SELECTION_MASK = "import_layer_as_selection_mask" # as Selection Mask... + IMAGE_COLOR = "image_color" # Image Background Colour and Transparency... + VIEW_GRID = "view_grid" # Show Grid + VIEW_SNAP_TO_GRID = "view_snap_to_grid" # Snap To Grid + VIEW_TOGGLE_PAINTING_ASSISTANTS = "view_toggle_painting_assistants" # Show Painting Assistants + VIEW_TOGGLE_ASSISTANT_PREVIEWS = "view_toggle_assistant_previews" # Show Assistant Previews + VIEW_TOGGLE_REFERENCE_IMAGES = "view_toggle_reference_images" # Show Reference Images + MAKE_BRUSH_COLOR_LIGHTER = "make_brush_color_lighter" # Make brush colour lighter + MAKE_BRUSH_COLOR_DARKER = "make_brush_color_darker" # Make brush colour darker + MAKE_BRUSH_COLOR_SATURATED = "make_brush_color_saturated" # Make brush colour more saturated + MAKE_BRUSH_COLOR_DESATURATED = "make_brush_color_desaturated" # Make brush colour more desaturated + SHIFT_BRUSH_COLOR_CLOCKWISE = "shift_brush_color_clockwise" # Shift brush colour hue clockwise + SHIFT_BRUSH_COLOR_COUNTER_CLOCKWISE = "shift_brush_color_counter_clockwise" # Shift brush colour hue counter-clockwise + MAKE_BRUSH_COLOR_REDDER = "make_brush_color_redder" # Make brush colour more red + MAKE_BRUSH_COLOR_GREENER = "make_brush_color_greener" # Make brush colour more green + MAKE_BRUSH_COLOR_BLUER = "make_brush_color_bluer" # Make brush colour more blue + MAKE_BRUSH_COLOR_YELLOWER = "make_brush_color_yellower" # Make brush colour more yellow + INCREASE_OPACITY = "increase_opacity" # Increase Opacity + DECREASE_OPACITY = "decrease_opacity" # Decrease Opacity + INCREASE_FLOW = "increase_flow" # Increase Flow + DECREASE_FLOW = "decrease_flow" # Decrease Flow + INCREASE_FADE = "increase_fade" # Increase Fade + DECREASE_FADE = "decrease_fade" # Decrease Fade + INCREASE_SCATTER = "increase_scatter" # Increase Scatter + DECREASE_SCATTER = "decrease_scatter" # Decrease Scatter + MIRROR_CANVAS = "mirror_canvas" # Mirror View + MIRROR_CANVAS_AROUND_CURSOR = "mirror_canvas_around_cursor" # Mirror View Around Cursor + PATTERNS = "patterns" # Patterns + GRADIENTS = "gradients" # Gradients + DUAL = "dual" # Choose foreground and background colours + ERASE_ACTION = "erase_action" # Set eraser mode + RELOAD_PRESET_ACTION = "reload_preset_action" # Reload Original Preset + PRESERVE_ALPHA = "preserve_alpha" # Preserve Alpha + MIRROR_X_HIDE_DECORATIONS = "mirrorX-hideDecorations" # Hide Mirror X Line + MIRROR_X_LOCK = "mirrorX-lock" # Lock X Line + MIRROR_X_MOVE_TO_CENTER = "mirrorX-moveToCenter" # Move to Canvas Centre X + MIRROR_Y_HIDE_DECORATIONS = "mirrorY-hideDecorations" # Hide Mirror Y Line + MIRROR_Y_LOCK = "mirrorY-lock" # Lock Y Line + MIRROR_Y_MOVE_TO_CENTER = "mirrorY-moveToCenter" # Move to Canvas Centre Y + H_MIRROR_ACTION = "hmirror_action" # Horizontal Mirror Tool + V_MIRROR_ACTION = "vmirror_action" # Vertical Mirror Tool + NEXT_BLENDING_MODE = "Next Blending Mode" # Next Blending Mode + PREVIOUS_BLENDING_MODE = "Previous Blending Mode" # Previous Blending Mode + SELECT_NORMAL_BLENDING_MODE = "Select Normal Blending Mode" # Select Normal Blending Mode + SELECT_DISSOLVE_BLENDING_MODE = "Select Dissolve Blending Mode" # Select Dissolve Blending Mode + SELECT_BEHIND_BLENDING_MODE = "Select Behind Blending Mode" # Select Behind Blending Mode + SELECT_CLEAR_BLENDING_MODE = "Select Clear Blending Mode" # Select Clear Blending Mode + SELECT_DARKEN_BLENDING_MODE = "Select Darken Blending Mode" # Select Darken Blending Mode + SELECT_MULTIPLY_BLENDING_MODE = "Select Multiply Blending Mode" # Select Multiply Blending Mode + SELECT_COLOR_BURN_BLENDING_MODE = "Select Color Burn Blending Mode" # Select Colour Burn Blending Mode + SELECT_LINEAR_BURN_BLENDING_MODE = "Select Linear Burn Blending Mode" # Select Linear Burn Blending Mode + SELECT_LIGHTEN_BLENDING_MODE = "Select Lighten Blending Mode" # Select Lighten Blending Mode + SELECT_SCREEN_BLENDING_MODE = "Select Screen Blending Mode" # Select Screen Blending Mode + SELECT_COLOR_DODGE_BLENDING_MODE = "Select Color Dodge Blending Mode" # Select Colour Dodge Blending Mode + SELECT_LINEAR_DODGE_BLENDING_MODE = "Select Linear Dodge Blending Mode" # Select Linear Dodge Blending Mode + SELECT_OVERLAY_BLENDING_MODE = "Select Overlay Blending Mode" # Select Overlay Blending Mode + SELECT_HARD_OVERLAY_BLENDING_MODE = "Select Hard Overlay Blending Mode" # Select Hard Overlay Blending Mode + SELECT_SOFT_LIGHT_BLENDING_MODE = "Select Soft Light Blending Mode" # Select Soft Light Blending Mode + SELECT_HARD_LIGHT_BLENDING_MODE = "Select Hard Light Blending Mode" # Select Hard Light Blending Mode + SELECT_VIVID_LIGHT_BLENDING_MODE = "Select Vivid Light Blending Mode" # Select Vivid Light Blending Mode + SELECT_LINEAR_LIGHT_BLENDING_MODE = "Select Linear Light Blending Mode" # Select Linear Light Blending Mode + SELECT_PIN_LIGHT_BLENDING_MODE = "Select Pin Light Blending Mode" # Select Pin Light Blending Mode + SELECT_HARD_MIX_BLENDING_MODE = "Select Hard Mix Blending Mode" # Select Hard Mix Blending Mode + SELECT_DIFFERENCE_BLENDING_MODE = "Select Difference Blending Mode" # Select Difference Blending Mode + SELECT_EXCLUSION_BLENDING_MODE = "Select Exclusion Blending Mode" # Select Exclusion Blending Mode + SELECT_HUE_BLENDING_MODE = "Select Hue Blending Mode" # Select Hue Blending Mode + SELECT_SATURATION_BLENDING_MODE = "Select Saturation Blending Mode" # Select Saturation Blending Mode + SELECT_COLOR_BLENDING_MODE = "Select Color Blending Mode" # Select Colour Blending Mode + SELECT_LUMINOSITY_BLENDING_MODE = "Select Luminosity Blending Mode" # Select Luminosity Blending Mode + COMPOSITE_ACTIONS = "composite_actions" # Brush composite + BRUSHSLIDER1 = "brushslider1" # Brush option slider 1 + BRUSHSLIDER2 = "brushslider2" # Brush option slider 2 + BRUSHSLIDER3 = "brushslider3" # Brush option slider 3 + BRUSHSLIDER4 = "brushslider4" # Brush option slider 4 + NEXT_FAVORITE_PRESET = "next_favorite_preset" # Next Favourite Preset + PREVIOUS_FAVORITE_PRESET = "previous_favorite_preset" # Previous Favourite Preset + PREVIOUS_PRESET = "previous_preset" # Switch to Previous Preset + SHOW_BRUSH_PRESETS = "show_brush_presets" # Show Brush Presets + MIRROR_ACTIONS = "mirror_actions" # Mirror + WORKSPACES = "workspaces" # Workspaces + SHOW_BRUSH_EDITOR = "show_brush_editor" # Show Brush Editor + DISABLE_PRESSURE = "disable_pressure" # Use Pen Pressure + PAINTOPS = "paintops" # Painter's Tools + CHOOSE_FOREGROUND_COLOR = "chooseForegroundColor" # Open Foreground colour selector + CHOOSE_BACKGROUND_COLOR = "chooseBackgroundColor" # Open Background colour selector + IMAGESIZE = "imagesize" # Scale Image To New Size... + CANVASSIZE = "canvassize" # Resize Canvas... + LAYERSIZE = "layersize" # Scale Layer to new Size... + SCALE_ALL_LAYERS = "scaleAllLayers" # Scale All Layers to new Size... + SELECTIONSCALE = "selectionscale" # Scale... + BUGINFO = "buginfo" # Show Krita log for bug reports. + SYSINFO = "sysinfo" # Show system information for bug reports. + CRASHLOG = "crashlog" # Show crash log for bug reports. + CLONES_ARRAY = "clones_array" # Clones Array... + COLORRANGE = "colorrange" # Select from Colour Range... + SELECTOPAQUE = "selectopaque" # Select Opaque (Replace) + SELECTOPAQUE_ADD = "selectopaque_add" # Select Opaque (Add) + SELECTOPAQUE_SUBTRACT = "selectopaque_subtract" # Select Opaque (Subtract) + SELECTOPAQUE_INTERSECT = "selectopaque_intersect" # Select Opaque (Intersect) + IMAGECOLORSPACECONVERSION = "imagecolorspaceconversion" # Convert Image Colour Space... + LAYERCOLORSPACECONVERSION = "layercolorspaceconversion" # Convert Layer Colour Space... + DBEXPLORER = "dbexplorer" # Explore Resources Cache Database... + IMAGESPLIT = "imagesplit" # Image Split + LAYER_GROUP_SWITCHER_PREVIOUS = "LayerGroupSwitcher/previous" # Move into previous group + LAYER_GROUP_SWITCHER_NEXT = "LayerGroupSwitcher/next" # Move into next group + LAYERSPLIT = "layersplit" # Split Layer... + EDIT_LAYER_META_DATA = "EditLayerMetaData" # Edit metadata... + GROWSELECTION = "growselection" # Grow Selection... + SHRINKSELECTION = "shrinkselection" # Shrink Selection... + BORDERSELECTION = "borderselection" # Border Selection... + FEATHERSELECTION = "featherselection" # Feather Selection... + SMOOTHSELECTION = "smoothselection" # Smooth + OFFSETIMAGE = "offsetimage" # Offset Image... + OFFSETLAYER = "offsetlayer" # Offset Layer... + MANAGE_BUNDLES = "manage_bundles" # Manage Resource Libraries... + MANAGE_RESOURCES = "manage_resources" # Manage Resources... + ROTATEIMAGE = "rotateimage" # Rotate Image... + ROTATE_IMAGE_C_W90 = "rotateImageCW90" # Rotate Image 90° to the Right + ROTATE_IMAGE180 = "rotateImage180" # Rotate Image 180° + ROTATE_IMAGE_C_C_W90 = "rotateImageCCW90" # Rotate Image 90° to the Left + MIRROR_IMAGE_HORIZONTAL = "mirrorImageHorizontal" # Mirror Image Horizontally + MIRROR_IMAGE_VERTICAL = "mirrorImageVertical" # Mirror Image Vertically + ROTATELAYER = "rotatelayer" # Rotate Layer... + ROTATE_LAYER180 = "rotateLayer180" # Rotate Layer 180° + ROTATE_LAYER_C_W90 = "rotateLayerCW90" # Rotate Layer 90° to the Right + ROTATE_LAYER_C_C_W90 = "rotateLayerCCW90" # Rotate Layer 90° to the Left + ROTATE_ALL_LAYERS = "rotateAllLayers" # Rotate All Layers... + ROTATE_ALL_LAYERS_C_W90 = "rotateAllLayersCW90" # Rotate All Layers 90° to the Right + ROTATE_ALL_LAYERS_C_C_W90 = "rotateAllLayersCCW90" # Rotate All Layers 90° to the Left + ROTATE_ALL_LAYERS180 = "rotateAllLayers180" # Rotate All Layers 180° + SEPARATE = "separate" # Separate Image... + SHEARIMAGE = "shearimage" # Shear Image... + SHEARLAYER = "shearlayer" # Shear Layer... + SHEAR_ALL_LAYERS = "shearAllLayers" # Shear All Layers... + WAVELETDECOMPOSE = "waveletdecompose" # Wavelet Decompose ... + INCREASE_BRUSH_SIZE = "increase_brush_size" # Increase Brush Size + DECREASE_BRUSH_SIZE = "decrease_brush_size" # Decrease Brush Size + SELECTION_TOOL_MODE_ADD = "selection_tool_mode_add" # Selection Mode: Add + SELECTION_TOOL_MODE_REPLACE = "selection_tool_mode_replace" # Selection Mode: Replace + SELECTION_TOOL_MODE_SUBTRACT = "selection_tool_mode_subtract" # Selection Mode: Subtract + SELECTION_TOOL_MODE_INTERSECT = "selection_tool_mode_intersect" # Selection Mode: Intersect + UNDO_POLYGON_SELECTION = "undo_polygon_selection" # Undo Polygon Selection Points + OBJECT_ORDER_FRONT = "object_order_front" # Bring to Front + OBJECT_ORDER_RAISE = "object_order_raise" # Raise + OBJECT_ORDER_LOWER = "object_order_lower" # Lower + OBJECT_ORDER_BACK = "object_order_back" # Send to Back + OBJECT_TRANSFORM_ROTATE_90_CW = "object_transform_rotate_90_cw" # Rotate 90° CW + OBJECT_TRANSFORM_ROTATE_90_CCW = "object_transform_rotate_90_ccw" # Rotate 90° ACW + OBJECT_TRANSFORM_ROTATE_180 = "object_transform_rotate_180" # Rotate 180° + OBJECT_TRANSFORM_MIRROR_HORIZONTALLY = "object_transform_mirror_horizontally" # Mirror Horizontally + OBJECT_TRANSFORM_MIRROR_VERTICALLY = "object_transform_mirror_vertically" # Mirror Vertically + OBJECT_TRANSFORM_RESET = "object_transform_reset" # Reset Transformations + SET_NO_BRUSH_SMOOTHING = "set_no_brush_smoothing" # Brush Smoothing: Disabled + SET_SIMPLE_BRUSH_SMOOTHING = "set_simple_brush_smoothing" # Brush Smoothing: Basic + SET_WEIGHTED_BRUSH_SMOOTHING = "set_weighted_brush_smoothing" # Brush Smoothing: Weighted + SET_STABILIZER_BRUSH_SMOOTHING = "set_stabilizer_brush_smoothing" # Brush Smoothing: Stabiliser + TOGGLE_ASSISTANT = "toggle_assistant" # Toggle Snap To Assistants + CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" # Calligraphy: increase width + CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" # Calligraphy: decrease width + CALLIGRAPHY_INCREASE_ANGLE = "calligraphy_increase_angle" # Calligraphy: increase angle + CALLIGRAPHY_DECREASE_ANGLE = "calligraphy_decrease_angle" # Calligraphy: decrease angle + OBJECT_ALIGN_HORIZONTAL_LEFT = "object_align_horizontal_left" # Align Left + OBJECT_ALIGN_HORIZONTAL_CENTER = "object_align_horizontal_center" # Horizontally Centre + OBJECT_ALIGN_HORIZONTAL_RIGHT = "object_align_horizontal_right" # Align Right + OBJECT_ALIGN_VERTICAL_TOP = "object_align_vertical_top" # Align Top + OBJECT_ALIGN_VERTICAL_CENTER = "object_align_vertical_center" # Vertically Centre + OBJECT_ALIGN_VERTICAL_BOTTOM = "object_align_vertical_bottom" # Align Bottom + OBJECT_DISTRIBUTE_HORIZONTAL_LEFT = "object_distribute_horizontal_left" # Distribute Left + OBJECT_DISTRIBUTE_HORIZONTAL_CENTER = "object_distribute_horizontal_center" # Distribute Centres Horizontally + OBJECT_DISTRIBUTE_HORIZONTAL_RIGHT = "object_distribute_horizontal_right" # Distribute Right + OBJECT_DISTRIBUTE_HORIZONTAL_GAPS = "object_distribute_horizontal_gaps" # Distribute Horizontal Gap + OBJECT_DISTRIBUTE_VERTICAL_TOP = "object_distribute_vertical_top" # Distribute Top + OBJECT_DISTRIBUTE_VERTICAL_CENTER = "object_distribute_vertical_center" # Distribute Centres Vertically + OBJECT_DISTRIBUTE_VERTICAL_BOTTOM = "object_distribute_vertical_bottom" # Distribute Bottom + OBJECT_DISTRIBUTE_VERTICAL_GAPS = "object_distribute_vertical_gaps" # Distribute Vertical Gap + OBJECT_GROUP = "object_group" # Group + OBJECT_UNGROUP = "object_ungroup" # Ungroup + OBJECT_UNITE = "object_unite" # Unite + OBJECT_INTERSECT = "object_intersect" # Intersect + OBJECT_SUBTRACT = "object_subtract" # Subtract + OBJECT_SPLIT = "object_split" # Split + PATH_TOOL = "PathTool" # Edit Shapes Tool + PATHPOINT_CORNER = "pathpoint-corner" # Corner point + PATHPOINT_SMOOTH = "pathpoint-smooth" # Smooth point + PATHPOINT_SYMMETRIC = "pathpoint-symmetric" # Symmetric Point + PATHPOINT_CURVE = "pathpoint-curve" # Make curve point + PATHPOINT_LINE = "pathpoint-line" # Make line point + PATHSEGMENT_LINE = "pathsegment-line" # Segment to Line + PATHSEGMENT_CURVE = "pathsegment-curve" # Segment to Curve + PATHPOINT_INSERT = "pathpoint-insert" # Insert point + PATHPOINT_REMOVE = "pathpoint-remove" # Remove point + PATH_BREAK_POINT = "path-break-point" # Break at point + PATH_BREAK_SEGMENT = "path-break-segment" # Break at segment + PATHPOINT_JOIN = "pathpoint-join" # Join with segment + PATHPOINT_MERGE = "pathpoint-merge" # Merge points + CONVERT_TO_PATH = "convert-to-path" # To Path + MOVETOOL_MOVE_UP = "movetool-move-up" # Move up + MOVETOOL_MOVE_DOWN = "movetool-move-down" # Move down + MOVETOOL_MOVE_LEFT = "movetool-move-left" # Move left + MOVETOOL_MOVE_RIGHT = "movetool-move-right" # Move right + MOVETOOL_MOVE_UP_MORE = "movetool-move-up-more" # Move up more + MOVETOOL_MOVE_DOWN_MORE = "movetool-move-down-more" # Move down more + MOVETOOL_MOVE_LEFT_MORE = "movetool-move-left-more" # Move left more + MOVETOOL_MOVE_RIGHT_MORE = "movetool-move-right-more" # Move right more + MOVETOOL_SHOW_COORDINATES = "movetool-show-coordinates" # Show Coordinates + ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" # Add scalar keyframes + REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" # Remove scalar keyframe + INTERPOLATION_CONSTANT = "interpolation_constant" # Hold constant value. No interpolation. + INTERPOLATION_LINEAR = "interpolation_linear" # Linear interpolation. + INTERPOLATION_BEZIER = "interpolation_bezier" # Bezier curve interpolation. + TANGENTS_SHARP = "tangents_sharp" # Sharp interpolation tangents. + TANGENTS_SMOOTH = "tangents_smooth" # Smooth interpolation tangents. + ZOOM_TO_FIT_RANGE = "zoom_to_fit_range" # Zoom view to fit channel range. + ZOOM_TO_FIT_CURVE = "zoom_to_fit_curve" # Zoom view to fit curve. + DROP_FRAMES = "drop_frames" # Drop Frames + SHOW_GLOBAL_SELECTION_MASK = "show-global-selection-mask" # Show Global Selection Mask + RENAME_CURRENT_LAYER = "RenameCurrentLayer" # Rename current layer + LAYER_PROPERTIES = "layer_properties" # Properties... + REMOVE_LAYER = "remove_layer" # Remove Layer + MOVE_LAYER_UP = "move_layer_up" # Move Layer or Mask Up + MOVE_LAYER_DOWN = "move_layer_down" # Move Layer or Mask Down + SET_COPY_FROM = "set-copy-from" # Set Copy From... + TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" # Toggle Layer Soloing + TOGGLE_ONION_SKIN = "toggle_onion_skin" # Toggle onion skin + CREATE_SNAPSHOT = "create_snapshot" # Create Snapshot + SWITCHTO_SNAPSHOT = "switchto_snapshot" # Switch to Selected Snapshot + REMOVE_SNAPSHOT = "remove_snapshot" # Remove Selected Snapshot + INSERT_COLUMN_LEFT = "insert_column_left" # Insert Column Left + INSERT_COLUMN_RIGHT = "insert_column_right" # Insert Column Right + INSERT_MULTIPLE_COLUMNS = "insert_multiple_columns" # Insert Multiple Columns + REMOVE_COLUMNS_AND_PULL = "remove_columns_and_pull" # Remove Column and Pull + REMOVE_COLUMNS = "remove_columns" # Remove Column + INSERT_HOLD_COLUMN = "insert_hold_column" # Insert Hold Column + INSERT_MULTIPLE_HOLD_COLUMNS = "insert_multiple_hold_columns" # Insert Multiple Hold Columns + REMOVE_HOLD_COLUMN = "remove_hold_column" # Remove Hold Column + REMOVE_MULTIPLE_HOLD_COLUMNS = "remove_multiple_hold_columns" # Remove Multiple Hold Columns + MIRROR_COLUMNS = "mirror_columns" # Mirror Columns + CLEAR_ANIMATION_CACHE = "clear_animation_cache" # Clear Cache + COPY_COLUMNS_TO_CLIPBOARD = "copy_columns_to_clipboard" # Copy Columns + CUT_COLUMNS_TO_CLIPBOARD = "cut_columns_to_clipboard" # Cut Columns + PASTE_COLUMNS_FROM_CLIPBOARD = "paste_columns_from_clipboard" # Paste Columns + ADD_BLANK_FRAME = "add_blank_frame" # Create Blank Frame + ADD_DUPLICATE_FRAME = "add_duplicate_frame" # Create Duplicate Frame + INSERT_KEYFRAME_LEFT = "insert_keyframe_left" # Insert Keyframe Left + INSERT_KEYFRAME_RIGHT = "insert_keyframe_right" # Insert Keyframe Right + INSERT_MULTIPLE_KEYFRAMES = "insert_multiple_keyframes" # Insert Multiple Keyframes + REMOVE_FRAMES_AND_PULL = "remove_frames_and_pull" # Remove Frame and Pull + REMOVE_FRAMES = "remove_frames" # Remove Keyframe + INSERT_HOLD_FRAME = "insert_hold_frame" # Insert Hold Frame + INSERT_MULTIPLE_HOLD_FRAMES = "insert_multiple_hold_frames" # Insert Multiple Hold Frames + REMOVE_HOLD_FRAME = "remove_hold_frame" # Remove Hold Frame + REMOVE_MULTIPLE_HOLD_FRAMES = "remove_multiple_hold_frames" # Remove Multiple Hold Frames + MIRROR_FRAMES = "mirror_frames" # Mirror Frames + COPY_FRAMES = "copy_frames" # Copy Keyframes + COPY_FRAMES_AS_CLONES = "copy_frames_as_clones" # Clone Keyframes + MAKE_CLONES_UNIQUE = "make_clones_unique" # Make Unique + CUT_FRAMES = "cut_frames" # Cut Keyframes + PASTE_FRAMES = "paste_frames" # Paste Keyframes + SET_START_TIME = "set_start_time" # Set Start Time + SET_END_TIME = "set_end_time" # Set End Time + UPDATE_PLAYBACK_RANGE = "update_playback_range" # Update Playback Range + TOGGLE_PLAYBACK = "toggle_playback" # Play / pause animation + STOP_PLAYBACK = "stop_playback" # Stop animation + PREVIOUS_FRAME = "previous_frame" # Previous Frame + NEXT_FRAME = "next_frame" # Next Frame + PREVIOUS_KEYFRAME = "previous_keyframe" # Previous Keyframe + NEXT_KEYFRAME = "next_keyframe" # Next Keyframe + PREVIOUS_MATCHING_KEYFRAME = "previous_matching_keyframe" # Previous Matching Keyframe + NEXT_MATCHING_KEYFRAME = "next_matching_keyframe" # Next Matching Keyframe + PREVIOUS_UNFILTERED_KEYFRAME = "previous_unfiltered_keyframe" # Previous Unfiltered Keyframe + NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" # Next Unfiltered Keyframe + AUTO_KEY = "auto_key" # Auto Frame Mode + FILE_NEW = "file_new" # New... + FILE_OPEN = "file_open" # Open... + FILE_QUIT = "file_quit" # Quit + OPTIONS_CONFIGURE_TOOLBARS = "options_configure_toolbars" # Configure Toolbars... + FULLSCREEN = "fullscreen" # Full Screen Mode + FILE_OPEN_RECENT = "file_open_recent" # Open Recent + FILE_SAVE = "file_save" # Save + FILE_SAVE_AS = "file_save_as" # Save As... + EDIT_UNDO = "edit_undo" # Undo + EDIT_REDO = "edit_redo" # Redo + FILE_IMPORT_ANIMATION = "file_import_animation" # Import animation frames... + FILE_IMPORT_VIDEO_ANIMATION = "file_import_video_animation" # Import video animation... + RENDER_ANIMATION = "render_animation" # Render Animation... + RENDER_ANIMATION_AGAIN = "render_animation_again" # Render Animation Again + FILE_CLOSE_ALL = "file_close_all" # Close All + FILE_IMPORT_FILE = "file_import_file" # Open existing Document as Untitled Document... + FILE_EXPORT_FILE = "file_export_file" # Export... + FILE_EXPORT_ADVANCED = "file_export_advanced" # Export Advanced... + FILE_DOCUMENTINFO = "file_documentinfo" # Document Information + THEME_MENU = "theme_menu" # Themes + VIEW_TOGGLEDOCKERS = "view_toggledockers" # Show Dockers + RESET_CONFIGURATIONS = "reset_configurations" # Reset All Settings + VIEW_DETACHED_CANVAS = "view_detached_canvas" # Detach canvas + VIEW_TOGGLEDOCKERTITLEBARS = "view_toggledockertitlebars" # Show Docker Titlebars + SETTINGS_DOCKERS_MENU = "settings_dockers_menu" # Dockers + WINDOW = "window" # Window + STYLE_MENU = "style_menu" # Styles + WINDOWS_CASCADE = "windows_cascade" # Cascade + WINDOWS_TILE = "windows_tile" # Tile + WINDOWS_NEXT = "windows_next" # Next + WINDOWS_PREVIOUS = "windows_previous" # Previous + VIEW_NEWWINDOW = "view_newwindow" # New Window + FILE_CLOSE = "file_close" # Close + FILE_SESSIONS = "file_sessions" # Sessions... + COMMAND_BAR_OPEN = "command_bar_open" # Search Actions + OPTIONS_CONFIGURE = "options_configure" # Configure Krita... + EXPANDING_SPACER_0 = "expanding_spacer_0" # Expanding Spacer + EXPANDING_SPACER_1 = "expanding_spacer_1" # Expanding Spacer + HELP_CONTENTS = "help_contents" # Krita Handbook + HELP_REPORT_BUG = "help_report_bug" # Report Bug... + SWITCH_APPLICATION_LANGUAGE = "switch_application_language" # Switch Application Language... + HELP_ABOUT_APP = "help_about_app" # About Krita + HELP_ABOUT_KDE = "help_about_kde" # About KDE + RECORDER_RECORD_TOGGLE = "recorder_record_toggle" # Record Timelapse + RECORDER_EXPORT = "recorder_export" # Export Timelapse... + MAIN_TOOL_BAR = "mainToolBar" # Hide File Toolbar + BRUSHES_AND_STUFF = "BrushesAndStuff" # Hide Brushes and Stuff Toolbar + + # Built-in Scripts (The ones that make sesne to include) + COLOR_SPACE = "color_space" # Colour Space + DOCUMENT_TOOLS = "document_tools" # Document Tools + EXPORT_LAYERS = "export_layers" # Export Layers + FILTER_MANAGER = "filter_manager" # Filter Manager + PLUGIN_IMPORTER_FILE = "plugin_importer_file" # Import Python Plugin from File... + PLUGIN_IMPORTER_WEB = "plugin_importer_web" # Import Python Plugin from Web... + PYTHON_SCRIPTER = "python_scripter" # Scripter + TEN_SCRIPTS = "ten_scripts" # Ten Scripts + EXECUTE_SCRIPT_1 = "execute_script_1" # Execute Script 1 + EXECUTE_SCRIPT_2 = "execute_script_2" # Execute Script 2 + EXECUTE_SCRIPT_3 = "execute_script_3" # Execute Script 3 + EXECUTE_SCRIPT_4 = "execute_script_4" # Execute Script 4 + EXECUTE_SCRIPT_5 = "execute_script_5" # Execute Script 5 + EXECUTE_SCRIPT_6 = "execute_script_6" # Execute Script 6 + EXECUTE_SCRIPT_7 = "execute_script_7" # Execute Script 7 + EXECUTE_SCRIPT_8 = "execute_script_8" # Execute Script 8 + EXECUTE_SCRIPT_9 = "execute_script_9" # Execute Script 9 + EXECUTE_SCRIPT_10 = "execute_script_10" # Execute Script 10 + + # ShortcutComposer actions (The ones that make sense to use this way) + RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" # Reload Shortcut Composer + CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" # Configure Shortcut Composer + + def activate(self): + Api.instance().action(self.value).trigger() + + @property + def icon(self) -> QIcon: + """Return the icon of this tool.""" + icon_name = _ICON_NAME_MAP.get(self, "edit-delete") + return Api.instance().icon(icon_name) + + +_ICON_NAME_MAP = { +} +"""Maps actions to names of their icons.""" From 06a01889a4e32dbff376e3b7463d0387cf1c26bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Go=C5=82=C4=85b=20=28SirPigeonz=29?= Date: Wed, 28 Jun 2023 18:10:22 +0200 Subject: [PATCH 47/69] WIP Updated action.py with new auto-gen enum names Generated new names for feedback, cleaned it up a bit by hand but needs more work. First I want to get green light before I will start this long and boring mission :D --- shortcut_composer/api_krita/enums/action.py | 1092 +++++++++---------- 1 file changed, 530 insertions(+), 562 deletions(-) diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py index 30095cb2..1a03b188 100644 --- a/shortcut_composer/api_krita/enums/action.py +++ b/shortcut_composer/api_krita/enums/action.py @@ -4,578 +4,546 @@ from krita import Krita as Api from enum import Enum -from PyQt5.QtGui import QIcon - class Action(Enum): """ Contains all known actions of Krita. Example usage: `Tool.FREEHAND_BRUSH` - - Available actions: - - `TBA` """ - # Filters - FILTER_APPLY_AGAIN = "filter_apply_again" # Apply Filter Again - FILTER_APPLY_REPROMPT = "filter_apply_reprompt" # Apply Filter Again (Reprompt) - ADJUST_FILTERS = "adjust_filters" # Adjust - KRITA_FILTER_ASC_CDL = "krita_filter_asc-cdl" # Slope, Offset, Power... - KRITA_FILTER_AUTOCONTRAST = "krita_filter_autocontrast" # Auto Contrast - BLUR_FILTERS = "blur_filters" # Blur - KRITA_FILTER_BLUR = "krita_filter_blur" # Blur... - KRITA_FILTER_BURN = "krita_filter_burn" # Burn... - KRITA_FILTER_COLORBALANCE = "krita_filter_colorbalance" # Colour Balance... - COLOR_FILTERS = "color_filters" # Colours - KRITA_FILTER_COLORTOALPHA = "krita_filter_colortoalpha" # Colour to Alpha... - KRITA_FILTER_COLORTRANSFER = "krita_filter_colortransfer" # Colour Transfer... - KRITA_FILTER_CROSSCHANNEL = "krita_filter_crosschannel" # Cross-channel adjustment curves... - KRITA_FILTER_DESATURATE = "krita_filter_desaturate" # Desaturate... - KRITA_FILTER_DODGE = "krita_filter_dodge" # Dodge... - EDGE_FILTERS = "edge_filters" # Edge Detection - KRITA_FILTER_EDGE_DETECTION = "krita_filter_edge detection" # Edge Detection... - EMBOSS_FILTERS = "emboss_filters" # Emboss - KRITA_FILTER_EMBOSS = "krita_filter_emboss" # Emboss with Variable Depth... - KRITA_FILTER_EMBOSS_ALL_DIRECTIONS = "krita_filter_emboss all directions" # Emboss in All Directions - KRITA_FILTER_EMBOSS_HORIZONTAL_AND_VERTICAL = "krita_filter_emboss horizontal and vertical" # Emboss Horizontal Vertical - KRITA_FILTER_EMBOSS_HORIZONTAL_ONLY = "krita_filter_emboss horizontal only" # Emboss Horizontal Only - KRITA_FILTER_EMBOSS_LAPLASCIAN = "krita_filter_emboss laplascian" # Emboss (Laplacian) - KRITA_FILTER_EMBOSS_VERTICAL_ONLY = "krita_filter_emboss vertical only" # Emboss Vertical Only - KRITA_FILTER_GAUSSIAN_BLUR = "krita_filter_gaussian blur" # Gaussian Blur... - KRITA_FILTER_GAUSSIANHIGHPASS = "krita_filter_gaussianhighpass" # Gaussian High Pass... - ENHANCE_FILTERS = "enhance_filters" # Enhance - KRITA_FILTER_GAUSSIANNOISEREDUCER = "krita_filter_gaussiannoisereducer" # Gaussian Noise Reduction... - MAP_FILTERS = "map_filters" # Map - KRITA_FILTER_GRADIENTMAP = "krita_filter_gradientmap" # Gradient Map... - ARTISTIC_FILTERS = "artistic_filters" # Artistic - KRITA_FILTER_HALFTONE = "krita_filter_halftone" # Halftone... - KRITA_FILTER_HEIGHT_TO_NORMAL = "krita_filter_height to normal" # Height to Normal Map... - KRITA_FILTER_HSVADJUSTMENT = "krita_filter_hsvadjustment" # HSV Adjustment... - KRITA_FILTER_INDEXCOLORS = "krita_filter_indexcolors" # Index Colours... - KRITA_FILTER_INVERT = "krita_filter_invert" # Invert - KRITA_FILTER_LENS_BLUR = "krita_filter_lens blur" # Lens Blur... - KRITA_FILTER_LEVELS = "krita_filter_levels" # Levels... - KRITA_FILTER_MAXIMIZE = "krita_filter_maximize" # Maximise Channel - KRITA_FILTER_MEAN_REMOVAL = "krita_filter_mean removal" # Mean Removal - KRITA_FILTER_MINIMIZE = "krita_filter_minimize" # Minimise Channel - KRITA_FILTER_MOTION_BLUR = "krita_filter_motion blur" # Motion Blur... - OTHER_FILTERS = "other_filters" # Other - KRITA_FILTER_NOISE = "krita_filter_noise" # Random Noise... - KRITA_FILTER_NORMALIZE = "krita_filter_normalize" # Normalise - KRITA_FILTER_OILPAINT = "krita_filter_oilpaint" # Oilpaint... - KRITA_FILTER_PALETTIZE = "krita_filter_palettize" # Palettise... - KRITA_FILTER_PERCHANNEL = "krita_filter_perchannel" # Colour Adjustment curves... - KRITA_FILTER_PHONGBUMPMAP = "krita_filter_phongbumpmap" # Phong Bumpmap... - KRITA_FILTER_PIXELIZE = "krita_filter_pixelize" # Pixelise... - KRITA_FILTER_POSTERIZE = "krita_filter_posterize" # Posterise... - KRITA_FILTER_RAINDROPS = "krita_filter_raindrops" # Raindrops... - KRITA_FILTER_RANDOMPICK = "krita_filter_randompick" # Random Pick... - KRITA_FILTER_ROUNDCORNERS = "krita_filter_roundcorners" # Round Corners... - KRITA_FILTER_SHARPEN = "krita_filter_sharpen" # Sharpen - KRITA_FILTER_SMALLTILES = "krita_filter_smalltiles" # Small Tiles... - KRITA_FILTER_THRESHOLD = "krita_filter_threshold" # Threshold... - KRITA_FILTER_UNSHARP = "krita_filter_unsharp" # Unsharp Mask... - KRITA_FILTER_WAVE = "krita_filter_wave" # Wave... - KRITA_FILTER_WAVELETNOISEREDUCER = "krita_filter_waveletnoisereducer" # Wavelet Noise Reducer... - Q_MIC = "QMic" # Start G'MIC-Qt - Q_MIC_AGAIN = "QMicAgain" # Re-apply the last G'MIC filter - - # Untagged - SAVE_INCREMENTAL_VERSION = "save_incremental_version" # Save Incremental Version - SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" # Save Incremental Backup - TABLET_DEBUGGER = "tablet_debugger" # Toggle Tablet Debugger - CREATE_TEMPLATE = "create_template" # Create Template From Image... - CREATE_COPY = "create_copy" # Create Copy From Current Image - OPEN_RESOURCES_DIRECTORY = "open_resources_directory" # Open Resources Folder - ROTATE_CANVAS_RIGHT = "rotate_canvas_right" # Rotate Canvas Right - ROTATE_CANVAS_LEFT = "rotate_canvas_left" # Rotate Canvas Left - RESET_CANVAS_ROTATION = "reset_canvas_rotation" # Reset Canvas Rotation - WRAP_AROUND_MODE = "wrap_around_mode" # Wrap Around Mode - LEVEL_OF_DETAIL_MODE = "level_of_detail_mode" # Instant Preview Mode - SOFT_PROOF = "softProof" # Soft Proofing - GAMUT_CHECK = "gamutCheck" # Out of Gamut Warnings - SHOW_STATUS_BAR = "showStatusBar" # Show Status Bar - VIEW_SHOW_CANVAS_ONLY = "view_show_canvas_only" # Show Canvas Only - RULER_PIXEL_MULTIPLE2 = "ruler_pixel_multiple2" # Use multiple of 2 for pixel scale - VIEW_RULER = "view_ruler" # Show Rulers - RULERS_TRACK_MOUSE = "rulers_track_mouse" # Rulers Track Pointer - ZOOM_TO_100PCT = "zoom_to_100pct" # Reset zoom - VIEW_ZOOM_IN = "view_zoom_in" # Zoom In - VIEW_ZOOM_OUT = "view_zoom_out" # Zoom Out - TOGGLE_ZOOM_TO_FIT = "toggle_zoom_to_fit" # Toggle Zoom Fit to Page - SETTINGS_ACTIVE_AUTHOR = "settings_active_author" # Active Author Profile - VIEW_PIXEL_GRID = "view_pixel_grid" # Show Pixel Grid - TOGGLE_FG_BG = "toggle_fg_bg" # Swap Foreground and Background Colours - RESET_FG_BG = "reset_fg_bg" # Set Foreground and Background Colours to Black and White - TOGGLE_BRUSH_OUTLINE = "toggle_brush_outline" # Toggle Brush Outline - EDIT_CUT = "edit_cut" # Cut - EDIT_COPY = "edit_copy" # Copy - EDIT_PASTE = "edit_paste" # Paste - COPY_SHARP = "copy_sharp" # Copy (sharp) - CUT_SHARP = "cut_sharp" # Cut (sharp) - PASTE_NEW = "paste_new" # Paste into New Image - PASTE_AT = "paste_at" # Paste at Cursor - PASTE_INTO = "paste_into" # Paste into Active Layer - PASTE_AS_REFERENCE = "paste_as_reference" # Paste as Reference Image - PASTE_SHAPE_STYLE = "paste_shape_style" # Paste Shape Style - COPY_MERGED = "copy_merged" # Copy merged - SELECT_ALL = "select_all" # Select All - DESELECT = "deselect" # Deselect - CLEAR = "clear" # Clear - RESELECT = "reselect" # Reselect - INVERT_SELECTION = "invert_selection" # Invert Selection - COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" # Copy Selection to New Layer - CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" # Cut Selection to New Layer - FILL_SELECTION_FOREGROUND_COLOR = "fill_selection_foreground_color" # Fill with Foreground Colour - FILL_SELECTION_BACKGROUND_COLOR = "fill_selection_background_color" # Fill with Background Colour - FILL_SELECTION_PATTERN = "fill_selection_pattern" # Fill with Pattern - FILL_SELECTION_FOREGROUND_COLOR_OPACITY = "fill_selection_foreground_color_opacity" # Fill with Foreground Colour (Opacity) - FILL_SELECTION_BACKGROUND_COLOR_OPACITY = "fill_selection_background_color_opacity" # Fill with Background Colour (Opacity) - FILL_SELECTION_PATTERN_OPACITY = "fill_selection_pattern_opacity" # Fill with Pattern (Opacity) - STROKE_SHAPES = "stroke_shapes" # Stroke selected shapes - TOGGLE_DISPLAY_SELECTION = "toggle_display_selection" # Display Selection - RESIZEIMAGETOSELECTION = "resizeimagetoselection" # Trim to Selection - EDIT_SELECTION = "edit_selection" # Edit Selection - CONVERT_TO_VECTOR_SELECTION = "convert_to_vector_selection" # Convert to Vector Selection - CONVERT_TO_RASTER_SELECTION = "convert_to_raster_selection" # Convert to Raster Selection - CONVERT_SHAPES_TO_VECTOR_SELECTION = "convert_shapes_to_vector_selection" # Convert Shapes to Vector Selection - CONVERT_SELECTION_TO_SHAPE = "convert_selection_to_shape" # Convert to Shape - TOGGLE_SELECTION_OVERLAY_MODE = "toggle-selection-overlay-mode" # Toggle Selection Display Mode - STROKE_SELECTION = "stroke_selection" # Stroke Selection... - VIEW_SHOW_GUIDES = "view_show_guides" # Show Guides - VIEW_LOCK_GUIDES = "view_lock_guides" # Lock Guides - VIEW_SNAP_TO_GUIDES = "view_snap_to_guides" # Snap to Guides - SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" # Show Snap Options Popup - VIEW_SNAP_ORTHOGONAL = "view_snap_orthogonal" # Snap Orthogonal - VIEW_SNAP_NODE = "view_snap_node" # Snap Node - VIEW_SNAP_EXTENSION = "view_snap_extension" # Snap Extension - VIEW_SNAP_INTERSECTION = "view_snap_intersection" # Snap Intersection - VIEW_SNAP_BOUNDING_BOX = "view_snap_bounding_box" # Snap Bounding Box - VIEW_SNAP_IMAGE_BOUNDS = "view_snap_image_bounds" # Snap Image Bounds - VIEW_SNAP_IMAGE_CENTER = "view_snap_image_center" # Snap Image Centre - VIEW_SNAP_TO_PIXEL = "view_snap_to_pixel" # Snap Pixel - FLATTEN_IMAGE = "flatten_image" # Flatten image - MERGE_LAYER = "merge_layer" # Merge with Layer Below - FLATTEN_LAYER = "flatten_layer" # Flatten Layer - SAVE_GROUPS_AS_IMAGES = "save_groups_as_images" # Save Group Layers... - CONVERT_GROUP_TO_ANIMATED = "convert_group_to_animated" # Convert group to animated layer - RESIZEIMAGETOLAYER = "resizeimagetolayer" # Trim to Current Layer - TRIM_TO_IMAGE = "trim_to_image" # Trim to Image Size - LAYER_STYLE = "layer_style" # Layer Style... - COPY_LAYER_STYLE = "copy_layer_style" # Copy Layer Style - PASTE_LAYER_STYLE = "paste_layer_style" # Paste Layer Style - MIRROR_NODE_X = "mirrorNodeX" # Mirror Layer Horizontally - MIRROR_NODE_Y = "mirrorNodeY" # Mirror Layer Vertically - MIRROR_ALL_NODES_X = "mirrorAllNodesX" # Mirror All Layers Horizontally - MIRROR_ALL_NODES_Y = "mirrorAllNodesY" # Mirror All Layers Vertically - ACTIVATE_NEXT_LAYER = "activateNextLayer" # Activate next layer - ACTIVATE_NEXT_SIBLING_LAYER = "activateNextSiblingLayer" # Activate next sibling layer, skipping over groups. - ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" # Activate previous layer - ACTIVATE_PREVIOUS_SIBLING_LAYER = "activatePreviousSiblingLayer" # Activate previous sibling layer, skipping over groups. - SWITCH_TO_PREVIOUSLY_ACTIVE_NODE = "switchToPreviouslyActiveNode" # Activate previously selected layer - SAVE_NODE_AS_IMAGE = "save_node_as_image" # Save Layer/Mask... - SAVE_VECTOR_NODE_TO_SVG = "save_vector_node_to_svg" # Save Vector Layer as SVG... - DUPLICATELAYER = "duplicatelayer" # Duplicate Layer or Mask - COPY_LAYER_CLIPBOARD = "copy_layer_clipboard" # Copy Layer - CUT_LAYER_CLIPBOARD = "cut_layer_clipboard" # Cut Layer - PASTE_LAYER_FROM_CLIPBOARD = "paste_layer_from_clipboard" # Paste Layer - CREATE_QUICK_GROUP = "create_quick_group" # Quick Group - CREATE_QUICK_CLIPPING_GROUP = "create_quick_clipping_group" # Quick Clipping Group - QUICK_UNGROUP = "quick_ungroup" # Quick Ungroup - SELECT_ALL_LAYERS = "select_all_layers" # All Layers - SELECT_VISIBLE_LAYERS = "select_visible_layers" # Visible Layers - SELECT_LOCKED_LAYERS = "select_locked_layers" # Locked Layers - SELECT_INVISIBLE_LAYERS = "select_invisible_layers" # Invisible Layers - SELECT_UNLOCKED_LAYERS = "select_unlocked_layers" # Unlocked Layers - NEW_FROM_VISIBLE = "new_from_visible" # New Layer From Visible - PIN_TO_TIMELINE = "pin_to_timeline" # Pin to Timeline - ADD_NEW_PAINT_LAYER = "add_new_paint_layer" # Add Paint Layer - ADD_NEW_GROUP_LAYER = "add_new_group_layer" # Add Group Layer - ADD_NEW_CLONE_LAYER = "add_new_clone_layer" # Add Clone Layer - ADD_NEW_SHAPE_LAYER = "add_new_shape_layer" # Add Vector Layer - ADD_NEW_ADJUSTMENT_LAYER = "add_new_adjustment_layer" # Add Filter Layer... - ADD_NEW_FILL_LAYER = "add_new_fill_layer" # Add Fill Layer... - ADD_NEW_FILE_LAYER = "add_new_file_layer" # Add File Layer... - ADD_NEW_TRANSPARENCY_MASK = "add_new_transparency_mask" # Add Transparency Mask - ADD_NEW_FILTER_MASK = "add_new_filter_mask" # Add Filter Mask... - ADD_NEW_COLORIZE_MASK = "add_new_colorize_mask" # Add Colourise Mask - ADD_NEW_TRANSFORM_MASK = "add_new_transform_mask" # Add Transform Mask - ADD_NEW_SELECTION_MASK = "add_new_selection_mask" # Add Local Selection - CONVERT_TO_PAINT_LAYER = "convert_to_paint_layer" # Convert to Paint Layer - CONVERT_TO_SELECTION_MASK = "convert_to_selection_mask" # Convert to Selection Mask - CONVERT_TO_FILTER_MASK = "convert_to_filter_mask" # Convert to Filter Mask... - CONVERT_TO_TRANSPARENCY_MASK = "convert_to_transparency_mask" # Convert to Transparency Mask - CONVERT_TO_ANIMATED = "convert_to_animated" # Convert to animated layer - CONVERT_TO_FILE_LAYER = "convert_to_file_layer" # to File Layer... - ISOLATE_ACTIVE_LAYER = "isolate_active_layer" # Isolate Active Layer - ISOLATE_ACTIVE_GROUP = "isolate_active_group" # Isolate Active Group - TOGGLE_LAYER_VISIBILITY = "toggle_layer_visibility" # Toggle layer visibility - TOGGLE_LAYER_LOCK = "toggle_layer_lock" # Toggle layer lock - TOGGLE_LAYER_INHERIT_ALPHA = "toggle_layer_inherit_alpha" # Toggle layer alpha inheritance - TOGGLE_LAYER_ALPHA_LOCK = "toggle_layer_alpha_lock" # Toggle layer alpha - SPLIT_ALPHA_INTO_MASK = "split_alpha_into_mask" # Alpha into Mask - SPLIT_ALPHA_WRITE = "split_alpha_write" # Write as Alpha - SPLIT_ALPHA_SAVE_MERGED = "split_alpha_save_merged" # Save Merged... - IMPORT_LAYER_FROM_FILE = "import_layer_from_file" # Import Layer... - IMAGE_PROPERTIES = "image_properties" # Properties... - IMPORT_LAYER_AS_PAINT_LAYER = "import_layer_as_paint_layer" # as Paint Layer... - IMPORT_LAYER_AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" # as Transparency Mask... - IMPORT_LAYER_AS_FILTER_MASK = "import_layer_as_filter_mask" # as Filter Mask... - IMPORT_LAYER_AS_SELECTION_MASK = "import_layer_as_selection_mask" # as Selection Mask... - IMAGE_COLOR = "image_color" # Image Background Colour and Transparency... - VIEW_GRID = "view_grid" # Show Grid - VIEW_SNAP_TO_GRID = "view_snap_to_grid" # Snap To Grid - VIEW_TOGGLE_PAINTING_ASSISTANTS = "view_toggle_painting_assistants" # Show Painting Assistants - VIEW_TOGGLE_ASSISTANT_PREVIEWS = "view_toggle_assistant_previews" # Show Assistant Previews - VIEW_TOGGLE_REFERENCE_IMAGES = "view_toggle_reference_images" # Show Reference Images - MAKE_BRUSH_COLOR_LIGHTER = "make_brush_color_lighter" # Make brush colour lighter - MAKE_BRUSH_COLOR_DARKER = "make_brush_color_darker" # Make brush colour darker - MAKE_BRUSH_COLOR_SATURATED = "make_brush_color_saturated" # Make brush colour more saturated - MAKE_BRUSH_COLOR_DESATURATED = "make_brush_color_desaturated" # Make brush colour more desaturated - SHIFT_BRUSH_COLOR_CLOCKWISE = "shift_brush_color_clockwise" # Shift brush colour hue clockwise - SHIFT_BRUSH_COLOR_COUNTER_CLOCKWISE = "shift_brush_color_counter_clockwise" # Shift brush colour hue counter-clockwise - MAKE_BRUSH_COLOR_REDDER = "make_brush_color_redder" # Make brush colour more red - MAKE_BRUSH_COLOR_GREENER = "make_brush_color_greener" # Make brush colour more green - MAKE_BRUSH_COLOR_BLUER = "make_brush_color_bluer" # Make brush colour more blue - MAKE_BRUSH_COLOR_YELLOWER = "make_brush_color_yellower" # Make brush colour more yellow - INCREASE_OPACITY = "increase_opacity" # Increase Opacity - DECREASE_OPACITY = "decrease_opacity" # Decrease Opacity - INCREASE_FLOW = "increase_flow" # Increase Flow - DECREASE_FLOW = "decrease_flow" # Decrease Flow - INCREASE_FADE = "increase_fade" # Increase Fade - DECREASE_FADE = "decrease_fade" # Decrease Fade - INCREASE_SCATTER = "increase_scatter" # Increase Scatter - DECREASE_SCATTER = "decrease_scatter" # Decrease Scatter - MIRROR_CANVAS = "mirror_canvas" # Mirror View - MIRROR_CANVAS_AROUND_CURSOR = "mirror_canvas_around_cursor" # Mirror View Around Cursor - PATTERNS = "patterns" # Patterns - GRADIENTS = "gradients" # Gradients - DUAL = "dual" # Choose foreground and background colours - ERASE_ACTION = "erase_action" # Set eraser mode - RELOAD_PRESET_ACTION = "reload_preset_action" # Reload Original Preset - PRESERVE_ALPHA = "preserve_alpha" # Preserve Alpha - MIRROR_X_HIDE_DECORATIONS = "mirrorX-hideDecorations" # Hide Mirror X Line - MIRROR_X_LOCK = "mirrorX-lock" # Lock X Line - MIRROR_X_MOVE_TO_CENTER = "mirrorX-moveToCenter" # Move to Canvas Centre X - MIRROR_Y_HIDE_DECORATIONS = "mirrorY-hideDecorations" # Hide Mirror Y Line - MIRROR_Y_LOCK = "mirrorY-lock" # Lock Y Line - MIRROR_Y_MOVE_TO_CENTER = "mirrorY-moveToCenter" # Move to Canvas Centre Y - H_MIRROR_ACTION = "hmirror_action" # Horizontal Mirror Tool - V_MIRROR_ACTION = "vmirror_action" # Vertical Mirror Tool - NEXT_BLENDING_MODE = "Next Blending Mode" # Next Blending Mode - PREVIOUS_BLENDING_MODE = "Previous Blending Mode" # Previous Blending Mode - SELECT_NORMAL_BLENDING_MODE = "Select Normal Blending Mode" # Select Normal Blending Mode - SELECT_DISSOLVE_BLENDING_MODE = "Select Dissolve Blending Mode" # Select Dissolve Blending Mode - SELECT_BEHIND_BLENDING_MODE = "Select Behind Blending Mode" # Select Behind Blending Mode - SELECT_CLEAR_BLENDING_MODE = "Select Clear Blending Mode" # Select Clear Blending Mode - SELECT_DARKEN_BLENDING_MODE = "Select Darken Blending Mode" # Select Darken Blending Mode - SELECT_MULTIPLY_BLENDING_MODE = "Select Multiply Blending Mode" # Select Multiply Blending Mode - SELECT_COLOR_BURN_BLENDING_MODE = "Select Color Burn Blending Mode" # Select Colour Burn Blending Mode - SELECT_LINEAR_BURN_BLENDING_MODE = "Select Linear Burn Blending Mode" # Select Linear Burn Blending Mode - SELECT_LIGHTEN_BLENDING_MODE = "Select Lighten Blending Mode" # Select Lighten Blending Mode - SELECT_SCREEN_BLENDING_MODE = "Select Screen Blending Mode" # Select Screen Blending Mode - SELECT_COLOR_DODGE_BLENDING_MODE = "Select Color Dodge Blending Mode" # Select Colour Dodge Blending Mode - SELECT_LINEAR_DODGE_BLENDING_MODE = "Select Linear Dodge Blending Mode" # Select Linear Dodge Blending Mode - SELECT_OVERLAY_BLENDING_MODE = "Select Overlay Blending Mode" # Select Overlay Blending Mode - SELECT_HARD_OVERLAY_BLENDING_MODE = "Select Hard Overlay Blending Mode" # Select Hard Overlay Blending Mode - SELECT_SOFT_LIGHT_BLENDING_MODE = "Select Soft Light Blending Mode" # Select Soft Light Blending Mode - SELECT_HARD_LIGHT_BLENDING_MODE = "Select Hard Light Blending Mode" # Select Hard Light Blending Mode - SELECT_VIVID_LIGHT_BLENDING_MODE = "Select Vivid Light Blending Mode" # Select Vivid Light Blending Mode - SELECT_LINEAR_LIGHT_BLENDING_MODE = "Select Linear Light Blending Mode" # Select Linear Light Blending Mode - SELECT_PIN_LIGHT_BLENDING_MODE = "Select Pin Light Blending Mode" # Select Pin Light Blending Mode - SELECT_HARD_MIX_BLENDING_MODE = "Select Hard Mix Blending Mode" # Select Hard Mix Blending Mode - SELECT_DIFFERENCE_BLENDING_MODE = "Select Difference Blending Mode" # Select Difference Blending Mode - SELECT_EXCLUSION_BLENDING_MODE = "Select Exclusion Blending Mode" # Select Exclusion Blending Mode - SELECT_HUE_BLENDING_MODE = "Select Hue Blending Mode" # Select Hue Blending Mode - SELECT_SATURATION_BLENDING_MODE = "Select Saturation Blending Mode" # Select Saturation Blending Mode - SELECT_COLOR_BLENDING_MODE = "Select Color Blending Mode" # Select Colour Blending Mode - SELECT_LUMINOSITY_BLENDING_MODE = "Select Luminosity Blending Mode" # Select Luminosity Blending Mode - COMPOSITE_ACTIONS = "composite_actions" # Brush composite - BRUSHSLIDER1 = "brushslider1" # Brush option slider 1 - BRUSHSLIDER2 = "brushslider2" # Brush option slider 2 - BRUSHSLIDER3 = "brushslider3" # Brush option slider 3 - BRUSHSLIDER4 = "brushslider4" # Brush option slider 4 - NEXT_FAVORITE_PRESET = "next_favorite_preset" # Next Favourite Preset - PREVIOUS_FAVORITE_PRESET = "previous_favorite_preset" # Previous Favourite Preset - PREVIOUS_PRESET = "previous_preset" # Switch to Previous Preset - SHOW_BRUSH_PRESETS = "show_brush_presets" # Show Brush Presets - MIRROR_ACTIONS = "mirror_actions" # Mirror - WORKSPACES = "workspaces" # Workspaces - SHOW_BRUSH_EDITOR = "show_brush_editor" # Show Brush Editor - DISABLE_PRESSURE = "disable_pressure" # Use Pen Pressure - PAINTOPS = "paintops" # Painter's Tools - CHOOSE_FOREGROUND_COLOR = "chooseForegroundColor" # Open Foreground colour selector - CHOOSE_BACKGROUND_COLOR = "chooseBackgroundColor" # Open Background colour selector - IMAGESIZE = "imagesize" # Scale Image To New Size... - CANVASSIZE = "canvassize" # Resize Canvas... - LAYERSIZE = "layersize" # Scale Layer to new Size... - SCALE_ALL_LAYERS = "scaleAllLayers" # Scale All Layers to new Size... - SELECTIONSCALE = "selectionscale" # Scale... - BUGINFO = "buginfo" # Show Krita log for bug reports. - SYSINFO = "sysinfo" # Show system information for bug reports. - CRASHLOG = "crashlog" # Show crash log for bug reports. - CLONES_ARRAY = "clones_array" # Clones Array... - COLORRANGE = "colorrange" # Select from Colour Range... - SELECTOPAQUE = "selectopaque" # Select Opaque (Replace) - SELECTOPAQUE_ADD = "selectopaque_add" # Select Opaque (Add) - SELECTOPAQUE_SUBTRACT = "selectopaque_subtract" # Select Opaque (Subtract) - SELECTOPAQUE_INTERSECT = "selectopaque_intersect" # Select Opaque (Intersect) - IMAGECOLORSPACECONVERSION = "imagecolorspaceconversion" # Convert Image Colour Space... - LAYERCOLORSPACECONVERSION = "layercolorspaceconversion" # Convert Layer Colour Space... - DBEXPLORER = "dbexplorer" # Explore Resources Cache Database... - IMAGESPLIT = "imagesplit" # Image Split - LAYER_GROUP_SWITCHER_PREVIOUS = "LayerGroupSwitcher/previous" # Move into previous group - LAYER_GROUP_SWITCHER_NEXT = "LayerGroupSwitcher/next" # Move into next group - LAYERSPLIT = "layersplit" # Split Layer... - EDIT_LAYER_META_DATA = "EditLayerMetaData" # Edit metadata... - GROWSELECTION = "growselection" # Grow Selection... - SHRINKSELECTION = "shrinkselection" # Shrink Selection... - BORDERSELECTION = "borderselection" # Border Selection... - FEATHERSELECTION = "featherselection" # Feather Selection... - SMOOTHSELECTION = "smoothselection" # Smooth - OFFSETIMAGE = "offsetimage" # Offset Image... - OFFSETLAYER = "offsetlayer" # Offset Layer... - MANAGE_BUNDLES = "manage_bundles" # Manage Resource Libraries... - MANAGE_RESOURCES = "manage_resources" # Manage Resources... - ROTATEIMAGE = "rotateimage" # Rotate Image... - ROTATE_IMAGE_C_W90 = "rotateImageCW90" # Rotate Image 90° to the Right - ROTATE_IMAGE180 = "rotateImage180" # Rotate Image 180° - ROTATE_IMAGE_C_C_W90 = "rotateImageCCW90" # Rotate Image 90° to the Left - MIRROR_IMAGE_HORIZONTAL = "mirrorImageHorizontal" # Mirror Image Horizontally - MIRROR_IMAGE_VERTICAL = "mirrorImageVertical" # Mirror Image Vertically - ROTATELAYER = "rotatelayer" # Rotate Layer... - ROTATE_LAYER180 = "rotateLayer180" # Rotate Layer 180° - ROTATE_LAYER_C_W90 = "rotateLayerCW90" # Rotate Layer 90° to the Right - ROTATE_LAYER_C_C_W90 = "rotateLayerCCW90" # Rotate Layer 90° to the Left - ROTATE_ALL_LAYERS = "rotateAllLayers" # Rotate All Layers... - ROTATE_ALL_LAYERS_C_W90 = "rotateAllLayersCW90" # Rotate All Layers 90° to the Right - ROTATE_ALL_LAYERS_C_C_W90 = "rotateAllLayersCCW90" # Rotate All Layers 90° to the Left - ROTATE_ALL_LAYERS180 = "rotateAllLayers180" # Rotate All Layers 180° - SEPARATE = "separate" # Separate Image... - SHEARIMAGE = "shearimage" # Shear Image... - SHEARLAYER = "shearlayer" # Shear Layer... - SHEAR_ALL_LAYERS = "shearAllLayers" # Shear All Layers... - WAVELETDECOMPOSE = "waveletdecompose" # Wavelet Decompose ... - INCREASE_BRUSH_SIZE = "increase_brush_size" # Increase Brush Size - DECREASE_BRUSH_SIZE = "decrease_brush_size" # Decrease Brush Size - SELECTION_TOOL_MODE_ADD = "selection_tool_mode_add" # Selection Mode: Add - SELECTION_TOOL_MODE_REPLACE = "selection_tool_mode_replace" # Selection Mode: Replace - SELECTION_TOOL_MODE_SUBTRACT = "selection_tool_mode_subtract" # Selection Mode: Subtract - SELECTION_TOOL_MODE_INTERSECT = "selection_tool_mode_intersect" # Selection Mode: Intersect - UNDO_POLYGON_SELECTION = "undo_polygon_selection" # Undo Polygon Selection Points - OBJECT_ORDER_FRONT = "object_order_front" # Bring to Front - OBJECT_ORDER_RAISE = "object_order_raise" # Raise - OBJECT_ORDER_LOWER = "object_order_lower" # Lower - OBJECT_ORDER_BACK = "object_order_back" # Send to Back - OBJECT_TRANSFORM_ROTATE_90_CW = "object_transform_rotate_90_cw" # Rotate 90° CW - OBJECT_TRANSFORM_ROTATE_90_CCW = "object_transform_rotate_90_ccw" # Rotate 90° ACW - OBJECT_TRANSFORM_ROTATE_180 = "object_transform_rotate_180" # Rotate 180° - OBJECT_TRANSFORM_MIRROR_HORIZONTALLY = "object_transform_mirror_horizontally" # Mirror Horizontally - OBJECT_TRANSFORM_MIRROR_VERTICALLY = "object_transform_mirror_vertically" # Mirror Vertically - OBJECT_TRANSFORM_RESET = "object_transform_reset" # Reset Transformations - SET_NO_BRUSH_SMOOTHING = "set_no_brush_smoothing" # Brush Smoothing: Disabled - SET_SIMPLE_BRUSH_SMOOTHING = "set_simple_brush_smoothing" # Brush Smoothing: Basic - SET_WEIGHTED_BRUSH_SMOOTHING = "set_weighted_brush_smoothing" # Brush Smoothing: Weighted - SET_STABILIZER_BRUSH_SMOOTHING = "set_stabilizer_brush_smoothing" # Brush Smoothing: Stabiliser - TOGGLE_ASSISTANT = "toggle_assistant" # Toggle Snap To Assistants - CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" # Calligraphy: increase width - CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" # Calligraphy: decrease width - CALLIGRAPHY_INCREASE_ANGLE = "calligraphy_increase_angle" # Calligraphy: increase angle - CALLIGRAPHY_DECREASE_ANGLE = "calligraphy_decrease_angle" # Calligraphy: decrease angle - OBJECT_ALIGN_HORIZONTAL_LEFT = "object_align_horizontal_left" # Align Left - OBJECT_ALIGN_HORIZONTAL_CENTER = "object_align_horizontal_center" # Horizontally Centre - OBJECT_ALIGN_HORIZONTAL_RIGHT = "object_align_horizontal_right" # Align Right - OBJECT_ALIGN_VERTICAL_TOP = "object_align_vertical_top" # Align Top - OBJECT_ALIGN_VERTICAL_CENTER = "object_align_vertical_center" # Vertically Centre - OBJECT_ALIGN_VERTICAL_BOTTOM = "object_align_vertical_bottom" # Align Bottom - OBJECT_DISTRIBUTE_HORIZONTAL_LEFT = "object_distribute_horizontal_left" # Distribute Left - OBJECT_DISTRIBUTE_HORIZONTAL_CENTER = "object_distribute_horizontal_center" # Distribute Centres Horizontally - OBJECT_DISTRIBUTE_HORIZONTAL_RIGHT = "object_distribute_horizontal_right" # Distribute Right - OBJECT_DISTRIBUTE_HORIZONTAL_GAPS = "object_distribute_horizontal_gaps" # Distribute Horizontal Gap - OBJECT_DISTRIBUTE_VERTICAL_TOP = "object_distribute_vertical_top" # Distribute Top - OBJECT_DISTRIBUTE_VERTICAL_CENTER = "object_distribute_vertical_center" # Distribute Centres Vertically - OBJECT_DISTRIBUTE_VERTICAL_BOTTOM = "object_distribute_vertical_bottom" # Distribute Bottom - OBJECT_DISTRIBUTE_VERTICAL_GAPS = "object_distribute_vertical_gaps" # Distribute Vertical Gap - OBJECT_GROUP = "object_group" # Group - OBJECT_UNGROUP = "object_ungroup" # Ungroup - OBJECT_UNITE = "object_unite" # Unite - OBJECT_INTERSECT = "object_intersect" # Intersect - OBJECT_SUBTRACT = "object_subtract" # Subtract - OBJECT_SPLIT = "object_split" # Split - PATH_TOOL = "PathTool" # Edit Shapes Tool - PATHPOINT_CORNER = "pathpoint-corner" # Corner point - PATHPOINT_SMOOTH = "pathpoint-smooth" # Smooth point - PATHPOINT_SYMMETRIC = "pathpoint-symmetric" # Symmetric Point - PATHPOINT_CURVE = "pathpoint-curve" # Make curve point - PATHPOINT_LINE = "pathpoint-line" # Make line point - PATHSEGMENT_LINE = "pathsegment-line" # Segment to Line - PATHSEGMENT_CURVE = "pathsegment-curve" # Segment to Curve - PATHPOINT_INSERT = "pathpoint-insert" # Insert point - PATHPOINT_REMOVE = "pathpoint-remove" # Remove point - PATH_BREAK_POINT = "path-break-point" # Break at point - PATH_BREAK_SEGMENT = "path-break-segment" # Break at segment - PATHPOINT_JOIN = "pathpoint-join" # Join with segment - PATHPOINT_MERGE = "pathpoint-merge" # Merge points - CONVERT_TO_PATH = "convert-to-path" # To Path - MOVETOOL_MOVE_UP = "movetool-move-up" # Move up - MOVETOOL_MOVE_DOWN = "movetool-move-down" # Move down - MOVETOOL_MOVE_LEFT = "movetool-move-left" # Move left - MOVETOOL_MOVE_RIGHT = "movetool-move-right" # Move right - MOVETOOL_MOVE_UP_MORE = "movetool-move-up-more" # Move up more - MOVETOOL_MOVE_DOWN_MORE = "movetool-move-down-more" # Move down more - MOVETOOL_MOVE_LEFT_MORE = "movetool-move-left-more" # Move left more - MOVETOOL_MOVE_RIGHT_MORE = "movetool-move-right-more" # Move right more - MOVETOOL_SHOW_COORDINATES = "movetool-show-coordinates" # Show Coordinates - ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" # Add scalar keyframes - REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" # Remove scalar keyframe - INTERPOLATION_CONSTANT = "interpolation_constant" # Hold constant value. No interpolation. - INTERPOLATION_LINEAR = "interpolation_linear" # Linear interpolation. - INTERPOLATION_BEZIER = "interpolation_bezier" # Bezier curve interpolation. - TANGENTS_SHARP = "tangents_sharp" # Sharp interpolation tangents. - TANGENTS_SMOOTH = "tangents_smooth" # Smooth interpolation tangents. - ZOOM_TO_FIT_RANGE = "zoom_to_fit_range" # Zoom view to fit channel range. - ZOOM_TO_FIT_CURVE = "zoom_to_fit_curve" # Zoom view to fit curve. - DROP_FRAMES = "drop_frames" # Drop Frames - SHOW_GLOBAL_SELECTION_MASK = "show-global-selection-mask" # Show Global Selection Mask - RENAME_CURRENT_LAYER = "RenameCurrentLayer" # Rename current layer - LAYER_PROPERTIES = "layer_properties" # Properties... - REMOVE_LAYER = "remove_layer" # Remove Layer - MOVE_LAYER_UP = "move_layer_up" # Move Layer or Mask Up - MOVE_LAYER_DOWN = "move_layer_down" # Move Layer or Mask Down - SET_COPY_FROM = "set-copy-from" # Set Copy From... - TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" # Toggle Layer Soloing - TOGGLE_ONION_SKIN = "toggle_onion_skin" # Toggle onion skin - CREATE_SNAPSHOT = "create_snapshot" # Create Snapshot - SWITCHTO_SNAPSHOT = "switchto_snapshot" # Switch to Selected Snapshot - REMOVE_SNAPSHOT = "remove_snapshot" # Remove Selected Snapshot - INSERT_COLUMN_LEFT = "insert_column_left" # Insert Column Left - INSERT_COLUMN_RIGHT = "insert_column_right" # Insert Column Right - INSERT_MULTIPLE_COLUMNS = "insert_multiple_columns" # Insert Multiple Columns - REMOVE_COLUMNS_AND_PULL = "remove_columns_and_pull" # Remove Column and Pull - REMOVE_COLUMNS = "remove_columns" # Remove Column - INSERT_HOLD_COLUMN = "insert_hold_column" # Insert Hold Column - INSERT_MULTIPLE_HOLD_COLUMNS = "insert_multiple_hold_columns" # Insert Multiple Hold Columns - REMOVE_HOLD_COLUMN = "remove_hold_column" # Remove Hold Column - REMOVE_MULTIPLE_HOLD_COLUMNS = "remove_multiple_hold_columns" # Remove Multiple Hold Columns - MIRROR_COLUMNS = "mirror_columns" # Mirror Columns - CLEAR_ANIMATION_CACHE = "clear_animation_cache" # Clear Cache - COPY_COLUMNS_TO_CLIPBOARD = "copy_columns_to_clipboard" # Copy Columns - CUT_COLUMNS_TO_CLIPBOARD = "cut_columns_to_clipboard" # Cut Columns - PASTE_COLUMNS_FROM_CLIPBOARD = "paste_columns_from_clipboard" # Paste Columns - ADD_BLANK_FRAME = "add_blank_frame" # Create Blank Frame - ADD_DUPLICATE_FRAME = "add_duplicate_frame" # Create Duplicate Frame - INSERT_KEYFRAME_LEFT = "insert_keyframe_left" # Insert Keyframe Left - INSERT_KEYFRAME_RIGHT = "insert_keyframe_right" # Insert Keyframe Right - INSERT_MULTIPLE_KEYFRAMES = "insert_multiple_keyframes" # Insert Multiple Keyframes - REMOVE_FRAMES_AND_PULL = "remove_frames_and_pull" # Remove Frame and Pull - REMOVE_FRAMES = "remove_frames" # Remove Keyframe - INSERT_HOLD_FRAME = "insert_hold_frame" # Insert Hold Frame - INSERT_MULTIPLE_HOLD_FRAMES = "insert_multiple_hold_frames" # Insert Multiple Hold Frames - REMOVE_HOLD_FRAME = "remove_hold_frame" # Remove Hold Frame - REMOVE_MULTIPLE_HOLD_FRAMES = "remove_multiple_hold_frames" # Remove Multiple Hold Frames - MIRROR_FRAMES = "mirror_frames" # Mirror Frames - COPY_FRAMES = "copy_frames" # Copy Keyframes - COPY_FRAMES_AS_CLONES = "copy_frames_as_clones" # Clone Keyframes - MAKE_CLONES_UNIQUE = "make_clones_unique" # Make Unique - CUT_FRAMES = "cut_frames" # Cut Keyframes - PASTE_FRAMES = "paste_frames" # Paste Keyframes - SET_START_TIME = "set_start_time" # Set Start Time - SET_END_TIME = "set_end_time" # Set End Time - UPDATE_PLAYBACK_RANGE = "update_playback_range" # Update Playback Range - TOGGLE_PLAYBACK = "toggle_playback" # Play / pause animation - STOP_PLAYBACK = "stop_playback" # Stop animation - PREVIOUS_FRAME = "previous_frame" # Previous Frame - NEXT_FRAME = "next_frame" # Next Frame - PREVIOUS_KEYFRAME = "previous_keyframe" # Previous Keyframe - NEXT_KEYFRAME = "next_keyframe" # Next Keyframe - PREVIOUS_MATCHING_KEYFRAME = "previous_matching_keyframe" # Previous Matching Keyframe - NEXT_MATCHING_KEYFRAME = "next_matching_keyframe" # Next Matching Keyframe - PREVIOUS_UNFILTERED_KEYFRAME = "previous_unfiltered_keyframe" # Previous Unfiltered Keyframe - NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" # Next Unfiltered Keyframe - AUTO_KEY = "auto_key" # Auto Frame Mode - FILE_NEW = "file_new" # New... - FILE_OPEN = "file_open" # Open... - FILE_QUIT = "file_quit" # Quit - OPTIONS_CONFIGURE_TOOLBARS = "options_configure_toolbars" # Configure Toolbars... - FULLSCREEN = "fullscreen" # Full Screen Mode - FILE_OPEN_RECENT = "file_open_recent" # Open Recent - FILE_SAVE = "file_save" # Save - FILE_SAVE_AS = "file_save_as" # Save As... - EDIT_UNDO = "edit_undo" # Undo - EDIT_REDO = "edit_redo" # Redo - FILE_IMPORT_ANIMATION = "file_import_animation" # Import animation frames... - FILE_IMPORT_VIDEO_ANIMATION = "file_import_video_animation" # Import video animation... - RENDER_ANIMATION = "render_animation" # Render Animation... - RENDER_ANIMATION_AGAIN = "render_animation_again" # Render Animation Again - FILE_CLOSE_ALL = "file_close_all" # Close All - FILE_IMPORT_FILE = "file_import_file" # Open existing Document as Untitled Document... - FILE_EXPORT_FILE = "file_export_file" # Export... - FILE_EXPORT_ADVANCED = "file_export_advanced" # Export Advanced... - FILE_DOCUMENTINFO = "file_documentinfo" # Document Information - THEME_MENU = "theme_menu" # Themes - VIEW_TOGGLEDOCKERS = "view_toggledockers" # Show Dockers - RESET_CONFIGURATIONS = "reset_configurations" # Reset All Settings - VIEW_DETACHED_CANVAS = "view_detached_canvas" # Detach canvas - VIEW_TOGGLEDOCKERTITLEBARS = "view_toggledockertitlebars" # Show Docker Titlebars - SETTINGS_DOCKERS_MENU = "settings_dockers_menu" # Dockers - WINDOW = "window" # Window - STYLE_MENU = "style_menu" # Styles - WINDOWS_CASCADE = "windows_cascade" # Cascade - WINDOWS_TILE = "windows_tile" # Tile - WINDOWS_NEXT = "windows_next" # Next - WINDOWS_PREVIOUS = "windows_previous" # Previous - VIEW_NEWWINDOW = "view_newwindow" # New Window - FILE_CLOSE = "file_close" # Close - FILE_SESSIONS = "file_sessions" # Sessions... - COMMAND_BAR_OPEN = "command_bar_open" # Search Actions - OPTIONS_CONFIGURE = "options_configure" # Configure Krita... - EXPANDING_SPACER_0 = "expanding_spacer_0" # Expanding Spacer - EXPANDING_SPACER_1 = "expanding_spacer_1" # Expanding Spacer - HELP_CONTENTS = "help_contents" # Krita Handbook - HELP_REPORT_BUG = "help_report_bug" # Report Bug... - SWITCH_APPLICATION_LANGUAGE = "switch_application_language" # Switch Application Language... - HELP_ABOUT_APP = "help_about_app" # About Krita - HELP_ABOUT_KDE = "help_about_kde" # About KDE - RECORDER_RECORD_TOGGLE = "recorder_record_toggle" # Record Timelapse - RECORDER_EXPORT = "recorder_export" # Export Timelapse... - MAIN_TOOL_BAR = "mainToolBar" # Hide File Toolbar - BRUSHES_AND_STUFF = "BrushesAndStuff" # Hide Brushes and Stuff Toolbar - - # Built-in Scripts (The ones that make sesne to include) - COLOR_SPACE = "color_space" # Colour Space - DOCUMENT_TOOLS = "document_tools" # Document Tools - EXPORT_LAYERS = "export_layers" # Export Layers - FILTER_MANAGER = "filter_manager" # Filter Manager - PLUGIN_IMPORTER_FILE = "plugin_importer_file" # Import Python Plugin from File... - PLUGIN_IMPORTER_WEB = "plugin_importer_web" # Import Python Plugin from Web... - PYTHON_SCRIPTER = "python_scripter" # Scripter - TEN_SCRIPTS = "ten_scripts" # Ten Scripts - EXECUTE_SCRIPT_1 = "execute_script_1" # Execute Script 1 - EXECUTE_SCRIPT_2 = "execute_script_2" # Execute Script 2 - EXECUTE_SCRIPT_3 = "execute_script_3" # Execute Script 3 - EXECUTE_SCRIPT_4 = "execute_script_4" # Execute Script 4 - EXECUTE_SCRIPT_5 = "execute_script_5" # Execute Script 5 - EXECUTE_SCRIPT_6 = "execute_script_6" # Execute Script 6 - EXECUTE_SCRIPT_7 = "execute_script_7" # Execute Script 7 - EXECUTE_SCRIPT_8 = "execute_script_8" # Execute Script 8 - EXECUTE_SCRIPT_9 = "execute_script_9" # Execute Script 9 - EXECUTE_SCRIPT_10 = "execute_script_10" # Execute Script 10 + SAVE_INCREMENTAL_VERSION = "save_incremental_version" + SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" + TOGGLE_TABLET_DEBUGGER = "tablet_debugger" + CREATE_TEMPLATE_FROM_IMAGE = "create_template" + CREATE_COPY_FROM_CURRENT_IMAGE = "create_copy" + OPEN_RESOURCES_FOLDER = "open_resources_directory" + ROTATE_CANVAS_RIGHT = "rotate_canvas_right" + ROTATE_CANVAS_LEFT = "rotate_canvas_left" + RESET_CANVAS_ROTATION = "reset_canvas_rotation" + WRAP_AROUND_MODE = "wrap_around_mode" + INSTANT_PREVIEW_MODE = "level_of_detail_mode" + OUT_OF_GAMUT_WARNINGS = "gamutCheck" + SHOW_STATUS_BAR = "showStatusBar" + SHOW_CANVAS_ONLY = "view_show_canvas_only" + USE_MULTIPLE_OF_2_FOR_PIXEL_SCALE = "ruler_pixel_multiple2" + RULERS_TRACK_POINTER = "rulers_track_mouse" + RESET_ZOOM = "zoom_to_100pct" + ZOOM_IN = "view_zoom_in" + ZOOM_OUT = "view_zoom_out" + TOGGLE_ZOOM_FIT_TO_PAGE = "toggle_zoom_to_fit" + ACTIVE_AUTHOR_PROFILE = "settings_active_author" + SHOW_PIXEL_GRID = "view_pixel_grid" + SWAP_FOREGROUND_AND_BACKGROUND_COLOURS = "toggle_fg_bg" + SET_FOREGROUND_AND_BACKGROUND_COLOURS_TO_BLACK_AND_WHITE = "reset_fg_bg" + TOGGLE_BRUSH_OUTLINE = "toggle_brush_outline" + APPLY_FILTER_AGAIN = "filter_apply_again" + APPLY_FILTER_AGAIN_REPROMPT = "filter_apply_reprompt" + SLOPE, _OFFSET, _POWER = "krita_filter_asc-cdl" + AUTO_CONTRAST = "krita_filter_autocontrast" + BLUR = "krita_filter_blur" + BURN = "krita_filter_burn" + COLOUR_BALANCE = "krita_filter_colorbalance" + COLOUR_TO_ALPHA = "krita_filter_colortoalpha" + COLOUR_TRANSFER = "krita_filter_colortransfer" + CROSS_CHANNEL_ADJUSTMENT_CURVES = "krita_filter_crosschannel" + DESATURATE = "krita_filter_desaturate" + DODGE = "krita_filter_dodge" + EDGE_DETECTION = "krita_filter_edge detection" + EMBOSS_WITH_VARIABLE_DEPTH = "krita_filter_emboss" + EMBOSS_IN_ALL_DIRECTIONS = "krita_filter_emboss all directions" + EMBOSS_HORIZONTAL_VERTICAL = "krita_filter_emboss horizontal and vertical" + EMBOSS_HORIZONTAL_ONLY = "krita_filter_emboss horizontal only" + EMBOSS_LAPLACIAN = "krita_filter_emboss laplascian" + EMBOSS_VERTICAL_ONLY = "krita_filter_emboss vertical only" + GAUSSIAN_BLUR = "krita_filter_gaussian blur" + GAUSSIAN_HIGH_PASS = "krita_filter_gaussianhighpass" + GAUSSIAN_NOISE_REDUCTION = "krita_filter_gaussiannoisereducer" + GRADIENT_MAP = "krita_filter_gradientmap" + HALFTONE = "krita_filter_halftone" + HEIGHT_TO_NORMAL_MAP = "krita_filter_height to normal" + H_S_V_ADJUSTMENT = "krita_filter_hsvadjustment" + INDEX_COLOURS = "krita_filter_indexcolors" + INVERT = "krita_filter_invert" + LENS_BLUR = "krita_filter_lens blur" + LEVELS = "krita_filter_levels" + MAXIMISE_CHANNEL = "krita_filter_maximize" + MEAN_REMOVAL = "krita_filter_mean removal" + MINIMISE_CHANNEL = "krita_filter_minimize" + MOTION_BLUR = "krita_filter_motion blur" + RANDOM_NOISE = "krita_filter_noise" + NORMALISE = "krita_filter_normalize" + OILPAINT = "krita_filter_oilpaint" + PALETTISE = "krita_filter_palettize" + COLOUR_ADJUSTMENT_CURVES = "krita_filter_perchannel" + PHONG_BUMPMAP = "krita_filter_phongbumpmap" + PIXELISE = "krita_filter_pixelize" + POSTERISE = "krita_filter_posterize" + RAINDROPS = "krita_filter_raindrops" + RANDOM_PICK = "krita_filter_randompick" + ROUND_CORNERS = "krita_filter_roundcorners" + SHARPEN = "krita_filter_sharpen" + SMALL_TILES = "krita_filter_smalltiles" + THRESHOLD = "krita_filter_threshold" + UNSHARP_MASK = "krita_filter_unsharp" + WAVE = "krita_filter_wave" + WAVELET_NOISE_REDUCER = "krita_filter_waveletnoisereducer" + CUT = "edit_cut" + COPY = "edit_copy" + PASTE = "edit_paste" + COPY_SHARP = "copy_sharp" + CUT_SHARP = "cut_sharp" + PASTE_INTO_NEW_IMAGE = "paste_new" + PASTE_AT_CURSOR = "paste_at" + PASTE_INTO_ACTIVE_LAYER = "paste_into" + PASTE_AS_REFERENCE_IMAGE = "paste_as_reference" + PASTE_SHAPE_STYLE = "paste_shape_style" + COPY_MERGED = "copy_merged" + SELECT_ALL = "select_all" + DESELECT = "deselect" + CLEAR = "clear" + RESELECT = "reselect" + INVERT_SELECTION = "invert_selection" + COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" + CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" + FILL_WITH_FOREGROUND_COLOUR = "fill_selection_foreground_color" + FILL_WITH_BACKGROUND_COLOUR = "fill_selection_background_color" + FILL_WITH_PATTERN = "fill_selection_pattern" + FILL_WITH_FOREGROUND_COLOUR_OPACITY = \ + "fill_selection_foreground_color_opacity" + FILL_WITH_BACKGROUND_COLOUR_OPACITY = \ + "fill_selection_background_color_opacity" + FILL_WITH_PATTERN_OPACITY = "fill_selection_pattern_opacity" + STROKE_SELECTED_SHAPES = "stroke_shapes" + DISPLAY_SELECTION = "toggle_display_selection" + TRIM_TO_SELECTION = "resizeimagetoselection" + EDIT_SELECTION = "edit_selection" + CONVERT_TO_VECTOR_SELECTION = "convert_to_vector_selection" + CONVERT_TO_RASTER_SELECTION = "convert_to_raster_selection" + CONVERT_SHAPES_TO_VECTOR_SELECTION = "convert_shapes_to_vector_selection" + CONVERT_TO_SHAPE = "convert_selection_to_shape" + TOGGLE_SELECTION_DISPLAY_MODE = "toggle-selection-overlay-mode" + STROKE_SELECTION = "stroke_selection" + SHOW_GUIDES = "view_show_guides" + LOCK_GUIDES = "view_lock_guides" + SNAP_TO_GUIDES = "view_snap_to_guides" + SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" + SNAP_ORTHOGONAL = "view_snap_orthogonal" + SNAP_NODE = "view_snap_node" + SNAP_EXTENSION = "view_snap_extension" + SNAP_INTERSECTION = "view_snap_intersection" + SNAP_BOUNDING_BOX = "view_snap_bounding_box" + SNAP_IMAGE_BOUNDS = "view_snap_image_bounds" + SNAP_IMAGE_CENTRE = "view_snap_image_center" + SNAP_PIXEL = "view_snap_to_pixel" + FLATTEN_IMAGE = "flatten_image" + MERGE_WITH_LAYER_BELOW = "merge_layer" + FLATTEN_LAYER = "flatten_layer" + SAVE_GROUP_LAYERS = "save_groups_as_images" + CONVERT_GROUP_TO_ANIMATED_LAYER = "convert_group_to_animated" + TRIM_TO_CURRENT_LAYER = "resizeimagetolayer" + TRIM_TO_IMAGE_SIZE = "trim_to_image" + LAYER_STYLE = "layer_style" + COPY_LAYER_STYLE = "copy_layer_style" + PASTE_LAYER_STYLE = "paste_layer_style" + MIRROR_LAYER_HORIZONTALLY = "mirrorNodeX" + MIRROR_LAYER_VERTICALLY = "mirrorNodeY" + MIRROR_ALL_LAYERS_HORIZONTALLY = "mirrorAllNodesX" + MIRROR_ALL_LAYERS_VERTICALLY = "mirrorAllNodesY" + ACTIVATE_NEXT_LAYER = "activateNextLayer" + ACTIVATE_NEXT_SIBLING_LAYER, _SKIPPING_OVER_GROUPS = \ + "activateNextSiblingLayer" + ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" + ACTIVATE_PREVIOUS_SIBLING_LAYER, _SKIPPING_OVER_GROUPS = \ + "activatePreviousSiblingLayer" + ACTIVATE_PREVIOUSLY_SELECTED_LAYER = "switchToPreviouslyActiveNode" + SAVE_LAYER_MASK = "save_node_as_image" + SAVE_VECTOR_LAYER_AS_S_V_G = "save_vector_node_to_svg" + DUPLICATE_LAYER_OR_MASK = "duplicatelayer" + COPY_LAYER = "copy_layer_clipboard" + CUT_LAYER = "cut_layer_clipboard" + PASTE_LAYER = "paste_layer_from_clipboard" + QUICK_GROUP = "create_quick_group" + QUICK_CLIPPING_GROUP = "create_quick_clipping_group" + QUICK_UNGROUP = "quick_ungroup" + ALL_LAYERS = "select_all_layers" + VISIBLE_LAYERS = "select_visible_layers" + LOCKED_LAYERS = "select_locked_layers" + INVISIBLE_LAYERS = "select_invisible_layers" + UNLOCKED_LAYERS = "select_unlocked_layers" + NEW_LAYER_FROM_VISIBLE = "new_from_visible" + PIN_TO_TIMELINE = "pin_to_timeline" + ADD_PAINT_LAYER = "add_new_paint_layer" + ADD_GROUP_LAYER = "add_new_group_layer" + ADD_CLONE_LAYER = "add_new_clone_layer" + ADD_VECTOR_LAYER = "add_new_shape_layer" + ADD_FILTER_LAYER = "add_new_adjustment_layer" + ADD_FILL_LAYER = "add_new_fill_layer" + ADD_FILE_LAYER = "add_new_file_layer" + ADD_TRANSPARENCY_MASK = "add_new_transparency_mask" + ADD_FILTER_MASK = "add_new_filter_mask" + ADD_COLOURISE_MASK = "add_new_colorize_mask" + ADD_TRANSFORM_MASK = "add_new_transform_mask" + ADD_LOCAL_SELECTION = "add_new_selection_mask" + CONVERT_TO_PAINT_LAYER = "convert_to_paint_layer" + CONVERT_TO_SELECTION_MASK = "convert_to_selection_mask" + CONVERT_TO_FILTER_MASK = "convert_to_filter_mask" + CONVERT_TO_TRANSPARENCY_MASK = "convert_to_transparency_mask" + CONVERT_TO_ANIMATED_LAYER = "convert_to_animated" + TO_FILE_LAYER = "convert_to_file_layer" + ISOLATE_ACTIVE_GROUP = "isolate_active_group" + TOGGLE_LAYER_VISIBILITY = "toggle_layer_visibility" + TOGGLE_LAYER_LOCK = "toggle_layer_lock" + TOGGLE_LAYER_ALPHA_INHERITANCE = "toggle_layer_inherit_alpha" + TOGGLE_LAYER_ALPHA = "toggle_layer_alpha_lock" + ALPHA_INTO_MASK = "split_alpha_into_mask" + WRITE_AS_ALPHA = "split_alpha_write" + SAVE_MERGED = "split_alpha_save_merged" + IMPORT_LAYER = "import_layer_from_file" + PROPERTIES = "image_properties" + AS_PAINT_LAYER = "import_layer_as_paint_layer" + AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" + AS_FILTER_MASK = "import_layer_as_filter_mask" + AS_SELECTION_MASK = "import_layer_as_selection_mask" + IMAGE_BACKGROUND_COLOUR_AND_TRANSPARENCY = "image_color" + MAKE_BRUSH_COLOUR_LIGHTER = "make_brush_color_lighter" + MAKE_BRUSH_COLOUR_DARKER = "make_brush_color_darker" + MAKE_BRUSH_COLOUR_MORE_SATURATED = "make_brush_color_saturated" + MAKE_BRUSH_COLOUR_MORE_DESATURATED = "make_brush_color_desaturated" + SHIFT_BRUSH_COLOUR_HUE_CLOCKWISE = "shift_brush_color_clockwise" + SHIFT_BRUSH_COLOUR_HUE_COUNTER_CLOCKWISE = \ + "shift_brush_color_counter_clockwise" + MAKE_BRUSH_COLOUR_MORE_RED = "make_brush_color_redder" + MAKE_BRUSH_COLOUR_MORE_GREEN = "make_brush_color_greener" + MAKE_BRUSH_COLOUR_MORE_BLUE = "make_brush_color_bluer" + MAKE_BRUSH_COLOUR_MORE_YELLOW = "make_brush_color_yellower" + INCREASE_OPACITY = "increase_opacity" + DECREASE_OPACITY = "decrease_opacity" + INCREASE_FLOW = "increase_flow" + DECREASE_FLOW = "decrease_flow" + INCREASE_FADE = "increase_fade" + DECREASE_FADE = "decrease_fade" + INCREASE_SCATTER = "increase_scatter" + DECREASE_SCATTER = "decrease_scatter" + MIRROR_VIEW_AROUND_CURSOR = "mirror_canvas_around_cursor" + PATTERNS = "patterns" + GRADIENTS = "gradients" + CHOOSE_FOREGROUND_AND_BACKGROUND_COLOURS = "dual" + RELOAD_ORIGINAL_PRESET = "reload_preset_action" + HIDE_MIRROR_X_LINE = "mirrorX-hideDecorations" + LOCK_X_LINE = "mirrorX-lock" + MOVE_TO_CANVAS_CENTRE_X = "mirrorX-moveToCenter" + HIDE_MIRROR_Y_LINE = "mirrorY-hideDecorations" + LOCK_Y_LINE = "mirrorY-lock" + MOVE_TO_CANVAS_CENTRE_Y = "mirrorY-moveToCenter" + HORIZONTAL_MIRROR_TOOL = "hmirror_action" + VERTICAL_MIRROR_TOOL = "vmirror_action" + NEXT_BLENDING_MODE = "Next Blending Mode" + PREVIOUS_BLENDING_MODE = "Previous Blending Mode" + BRUSH_COMPOSITE = "composite_actions" + BRUSH_OPTION_SLIDER_1 = "brushslider1" + BRUSH_OPTION_SLIDER_2 = "brushslider2" + BRUSH_OPTION_SLIDER_3 = "brushslider3" + BRUSH_OPTION_SLIDER_4 = "brushslider4" + NEXT_FAVOURITE_PRESET = "next_favorite_preset" + PREVIOUS_FAVOURITE_PRESET = "previous_favorite_preset" + SWITCH_TO_PREVIOUS_PRESET = "previous_preset" + SHOW_BRUSH_PRESETS = "show_brush_presets" + MIRROR = "mirror_actions" + WORKSPACES = "workspaces" + SHOW_BRUSH_EDITOR = "show_brush_editor" + USE_PEN_PRESSURE = "disable_pressure" + OPEN_FOREGROUND_COLOUR_SELECTOR = "chooseForegroundColor" + OPEN_BACKGROUND_COLOUR_SELECTOR = "chooseBackgroundColor" + SCALE_IMAGE_TO_NEW_SIZE = "imagesize" + RESIZE_CANVAS = "canvassize" + SCALE_LAYER_TO_NEW_SIZE = "layersize" + SCALE_ALL_LAYERS_TO_NEW_SIZE = "scaleAllLayers" + SCALE = "selectionscale" + SHOW_KRITA_LOG_FOR_BUG_REPORTS = "buginfo" + SHOW_SYSTEM_INFORMATION_FOR_BUG_REPORTS = "sysinfo" + SHOW_CRASH_LOG_FOR_BUG_REPORTS = "crashlog" + CLONES_ARRAY = "clones_array" + SELECT_FROM_COLOUR_RANGE = "colorrange" + SELECT_OPAQUE_REPLACE = "selectopaque" + SELECT_OPAQUE_ADD = "selectopaque_add" + SELECT_OPAQUE_SUBTRACT = "selectopaque_subtract" + SELECT_OPAQUE_INTERSECT = "selectopaque_intersect" + CONVERT_IMAGE_COLOUR_SPACE = "imagecolorspaceconversion" + CONVERT_LAYER_COLOUR_SPACE = "layercolorspaceconversion" + EXPLORE_RESOURCES_CACHE_DATABASE = "dbexplorer" + IMAGE_SPLIT = "imagesplit" + MOVE_INTO_PREVIOUS_GROUP = "LayerGroupSwitcher/previous" + MOVE_INTO_NEXT_GROUP = "LayerGroupSwitcher/next" + SPLIT_LAYER = "layersplit" + EDIT_METADATA = "EditLayerMetaData" + GROW_SELECTION = "growselection" + SHRINK_SELECTION = "shrinkselection" + BORDER_SELECTION = "borderselection" + FEATHER_SELECTION = "featherselection" + SMOOTH = "smoothselection" + OFFSET_IMAGE = "offsetimage" + OFFSET_LAYER = "offsetlayer" + START_G_M_I_C_QT = "QMic" + RE_APPLY_THE_LAST_G_M_I_C_FILTER = "QMicAgain" + MANAGE_RESOURCE_LIBRARIES = "manage_bundles" + MANAGE_RESOURCES = "manage_resources" + ROTATE_IMAGE = "rotateimage" + ROTATE_IMAGE_90_TO_THE_RIGHT = "rotateImageCW90" + ROTATE_IMAGE_180 = "rotateImage180" + ROTATE_IMAGE_90_TO_THE_LEFT = "rotateImageCCW90" + MIRROR_IMAGE_HORIZONTALLY = "mirrorImageHorizontal" + MIRROR_IMAGE_VERTICALLY = "mirrorImageVertical" + ROTATE_LAYER = "rotatelayer" + ROTATE_LAYER_180 = "rotateLayer180" + ROTATE_LAYER_90_TO_THE_RIGHT = "rotateLayerCW90" + ROTATE_LAYER_90_TO_THE_LEFT = "rotateLayerCCW90" + ROTATE_ALL_LAYERS = "rotateAllLayers" + ROTATE_ALL_LAYERS_90_TO_THE_RIGHT = "rotateAllLayersCW90" + ROTATE_ALL_LAYERS_90_TO_THE_LEFT = "rotateAllLayersCCW90" + ROTATE_ALL_LAYERS_180 = "rotateAllLayers180" + SEPARATE_IMAGE = "separate" + SHEAR_IMAGE = "shearimage" + SHEAR_LAYER = "shearlayer" + SHEAR_ALL_LAYERS = "shearAllLayers" + WAVELET_DECOMPOSE = "waveletdecompose" + INCREASE_BRUSH_SIZE = "increase_brush_size" + DECREASE_BRUSH_SIZE = "decrease_brush_size" + SELECTION_MODE_ADD = "selection_tool_mode_add" + SELECTION_MODE_REPLACE = "selection_tool_mode_replace" + SELECTION_MODE_SUBTRACT = "selection_tool_mode_subtract" + SELECTION_MODE_INTERSECT = "selection_tool_mode_intersect" + UNDO_POLYGON_SELECTION_POINTS = "undo_polygon_selection" + CORNER_POINT = "pathpoint-corner" + SMOOTH_POINT = "pathpoint-smooth" + SYMMETRIC_POINT = "pathpoint-symmetric" + MAKE_CURVE_POINT = "pathpoint-curve" + MAKE_LINE_POINT = "pathpoint-line" + SEGMENT_TO_LINE = "pathsegment-line" + SEGMENT_TO_CURVE = "pathsegment-curve" + INSERT_POINT = "pathpoint-insert" + REMOVE_POINT = "pathpoint-remove" + BREAK_AT_POINT = "path-break-point" + BREAK_AT_SEGMENT = "path-break-segment" + JOIN_WITH_SEGMENT = "pathpoint-join" + MERGE_POINTS = "pathpoint-merge" + TO_PATH = "convert-to-path" + BRING_TO_FRONT = "object_order_front" + RAISE = "object_order_raise" + LOWER = "object_order_lower" + SEND_TO_BACK = "object_order_back" + ALIGN_LEFT = "object_align_horizontal_left" + HORIZONTALLY_CENTRE = "object_align_horizontal_center" + ALIGN_RIGHT = "object_align_horizontal_right" + ALIGN_TOP = "object_align_vertical_top" + VERTICALLY_CENTRE = "object_align_vertical_center" + ALIGN_BOTTOM = "object_align_vertical_bottom" + DISTRIBUTE_LEFT = "object_distribute_horizontal_left" + DISTRIBUTE_CENTRES_HORIZONTALLY = "object_distribute_horizontal_center" + DISTRIBUTE_RIGHT = "object_distribute_horizontal_right" + DISTRIBUTE_HORIZONTAL_GAP = "object_distribute_horizontal_gaps" + DISTRIBUTE_TOP = "object_distribute_vertical_top" + DISTRIBUTE_CENTRES_VERTICALLY = "object_distribute_vertical_center" + DISTRIBUTE_BOTTOM = "object_distribute_vertical_bottom" + DISTRIBUTE_VERTICAL_GAP = "object_distribute_vertical_gaps" + GROUP = "object_group" + UNGROUP = "object_ungroup" + ROTATE_90_C_W = "object_transform_rotate_90_cw" + ROTATE_90_A_C_W = "object_transform_rotate_90_ccw" + ROTATE_180 = "object_transform_rotate_180" + MIRROR_HORIZONTALLY = "object_transform_mirror_horizontally" + MIRROR_VERTICALLY = "object_transform_mirror_vertically" + RESET_TRANSFORMATIONS = "object_transform_reset" + UNITE = "object_unite" + INTERSECT = "object_intersect" + SUBTRACT = "object_subtract" + SPLIT = "object_split" + BRUSH_SMOOTHING_DISABLED = "set_no_brush_smoothing" + BRUSH_SMOOTHING_BASIC = "set_simple_brush_smoothing" + BRUSH_SMOOTHING_WEIGHTED = "set_weighted_brush_smoothing" + BRUSH_SMOOTHING_STABILISER = "set_stabilizer_brush_smoothing" + MOVE_UP = "movetool-move-up" + MOVE_DOWN = "movetool-move-down" + MOVE_LEFT = "movetool-move-left" + MOVE_RIGHT = "movetool-move-right" + MOVE_UP_MORE = "movetool-move-up-more" + MOVE_DOWN_MORE = "movetool-move-down-more" + MOVE_LEFT_MORE = "movetool-move-left-more" + MOVE_RIGHT_MORE = "movetool-move-right-more" + SHOW_COORDINATES = "movetool-show-coordinates" + CALLIGRAPHY = "KarbonCalligraphyTool" + CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" + CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" + CALLIGRAPHY_INCREASE_ANGLE = "calligraphy_increase_angle" + CALLIGRAPHY_DECREASE_ANGLE = "calligraphy_decrease_angle" + ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" + REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" + HOLD_CONSTANT_VALUE_NO_INTERPOLATION = "interpolation_constant" + LINEAR_INTERPOLATION = "interpolation_linear" + BEZIER_CURVE_INTERPOLATION = "interpolation_bezier" + SHARP_INTERPOLATION_TANGENTS = "tangents_sharp" + SMOOTH_INTERPOLATION_TANGENTS = "tangents_smooth" + ZOOM_VIEW_TO_FIT_CHANNEL_RANGE = "zoom_to_fit_range" + ZOOM_VIEW_TO_FIT_CURVE = "zoom_to_fit_curve" + DROP_FRAMES = "drop_frames" + SHOW_GLOBAL_SELECTION_MASK = "show-global-selection-mask" + RENAME_CURRENT_LAYER = "RenameCurrentLayer" + LAYER_PROPERTIES = "layer_properties" + REMOVE_LAYER = "remove_layer" + MOVE_LAYER_OR_MASK_UP = "move_layer_up" + MOVE_LAYER_OR_MASK_DOWN = "move_layer_down" + SET_COPY_FROM = "set-copy-from" + TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" + CREATE_SNAPSHOT = "create_snapshot" + SWITCH_TO_SELECTED_SNAPSHOT = "switchto_snapshot" + REMOVE_SELECTED_SNAPSHOT = "remove_snapshot" + INSERT_COLUMN_LEFT = "insert_column_left" + INSERT_COLUMN_RIGHT = "insert_column_right" + INSERT_MULTIPLE_COLUMNS = "insert_multiple_columns" + REMOVE_COLUMN_AND_PULL = "remove_columns_and_pull" + REMOVE_COLUMN = "remove_columns" + INSERT_HOLD_COLUMN = "insert_hold_column" + INSERT_MULTIPLE_HOLD_COLUMNS = "insert_multiple_hold_columns" + REMOVE_HOLD_COLUMN = "remove_hold_column" + REMOVE_MULTIPLE_HOLD_COLUMNS = "remove_multiple_hold_columns" + MIRROR_COLUMNS = "mirror_columns" + CLEAR_CACHE = "clear_animation_cache" + COPY_COLUMNS = "copy_columns_to_clipboard" + CUT_COLUMNS = "cut_columns_to_clipboard" + PASTE_COLUMNS = "paste_columns_from_clipboard" + CREATE_BLANK_FRAME = "add_blank_frame" + CREATE_DUPLICATE_FRAME = "add_duplicate_frame" + INSERT_KEYFRAME_LEFT = "insert_keyframe_left" + INSERT_KEYFRAME_RIGHT = "insert_keyframe_right" + INSERT_MULTIPLE_KEYFRAMES = "insert_multiple_keyframes" + REMOVE_FRAME_AND_PULL = "remove_frames_and_pull" + REMOVE_KEYFRAME = "remove_frames" + INSERT_HOLD_FRAME = "insert_hold_frame" + INSERT_MULTIPLE_HOLD_FRAMES = "insert_multiple_hold_frames" + REMOVE_HOLD_FRAME = "remove_hold_frame" + REMOVE_MULTIPLE_HOLD_FRAMES = "remove_multiple_hold_frames" + MIRROR_FRAMES = "mirror_frames" + COPY_KEYFRAMES = "copy_frames" + CLONE_KEYFRAMES = "copy_frames_as_clones" + MAKE_UNIQUE = "make_clones_unique" + CUT_KEYFRAMES = "cut_frames" + PASTE_KEYFRAMES = "paste_frames" + SET_START_TIME = "set_start_time" + SET_END_TIME = "set_end_time" + UPDATE_PLAYBACK_RANGE = "update_playback_range" + PLAY_PAUSE_ANIMATION = "toggle_playback" + STOP_ANIMATION = "stop_playback" + PREVIOUS_FRAME = "previous_frame" + NEXT_FRAME = "next_frame" + PREVIOUS_KEYFRAME = "previous_keyframe" + NEXT_KEYFRAME = "next_keyframe" + PREVIOUS_MATCHING_KEYFRAME = "previous_matching_keyframe" + NEXT_MATCHING_KEYFRAME = "next_matching_keyframe" + PREVIOUS_UNFILTERED_KEYFRAME = "previous_unfiltered_keyframe" + NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" + AUTO_FRAME_MODE = "auto_key" + NEW = "file_new" + OPEN = "file_open" + QUIT = "file_quit" + CONFIGURE_TOOLBARS = "options_configure_toolbars" + FULL_SCREEN_MODE = "fullscreen" + OPEN_RECENT = "file_open_recent" + SAVE = "file_save" + SAVE_AS = "file_save_as" + UNDO = "edit_undo" + REDO = "edit_redo" + IMPORT_ANIMATION_FRAMES = "file_import_animation" + IMPORT_VIDEO_ANIMATION = "file_import_video_animation" + RENDER_ANIMATION = "render_animation" + RENDER_ANIMATION_AGAIN = "render_animation_again" + CLOSE_ALL = "file_close_all" + OPEN_EXISTING_DOCUMENT_AS_UNTITLED_DOCUMENT = "file_import_file" + EXPORT = "file_export_file" + EXPORT_ADVANCED = "file_export_advanced" + DOCUMENT_INFORMATION = "file_documentinfo" + THEMES = "theme_menu" + SHOW_DOCKERS = "view_toggledockers" + RESET_ALL_SETTINGS = "reset_configurations" + DETACH_CANVAS = "view_detached_canvas" + SHOW_DOCKER_TITLEBARS = "view_toggledockertitlebars" + DOCKERS = "settings_dockers_menu" + WINDOW = "window" + STYLES = "style_menu" + CASCADE = "windows_cascade" + TILE = "windows_tile" + NEXT = "windows_next" + PREVIOUS = "windows_previous" + NEW_WINDOW = "view_newwindow" + CLOSE = "file_close" + SESSIONS = "file_sessions" + SEARCH_ACTIONS = "command_bar_open" + CONFIGURE_KRITA = "options_configure" + KRITA_HANDBOOK = "help_contents" + REPORT_BUG = "help_report_bug" + SWITCH_APPLICATION_LANGUAGE = "switch_application_language" + ABOUT_KRITA = "help_about_app" + ABOUT_K_D_E = "help_about_kde" + TRANSFORM_TOOL_FREE = "Transform tool: free" + TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" + TRANSFORM_TOOL_WARP = "Transform tool: warp" + TRANSFORM_TOOL_CAGE = "Transform tool: cage" + TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" + TRANSFORM_TOOL_MESH = "Transform tool: mesh" + TEMPORARY_MOVE_TOOL = "Temporary move tool" + TEMPORARY_ERASER = "Temporary eraser" + TEMPORARY_PRESERVE_ALPHA = "Temporary preserve alpha" + PREVIEW_CURRENT_LAYER_VISIBILITY = "Preview current layer visibility" + PREVIEW_PROJECTION_BELOW = "Preview projection below" + CYCLE_PAINTING_OPACITY = "Cycle painting opacity" + CYCLE_SELECTION_TOOLS = "Cycle selection tools" + SCROLL_UNDO_STACK = "Scroll undo stack" + SCROLL_ISOLATED_LAYERS = "Scroll isolated layers" + SCROLL_TIMELINE_OR_ANIMATED_LAYERS = "Scroll timeline or animated layers" + SCROLL_BRUSH_SIZE_OR_OPACITY = "Scroll brush size or opacity" + SCROLL_CANVAS_ZOOM_OR_ROTATION = "Scroll canvas zoom or rotation" + PICK_MISC_TOOLS = "Pick misc tools" + PICK_PAINTING_BLENDING_MODES = "Pick painting blending modes" + CREATE_PAINTING_LAYER_WITH_BLENDING_MODE = \ + "Create painting layer with blending mode" + PICK_TRANSFORM_TOOL_MODES = "Pick transform tool modes" + PICK_BRUSH_PRESETS_RED = "Pick brush presets (red)" + PICK_BRUSH_PRESETS_GREEN = "Pick brush presets (green)" + PICK_BRUSH_PRESETS_BLUE = "Pick brush presets (blue)" + PICK_COLOR_SAMPLER_BLEND = "Pick Color Sampler Blend" + COLOUR_SPACE = "color_space" + DOCUMENT_TOOLS = "document_tools" + EXPORT_LAYERS = "export_layers" + FILTER_MANAGER = "filter_manager" + IMPORT_PYTHON_PLUGIN_FROM_FILE = "plugin_importer_file" + IMPORT_PYTHON_PLUGIN_FROM_WEB = "plugin_importer_web" + SCRIPTER = "python_scripter" + TEN_SCRIPTS = "ten_scripts" + EXECUTE_SCRIPT_1 = "execute_script_1" + EXECUTE_SCRIPT_2 = "execute_script_2" + EXECUTE_SCRIPT_3 = "execute_script_3" + EXECUTE_SCRIPT_4 = "execute_script_4" + EXECUTE_SCRIPT_5 = "execute_script_5" + EXECUTE_SCRIPT_6 = "execute_script_6" + EXECUTE_SCRIPT_7 = "execute_script_7" + EXECUTE_SCRIPT_8 = "execute_script_8" + EXECUTE_SCRIPT_9 = "execute_script_9" + EXECUTE_SCRIPT_10 = "execute_script_10" + RECORD_TIMELAPSE = "recorder_record_toggle" + EXPORT_TIMELAPSE = "recorder_export" + HIDE_FILE_TOOLBAR = "mainToolBar" + HIDE_BRUSHES_AND_STUFF_TOOLBAR = "BrushesAndStuff" + ZOOM = "view_zoom" + SHOW_COLOUR_SELECTOR = "show_color_selector" + SHOW_MY_PAINT_SHADE_SELECTOR = "show_mypaint_shade_selector" + SHOW_MINIMAL_SHADE_SELECTOR = "show_minimal_shade_selector" + SHOW_COLOUR_HISTORY = "show_color_history" + SHOW_COMMON_COLOURS = "show_common_colors" + UPDATE_COMPOSITION = "update_composition" + RENAME_COMPOSITION = "rename_composition" # ShortcutComposer actions (The ones that make sense to use this way) - RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" # Reload Shortcut Composer - CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" # Configure Shortcut Composer + CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" + RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" def activate(self): Api.instance().action(self.value).trigger() - - @property - def icon(self) -> QIcon: - """Return the icon of this tool.""" - icon_name = _ICON_NAME_MAP.get(self, "edit-delete") - return Api.instance().icon(icon_name) - - -_ICON_NAME_MAP = { -} -"""Maps actions to names of their icons.""" From 4b4abb8af4480a3cf6ba3e7c0fe8f5e6d1f52272 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:10:22 +0200 Subject: [PATCH 48/69] WIP: action controller --- shortcut_composer/actions.action | 5 + shortcut_composer/actions.py | 14 +- shortcut_composer/api_krita/enums/__init__.py | 10 +- shortcut_composer/api_krita/enums/action.py | 266 ++++++++++-------- .../core_components/controllers/__init__.py | 2 + .../controllers/core_controllers.py | 37 ++- 6 files changed, 216 insertions(+), 118 deletions(-) diff --git a/shortcut_composer/actions.action b/shortcut_composer/actions.action index 5adc32a6..48b0bc74 100755 --- a/shortcut_composer/actions.action +++ b/shortcut_composer/actions.action @@ -90,6 +90,11 @@ krita_tool_grid + + 1 + + + 1 format-text-bold diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index a0e1714e..ef7d7807 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -14,7 +14,7 @@ from PyQt5.QtGui import QColor -from api_krita.enums import Tool, Toggle, BlendingMode, TransformMode +from api_krita.enums import Action, Tool, Toggle, BlendingMode, TransformMode from core_components import instructions, controllers from data_components import ( CurrentLayerStack, @@ -194,6 +194,18 @@ def create_actions() -> List[templates.RawInstructions]: return [ pie_radius_scale=0.9 ), + # Use pie menu to pick one of the actions + templates.PieMenu( + name="Activate krita action", + controller=controllers.ActionController(), + values=[ + Action.BLUR, + Action.SAVE, + Action.UNDO, + Action.TOGGLE_LAYER_ALPHA + ], + ), + # Use pie menu to pick one of the brush blending modes. # Set tool to FREEHAND BRUSH if current tool does not allow to paint templates.PieMenu( diff --git a/shortcut_composer/api_krita/enums/__init__.py b/shortcut_composer/api_krita/enums/__init__.py index 113d3df3..2ec6cc56 100644 --- a/shortcut_composer/api_krita/enums/__init__.py +++ b/shortcut_composer/api_krita/enums/__init__.py @@ -6,7 +6,15 @@ from .transform_mode import TransformMode from .blending_mode import BlendingMode from .node_types import NodeType +from .action import Action from .toggle import Toggle from .tool import Tool -__all__ = ["TransformMode", "BlendingMode", "NodeType", "Toggle", "Tool"] +__all__ = [ + "TransformMode", + "BlendingMode", + "NodeType", + "Action", + "Toggle", + "Tool" +] diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py index 1a03b188..02d083ae 100644 --- a/shortcut_composer/api_krita/enums/action.py +++ b/shortcut_composer/api_krita/enums/action.py @@ -4,42 +4,79 @@ from krita import Krita as Api from enum import Enum +from PyQt5.QtGui import QIcon + class Action(Enum): """ - Contains all known actions of Krita. + Contains actions of Krita exposed to the plugin. - Example usage: `Tool.FREEHAND_BRUSH` + Example usage: `Action.UNDO` """ + # File + NEW = "file_new" + OPEN = "file_open" + QUIT = "file_quit" + OPEN_RECENT = "file_open_recent" + SAVE = "file_save" + SAVE_AS = "file_save_as" + UNDO = "edit_undo" + REDO = "edit_redo" + FULL_SCREEN_MODE = "fullscreen" + IMPORT_ANIMATION_FRAMES = "file_import_animation" + IMPORT_VIDEO_ANIMATION = "file_import_video_animation" + RENDER_ANIMATION = "render_animation" + RENDER_ANIMATION_AGAIN = "render_animation_again" + CLOSE_ALL = "file_close_all" + OPEN_EXISTING_DOCUMENT_AS_UNTITLED_DOCUMENT = "file_import_file" + EXPORT = "file_export_file" + EXPORT_ADVANCED = "file_export_advanced" + DOCUMENT_INFORMATION = "file_documentinfo" + SAVE_INCREMENTAL_VERSION = "save_incremental_version" SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" - TOGGLE_TABLET_DEBUGGER = "tablet_debugger" CREATE_TEMPLATE_FROM_IMAGE = "create_template" CREATE_COPY_FROM_CURRENT_IMAGE = "create_copy" OPEN_RESOURCES_FOLDER = "open_resources_directory" + + # Canvas navigation ROTATE_CANVAS_RIGHT = "rotate_canvas_right" ROTATE_CANVAS_LEFT = "rotate_canvas_left" RESET_CANVAS_ROTATION = "reset_canvas_rotation" WRAP_AROUND_MODE = "wrap_around_mode" INSTANT_PREVIEW_MODE = "level_of_detail_mode" - OUT_OF_GAMUT_WARNINGS = "gamutCheck" SHOW_STATUS_BAR = "showStatusBar" SHOW_CANVAS_ONLY = "view_show_canvas_only" - USE_MULTIPLE_OF_2_FOR_PIXEL_SCALE = "ruler_pixel_multiple2" RULERS_TRACK_POINTER = "rulers_track_mouse" RESET_ZOOM = "zoom_to_100pct" ZOOM_IN = "view_zoom_in" ZOOM_OUT = "view_zoom_out" TOGGLE_ZOOM_FIT_TO_PAGE = "toggle_zoom_to_fit" - ACTIVE_AUTHOR_PROFILE = "settings_active_author" - SHOW_PIXEL_GRID = "view_pixel_grid" + MIRROR_VIEW_AROUND_CURSOR = "mirror_canvas_around_cursor" + SWAP_FOREGROUND_AND_BACKGROUND_COLOURS = "toggle_fg_bg" SET_FOREGROUND_AND_BACKGROUND_COLOURS_TO_BLACK_AND_WHITE = "reset_fg_bg" + + # Canvas toggles TOGGLE_BRUSH_OUTLINE = "toggle_brush_outline" + SHOW_GUIDES = "view_show_guides" + LOCK_GUIDES = "view_lock_guides" + SHOW_PIXEL_GRID = "view_pixel_grid" + SNAP_TO_GUIDES = "view_snap_to_guides" + SNAP_ORTHOGONAL = "view_snap_orthogonal" + SNAP_NODE = "view_snap_node" + SNAP_EXTENSION = "view_snap_extension" + SNAP_INTERSECTION = "view_snap_intersection" + SNAP_BOUNDING_BOX = "view_snap_bounding_box" + SNAP_IMAGE_BOUNDS = "view_snap_image_bounds" + SNAP_IMAGE_CENTRE = "view_snap_image_center" + SNAP_PIXEL = "view_snap_to_pixel" + + # Filters shortcuts APPLY_FILTER_AGAIN = "filter_apply_again" APPLY_FILTER_AGAIN_REPROMPT = "filter_apply_reprompt" - SLOPE, _OFFSET, _POWER = "krita_filter_asc-cdl" + FILTER_ASC_CDL = "krita_filter_asc-cdl" AUTO_CONTRAST = "krita_filter_autocontrast" BLUR = "krita_filter_blur" BURN = "krita_filter_burn" @@ -62,7 +99,7 @@ class Action(Enum): GRADIENT_MAP = "krita_filter_gradientmap" HALFTONE = "krita_filter_halftone" HEIGHT_TO_NORMAL_MAP = "krita_filter_height to normal" - H_S_V_ADJUSTMENT = "krita_filter_hsvadjustment" + HSV_ADJUSTMENT = "krita_filter_hsvadjustment" INDEX_COLOURS = "krita_filter_indexcolors" INVERT = "krita_filter_invert" LENS_BLUR = "krita_filter_lens blur" @@ -88,6 +125,8 @@ class Action(Enum): UNSHARP_MASK = "krita_filter_unsharp" WAVE = "krita_filter_wave" WAVELET_NOISE_REDUCER = "krita_filter_waveletnoisereducer" + + # Edit document CUT = "edit_cut" COPY = "edit_copy" PASTE = "edit_paste" @@ -99,13 +138,11 @@ class Action(Enum): PASTE_AS_REFERENCE_IMAGE = "paste_as_reference" PASTE_SHAPE_STYLE = "paste_shape_style" COPY_MERGED = "copy_merged" - SELECT_ALL = "select_all" - DESELECT = "deselect" - CLEAR = "clear" - RESELECT = "reselect" - INVERT_SELECTION = "invert_selection" - COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" - CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" + FLATTEN_IMAGE = "flatten_image" + MERGE_WITH_LAYER_BELOW = "merge_layer" + FLATTEN_LAYER = "flatten_layer" + + # Filling FILL_WITH_FOREGROUND_COLOUR = "fill_selection_foreground_color" FILL_WITH_BACKGROUND_COLOUR = "fill_selection_background_color" FILL_WITH_PATTERN = "fill_selection_pattern" @@ -115,6 +152,17 @@ class Action(Enum): "fill_selection_background_color_opacity" FILL_WITH_PATTERN_OPACITY = "fill_selection_pattern_opacity" STROKE_SELECTED_SHAPES = "stroke_shapes" + + # Selection + SELECT_ALL = "select_all" + DESELECT = "deselect" + CLEAR = "clear" + RESELECT = "reselect" + INVERT_SELECTION = "invert_selection" + SELECTION_MODE_ADD = "selection_tool_mode_add" + SELECTION_MODE_REPLACE = "selection_tool_mode_replace" + SELECTION_MODE_SUBTRACT = "selection_tool_mode_subtract" + SELECTION_MODE_INTERSECT = "selection_tool_mode_intersect" DISPLAY_SELECTION = "toggle_display_selection" TRIM_TO_SELECTION = "resizeimagetoselection" EDIT_SELECTION = "edit_selection" @@ -124,21 +172,10 @@ class Action(Enum): CONVERT_TO_SHAPE = "convert_selection_to_shape" TOGGLE_SELECTION_DISPLAY_MODE = "toggle-selection-overlay-mode" STROKE_SELECTION = "stroke_selection" - SHOW_GUIDES = "view_show_guides" - LOCK_GUIDES = "view_lock_guides" - SNAP_TO_GUIDES = "view_snap_to_guides" - SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" - SNAP_ORTHOGONAL = "view_snap_orthogonal" - SNAP_NODE = "view_snap_node" - SNAP_EXTENSION = "view_snap_extension" - SNAP_INTERSECTION = "view_snap_intersection" - SNAP_BOUNDING_BOX = "view_snap_bounding_box" - SNAP_IMAGE_BOUNDS = "view_snap_image_bounds" - SNAP_IMAGE_CENTRE = "view_snap_image_center" - SNAP_PIXEL = "view_snap_to_pixel" - FLATTEN_IMAGE = "flatten_image" - MERGE_WITH_LAYER_BELOW = "merge_layer" - FLATTEN_LAYER = "flatten_layer" + COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" + CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" + + # Edit layers SAVE_GROUP_LAYERS = "save_groups_as_images" CONVERT_GROUP_TO_ANIMATED_LAYER = "convert_group_to_animated" TRIM_TO_CURRENT_LAYER = "resizeimagetolayer" @@ -150,15 +187,9 @@ class Action(Enum): MIRROR_LAYER_VERTICALLY = "mirrorNodeY" MIRROR_ALL_LAYERS_HORIZONTALLY = "mirrorAllNodesX" MIRROR_ALL_LAYERS_VERTICALLY = "mirrorAllNodesY" - ACTIVATE_NEXT_LAYER = "activateNextLayer" - ACTIVATE_NEXT_SIBLING_LAYER, _SKIPPING_OVER_GROUPS = \ - "activateNextSiblingLayer" - ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" - ACTIVATE_PREVIOUS_SIBLING_LAYER, _SKIPPING_OVER_GROUPS = \ - "activatePreviousSiblingLayer" ACTIVATE_PREVIOUSLY_SELECTED_LAYER = "switchToPreviouslyActiveNode" SAVE_LAYER_MASK = "save_node_as_image" - SAVE_VECTOR_LAYER_AS_S_V_G = "save_vector_node_to_svg" + SAVE_VECTOR_LAYER_AS_SVG = "save_vector_node_to_svg" DUPLICATE_LAYER_OR_MASK = "duplicatelayer" COPY_LAYER = "copy_layer_clipboard" CUT_LAYER = "cut_layer_clipboard" @@ -166,13 +197,22 @@ class Action(Enum): QUICK_GROUP = "create_quick_group" QUICK_CLIPPING_GROUP = "create_quick_clipping_group" QUICK_UNGROUP = "quick_ungroup" - ALL_LAYERS = "select_all_layers" - VISIBLE_LAYERS = "select_visible_layers" - LOCKED_LAYERS = "select_locked_layers" - INVISIBLE_LAYERS = "select_invisible_layers" - UNLOCKED_LAYERS = "select_unlocked_layers" - NEW_LAYER_FROM_VISIBLE = "new_from_visible" PIN_TO_TIMELINE = "pin_to_timeline" + + # Layer stack + SELECT_ALL_LAYERS = "select_all_layers" + SELECT_VISIBLE_LAYERS = "select_visible_layers" + SELECT_LOCKED_LAYERS = "select_locked_layers" + SELECT_INVISIBLE_LAYERS = "select_invisible_layers" + SELECT_UNLOCKED_LAYERS = "select_unlocked_layers" + ACTIVATE_NEXT_LAYER = "activateNextLayer" + ACTIVATE_NEXT_SIBLING_LAYER = \ + "activateNextSiblingLayer" + ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" + ACTIVATE_PREVIOUS_SIBLING_LAYER = \ + "activatePreviousSiblingLayer" + + # Layer creation ADD_PAINT_LAYER = "add_new_paint_layer" ADD_GROUP_LAYER = "add_new_group_layer" ADD_CLONE_LAYER = "add_new_clone_layer" @@ -191,6 +231,10 @@ class Action(Enum): CONVERT_TO_TRANSPARENCY_MASK = "convert_to_transparency_mask" CONVERT_TO_ANIMATED_LAYER = "convert_to_animated" TO_FILE_LAYER = "convert_to_file_layer" + IMPORT_LAYER = "import_layer_from_file" + NEW_LAYER_FROM_VISIBLE = "new_from_visible" + + # Layer handling ISOLATE_ACTIVE_GROUP = "isolate_active_group" TOGGLE_LAYER_VISIBILITY = "toggle_layer_visibility" TOGGLE_LAYER_LOCK = "toggle_layer_lock" @@ -199,13 +243,21 @@ class Action(Enum): ALPHA_INTO_MASK = "split_alpha_into_mask" WRITE_AS_ALPHA = "split_alpha_write" SAVE_MERGED = "split_alpha_save_merged" - IMPORT_LAYER = "import_layer_from_file" - PROPERTIES = "image_properties" + + # Importing layers AS_PAINT_LAYER = "import_layer_as_paint_layer" AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" AS_FILTER_MASK = "import_layer_as_filter_mask" AS_SELECTION_MASK = "import_layer_as_selection_mask" IMAGE_BACKGROUND_COLOUR_AND_TRANSPARENCY = "image_color" + + # Brush property editing + NEXT_FAVOURITE_PRESET = "next_favorite_preset" + PREVIOUS_FAVOURITE_PRESET = "previous_favorite_preset" + SWITCH_TO_PREVIOUS_PRESET = "previous_preset" + RELOAD_ORIGINAL_PRESET = "reload_preset_action" + INCREASE_BRUSH_SIZE = "increase_brush_size" + DECREASE_BRUSH_SIZE = "decrease_brush_size" MAKE_BRUSH_COLOUR_LIGHTER = "make_brush_color_lighter" MAKE_BRUSH_COLOUR_DARKER = "make_brush_color_darker" MAKE_BRUSH_COLOUR_MORE_SATURATED = "make_brush_color_saturated" @@ -225,11 +277,24 @@ class Action(Enum): DECREASE_FADE = "decrease_fade" INCREASE_SCATTER = "increase_scatter" DECREASE_SCATTER = "decrease_scatter" - MIRROR_VIEW_AROUND_CURSOR = "mirror_canvas_around_cursor" + BRUSH_SMOOTHING_DISABLED = "set_no_brush_smoothing" + BRUSH_SMOOTHING_BASIC = "set_simple_brush_smoothing" + BRUSH_SMOOTHING_WEIGHTED = "set_weighted_brush_smoothing" + BRUSH_SMOOTHING_STABILISER = "set_stabilizer_brush_smoothing" + # Opening menus + CONFIGURE_KRITA = "options_configure" + CONFIGURE_TOOLBARS = "options_configure_toolbars" + CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" + THEMES = "theme_menu" + SHOW_DOCKERS = "view_toggledockers" + RESET_ALL_SETTINGS = "reset_configurations" PATTERNS = "patterns" GRADIENTS = "gradients" CHOOSE_FOREGROUND_AND_BACKGROUND_COLOURS = "dual" - RELOAD_ORIGINAL_PRESET = "reload_preset_action" + PROPERTIES = "image_properties" + SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" + SHOW_BRUSH_PRESETS = "show_brush_presets" + HIDE_MIRROR_X_LINE = "mirrorX-hideDecorations" LOCK_X_LINE = "mirrorX-lock" MOVE_TO_CANVAS_CENTRE_X = "mirrorX-moveToCenter" @@ -245,10 +310,7 @@ class Action(Enum): BRUSH_OPTION_SLIDER_2 = "brushslider2" BRUSH_OPTION_SLIDER_3 = "brushslider3" BRUSH_OPTION_SLIDER_4 = "brushslider4" - NEXT_FAVOURITE_PRESET = "next_favorite_preset" - PREVIOUS_FAVOURITE_PRESET = "previous_favorite_preset" - SWITCH_TO_PREVIOUS_PRESET = "previous_preset" - SHOW_BRUSH_PRESETS = "show_brush_presets" + MIRROR = "mirror_actions" WORKSPACES = "workspaces" SHOW_BRUSH_EDITOR = "show_brush_editor" @@ -284,16 +346,18 @@ class Action(Enum): SMOOTH = "smoothselection" OFFSET_IMAGE = "offsetimage" OFFSET_LAYER = "offsetlayer" - START_G_M_I_C_QT = "QMic" - RE_APPLY_THE_LAST_G_M_I_C_FILTER = "QMicAgain" + START_GMIC_QT = "QMic" + REAPPLY_THE_LAST_GMIC_FILTER = "QMicAgain" MANAGE_RESOURCE_LIBRARIES = "manage_bundles" MANAGE_RESOURCES = "manage_resources" + ROTATE_IMAGE = "rotateimage" ROTATE_IMAGE_90_TO_THE_RIGHT = "rotateImageCW90" ROTATE_IMAGE_180 = "rotateImage180" ROTATE_IMAGE_90_TO_THE_LEFT = "rotateImageCCW90" MIRROR_IMAGE_HORIZONTALLY = "mirrorImageHorizontal" MIRROR_IMAGE_VERTICALLY = "mirrorImageVertical" + ROTATE_LAYER = "rotatelayer" ROTATE_LAYER_180 = "rotateLayer180" ROTATE_LAYER_90_TO_THE_RIGHT = "rotateLayerCW90" @@ -302,17 +366,14 @@ class Action(Enum): ROTATE_ALL_LAYERS_90_TO_THE_RIGHT = "rotateAllLayersCW90" ROTATE_ALL_LAYERS_90_TO_THE_LEFT = "rotateAllLayersCCW90" ROTATE_ALL_LAYERS_180 = "rotateAllLayers180" + SEPARATE_IMAGE = "separate" SHEAR_IMAGE = "shearimage" SHEAR_LAYER = "shearlayer" SHEAR_ALL_LAYERS = "shearAllLayers" + WAVELET_DECOMPOSE = "waveletdecompose" - INCREASE_BRUSH_SIZE = "increase_brush_size" - DECREASE_BRUSH_SIZE = "decrease_brush_size" - SELECTION_MODE_ADD = "selection_tool_mode_add" - SELECTION_MODE_REPLACE = "selection_tool_mode_replace" - SELECTION_MODE_SUBTRACT = "selection_tool_mode_subtract" - SELECTION_MODE_INTERSECT = "selection_tool_mode_intersect" + UNDO_POLYGON_SELECTION_POINTS = "undo_polygon_selection" CORNER_POINT = "pathpoint-corner" SMOOTH_POINT = "pathpoint-smooth" @@ -358,10 +419,7 @@ class Action(Enum): INTERSECT = "object_intersect" SUBTRACT = "object_subtract" SPLIT = "object_split" - BRUSH_SMOOTHING_DISABLED = "set_no_brush_smoothing" - BRUSH_SMOOTHING_BASIC = "set_simple_brush_smoothing" - BRUSH_SMOOTHING_WEIGHTED = "set_weighted_brush_smoothing" - BRUSH_SMOOTHING_STABILISER = "set_stabilizer_brush_smoothing" + MOVE_UP = "movetool-move-up" MOVE_DOWN = "movetool-move-down" MOVE_LEFT = "movetool-move-left" @@ -442,28 +500,7 @@ class Action(Enum): PREVIOUS_UNFILTERED_KEYFRAME = "previous_unfiltered_keyframe" NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" AUTO_FRAME_MODE = "auto_key" - NEW = "file_new" - OPEN = "file_open" - QUIT = "file_quit" - CONFIGURE_TOOLBARS = "options_configure_toolbars" - FULL_SCREEN_MODE = "fullscreen" - OPEN_RECENT = "file_open_recent" - SAVE = "file_save" - SAVE_AS = "file_save_as" - UNDO = "edit_undo" - REDO = "edit_redo" - IMPORT_ANIMATION_FRAMES = "file_import_animation" - IMPORT_VIDEO_ANIMATION = "file_import_video_animation" - RENDER_ANIMATION = "render_animation" - RENDER_ANIMATION_AGAIN = "render_animation_again" - CLOSE_ALL = "file_close_all" - OPEN_EXISTING_DOCUMENT_AS_UNTITLED_DOCUMENT = "file_import_file" - EXPORT = "file_export_file" - EXPORT_ADVANCED = "file_export_advanced" - DOCUMENT_INFORMATION = "file_documentinfo" - THEMES = "theme_menu" - SHOW_DOCKERS = "view_toggledockers" - RESET_ALL_SETTINGS = "reset_configurations" + DETACH_CANVAS = "view_detached_canvas" SHOW_DOCKER_TITLEBARS = "view_toggledockertitlebars" DOCKERS = "settings_dockers_menu" @@ -477,43 +514,21 @@ class Action(Enum): CLOSE = "file_close" SESSIONS = "file_sessions" SEARCH_ACTIONS = "command_bar_open" - CONFIGURE_KRITA = "options_configure" KRITA_HANDBOOK = "help_contents" REPORT_BUG = "help_report_bug" SWITCH_APPLICATION_LANGUAGE = "switch_application_language" ABOUT_KRITA = "help_about_app" ABOUT_K_D_E = "help_about_kde" - TRANSFORM_TOOL_FREE = "Transform tool: free" - TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" - TRANSFORM_TOOL_WARP = "Transform tool: warp" - TRANSFORM_TOOL_CAGE = "Transform tool: cage" - TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" - TRANSFORM_TOOL_MESH = "Transform tool: mesh" - TEMPORARY_MOVE_TOOL = "Temporary move tool" - TEMPORARY_ERASER = "Temporary eraser" - TEMPORARY_PRESERVE_ALPHA = "Temporary preserve alpha" PREVIEW_CURRENT_LAYER_VISIBILITY = "Preview current layer visibility" PREVIEW_PROJECTION_BELOW = "Preview projection below" CYCLE_PAINTING_OPACITY = "Cycle painting opacity" CYCLE_SELECTION_TOOLS = "Cycle selection tools" - SCROLL_UNDO_STACK = "Scroll undo stack" - SCROLL_ISOLATED_LAYERS = "Scroll isolated layers" - SCROLL_TIMELINE_OR_ANIMATED_LAYERS = "Scroll timeline or animated layers" - SCROLL_BRUSH_SIZE_OR_OPACITY = "Scroll brush size or opacity" - SCROLL_CANVAS_ZOOM_OR_ROTATION = "Scroll canvas zoom or rotation" - PICK_MISC_TOOLS = "Pick misc tools" - PICK_PAINTING_BLENDING_MODES = "Pick painting blending modes" - CREATE_PAINTING_LAYER_WITH_BLENDING_MODE = \ - "Create painting layer with blending mode" - PICK_TRANSFORM_TOOL_MODES = "Pick transform tool modes" - PICK_BRUSH_PRESETS_RED = "Pick brush presets (red)" - PICK_BRUSH_PRESETS_GREEN = "Pick brush presets (green)" - PICK_BRUSH_PRESETS_BLUE = "Pick brush presets (blue)" - PICK_COLOR_SAMPLER_BLEND = "Pick Color Sampler Blend" COLOUR_SPACE = "color_space" DOCUMENT_TOOLS = "document_tools" EXPORT_LAYERS = "export_layers" FILTER_MANAGER = "filter_manager" + + # Python Scripts IMPORT_PYTHON_PLUGIN_FROM_FILE = "plugin_importer_file" IMPORT_PYTHON_PLUGIN_FROM_WEB = "plugin_importer_web" SCRIPTER = "python_scripter" @@ -528,6 +543,12 @@ class Action(Enum): EXECUTE_SCRIPT_8 = "execute_script_8" EXECUTE_SCRIPT_9 = "execute_script_9" EXECUTE_SCRIPT_10 = "execute_script_10" + TRANSFORM_TOOL_FREE = "Transform tool: free" + TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" + TRANSFORM_TOOL_WARP = "Transform tool: warp" + TRANSFORM_TOOL_CAGE = "Transform tool: cage" + TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" + TRANSFORM_TOOL_MESH = "Transform tool: mesh" RECORD_TIMELAPSE = "recorder_record_toggle" EXPORT_TIMELAPSE = "recorder_export" HIDE_FILE_TOOLBAR = "mainToolBar" @@ -540,10 +561,27 @@ class Action(Enum): SHOW_COMMON_COLOURS = "show_common_colors" UPDATE_COMPOSITION = "update_composition" RENAME_COMPOSITION = "rename_composition" - - # ShortcutComposer actions (The ones that make sense to use this way) - CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" def activate(self): - Api.instance().action(self.value).trigger() + """Activate the action.""" + try: + Api.instance().action(self.value).trigger() + except AttributeError: + print(self.value) + + @property + def icon(self) -> QIcon: + """Return the icon of this action.""" + try: + return Api.instance().action(self.value).icon() + except AttributeError: + return QIcon() + + @property + def pretty_name(self) -> str: + """Return the name of this action.""" + try: + return Api.instance().action(self.value).text().replace("&", "") + except AttributeError: + return "---" diff --git a/shortcut_composer/core_components/controllers/__init__.py b/shortcut_composer/core_components/controllers/__init__.py index fa65f4e2..47a59d0d 100644 --- a/shortcut_composer/core_components/controllers/__init__.py +++ b/shortcut_composer/core_components/controllers/__init__.py @@ -27,6 +27,7 @@ from .core_controllers import ( TransformModeController, ToggleController, + ActionController, ToolController, UndoController, ) @@ -44,6 +45,7 @@ "OpacityController", "ToggleController", "PresetController", + "ActionController", "TimeController", "ToolController", "UndoController", diff --git a/shortcut_composer/core_components/controllers/core_controllers.py b/shortcut_composer/core_components/controllers/core_controllers.py index d82d7ca5..d0211083 100644 --- a/shortcut_composer/core_components/controllers/core_controllers.py +++ b/shortcut_composer/core_components/controllers/core_controllers.py @@ -1,13 +1,14 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Optional +from typing import Optional, Union, NoReturn from dataclasses import dataclass from PyQt5.QtGui import QIcon from api_krita import Krita -from api_krita.enums import Tool, Toggle, TransformMode +from api_krita.pyqt import Text +from api_krita.enums import Action, Tool, Toggle, TransformMode from api_krita.actions import TransformModeFinder from ..controller_base import Controller @@ -42,6 +43,38 @@ def get_pretty_name(self, value: Tool) -> str: return value.pretty_name +class ActionController(Controller[Action]): + """ + Gives access to krita actions. + + - Operates on `Action` + - Does not have a default value. + """ + + TYPE = Action + + @staticmethod + def get_value() -> NoReturn: + """Get currently active tool.""" + raise NotImplementedError() + + @staticmethod + def set_value(value: Action) -> None: + """Set a passed tool.""" + value.activate() + + def get_label(self, value: Tool) -> Union[QIcon, Text]: + """Forward the tools' icon.""" + icon = value.icon + if not icon.isNull(): + return value.icon + return Text(value.name[:3]) + + def get_pretty_name(self, value: Action) -> str: + """Forward enums' pretty name.""" + return value.pretty_name + + class TransformModeController(Controller[TransformMode]): """ Gives access to tools from toolbox. From 2b483e551d70ba5f1c450e3142d2f595d665cd3b Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Wed, 28 Jun 2023 18:10:22 +0200 Subject: [PATCH 49/69] Create EnumGroup for actions --- shortcut_composer/api_krita/enums/action.py | 243 ++++++++++---------- 1 file changed, 126 insertions(+), 117 deletions(-) diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py index 02d083ae..4ae49b6d 100644 --- a/shortcut_composer/api_krita/enums/action.py +++ b/shortcut_composer/api_krita/enums/action.py @@ -2,19 +2,19 @@ # SPDX-License-Identifier: GPL-3.0-or-later from krita import Krita as Api -from enum import Enum from PyQt5.QtGui import QIcon +from .helpers import EnumGroup, Group -class Action(Enum): +class Action(EnumGroup): """ Contains actions of Krita exposed to the plugin. Example usage: `Action.UNDO` """ - # File + _file = Group("File") NEW = "file_new" OPEN = "file_open" QUIT = "file_quit" @@ -34,17 +34,17 @@ class Action(Enum): EXPORT_ADVANCED = "file_export_advanced" DOCUMENT_INFORMATION = "file_documentinfo" + _saving = Group("Saving") SAVE_INCREMENTAL_VERSION = "save_incremental_version" SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" CREATE_TEMPLATE_FROM_IMAGE = "create_template" CREATE_COPY_FROM_CURRENT_IMAGE = "create_copy" OPEN_RESOURCES_FOLDER = "open_resources_directory" - # Canvas navigation + _canvas_navigation = Group("Canvas navigation") ROTATE_CANVAS_RIGHT = "rotate_canvas_right" ROTATE_CANVAS_LEFT = "rotate_canvas_left" RESET_CANVAS_ROTATION = "reset_canvas_rotation" - WRAP_AROUND_MODE = "wrap_around_mode" INSTANT_PREVIEW_MODE = "level_of_detail_mode" SHOW_STATUS_BAR = "showStatusBar" SHOW_CANVAS_ONLY = "view_show_canvas_only" @@ -55,10 +55,7 @@ class Action(Enum): TOGGLE_ZOOM_FIT_TO_PAGE = "toggle_zoom_to_fit" MIRROR_VIEW_AROUND_CURSOR = "mirror_canvas_around_cursor" - SWAP_FOREGROUND_AND_BACKGROUND_COLOURS = "toggle_fg_bg" - SET_FOREGROUND_AND_BACKGROUND_COLOURS_TO_BLACK_AND_WHITE = "reset_fg_bg" - - # Canvas toggles + _canvas_toggles = Group("Canvas toggles") TOGGLE_BRUSH_OUTLINE = "toggle_brush_outline" SHOW_GUIDES = "view_show_guides" LOCK_GUIDES = "view_lock_guides" @@ -73,7 +70,7 @@ class Action(Enum): SNAP_IMAGE_CENTRE = "view_snap_image_center" SNAP_PIXEL = "view_snap_to_pixel" - # Filters shortcuts + _filters_shortcuts = Group("Filters shortcuts") APPLY_FILTER_AGAIN = "filter_apply_again" APPLY_FILTER_AGAIN_REPROMPT = "filter_apply_reprompt" FILTER_ASC_CDL = "krita_filter_asc-cdl" @@ -126,7 +123,7 @@ class Action(Enum): WAVE = "krita_filter_wave" WAVELET_NOISE_REDUCER = "krita_filter_waveletnoisereducer" - # Edit document + _edit_document = Group("Edit document") CUT = "edit_cut" COPY = "edit_copy" PASTE = "edit_paste" @@ -142,7 +139,7 @@ class Action(Enum): MERGE_WITH_LAYER_BELOW = "merge_layer" FLATTEN_LAYER = "flatten_layer" - # Filling + _filling = Group("Filling") FILL_WITH_FOREGROUND_COLOUR = "fill_selection_foreground_color" FILL_WITH_BACKGROUND_COLOUR = "fill_selection_background_color" FILL_WITH_PATTERN = "fill_selection_pattern" @@ -153,7 +150,7 @@ class Action(Enum): FILL_WITH_PATTERN_OPACITY = "fill_selection_pattern_opacity" STROKE_SELECTED_SHAPES = "stroke_shapes" - # Selection + _selection = Group("Selection") SELECT_ALL = "select_all" DESELECT = "deselect" CLEAR = "clear" @@ -175,7 +172,7 @@ class Action(Enum): COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" - # Edit layers + _edit_layers = Group("Edit layers") SAVE_GROUP_LAYERS = "save_groups_as_images" CONVERT_GROUP_TO_ANIMATED_LAYER = "convert_group_to_animated" TRIM_TO_CURRENT_LAYER = "resizeimagetolayer" @@ -199,7 +196,13 @@ class Action(Enum): QUICK_UNGROUP = "quick_ungroup" PIN_TO_TIMELINE = "pin_to_timeline" - # Layer stack + _layer_stack = Group("Layer stack") + LAYER_PROPERTIES = "layer_properties" + RENAME_CURRENT_LAYER = "RenameCurrentLayer" + REMOVE_LAYER = "remove_layer" + MOVE_LAYER_OR_MASK_UP = "move_layer_up" + MOVE_LAYER_OR_MASK_DOWN = "move_layer_down" + TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" SELECT_ALL_LAYERS = "select_all_layers" SELECT_VISIBLE_LAYERS = "select_visible_layers" SELECT_LOCKED_LAYERS = "select_locked_layers" @@ -212,7 +215,7 @@ class Action(Enum): ACTIVATE_PREVIOUS_SIBLING_LAYER = \ "activatePreviousSiblingLayer" - # Layer creation + _layer_creation = Group("Layer creation") ADD_PAINT_LAYER = "add_new_paint_layer" ADD_GROUP_LAYER = "add_new_group_layer" ADD_CLONE_LAYER = "add_new_clone_layer" @@ -234,7 +237,7 @@ class Action(Enum): IMPORT_LAYER = "import_layer_from_file" NEW_LAYER_FROM_VISIBLE = "new_from_visible" - # Layer handling + _layer_handling = Group("Layer handling") ISOLATE_ACTIVE_GROUP = "isolate_active_group" TOGGLE_LAYER_VISIBILITY = "toggle_layer_visibility" TOGGLE_LAYER_LOCK = "toggle_layer_lock" @@ -244,14 +247,14 @@ class Action(Enum): WRITE_AS_ALPHA = "split_alpha_write" SAVE_MERGED = "split_alpha_save_merged" - # Importing layers + _importing_layers = Group("Importing layers") AS_PAINT_LAYER = "import_layer_as_paint_layer" AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" AS_FILTER_MASK = "import_layer_as_filter_mask" AS_SELECTION_MASK = "import_layer_as_selection_mask" IMAGE_BACKGROUND_COLOUR_AND_TRANSPARENCY = "image_color" - # Brush property editing + _brush_property_editing = Group("Brush property editing") NEXT_FAVOURITE_PRESET = "next_favorite_preset" PREVIOUS_FAVOURITE_PRESET = "previous_favorite_preset" SWITCH_TO_PREVIOUS_PRESET = "previous_preset" @@ -281,10 +284,16 @@ class Action(Enum): BRUSH_SMOOTHING_BASIC = "set_simple_brush_smoothing" BRUSH_SMOOTHING_WEIGHTED = "set_weighted_brush_smoothing" BRUSH_SMOOTHING_STABILISER = "set_stabilizer_brush_smoothing" - # Opening menus + + _opening_menus = Group("Opening menus") CONFIGURE_KRITA = "options_configure" + SHOW_BRUSH_EDITOR = "show_brush_editor" CONFIGURE_TOOLBARS = "options_configure_toolbars" CONFIGURE_SHORTCUT_COMPOSER = "Configure Shortcut Composer" + MANAGE_RESOURCE_LIBRARIES = "manage_bundles" + MANAGE_RESOURCES = "manage_resources" + START_GMIC_QT = "QMic" + REAPPLY_THE_LAST_GMIC_FILTER = "QMicAgain" THEMES = "theme_menu" SHOW_DOCKERS = "view_toggledockers" RESET_ALL_SETTINGS = "reset_configurations" @@ -295,6 +304,7 @@ class Action(Enum): SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" SHOW_BRUSH_PRESETS = "show_brush_presets" + _tool_bars = Group("Tool bars") HIDE_MIRROR_X_LINE = "mirrorX-hideDecorations" LOCK_X_LINE = "mirrorX-lock" MOVE_TO_CANVAS_CENTRE_X = "mirrorX-moveToCenter" @@ -310,47 +320,28 @@ class Action(Enum): BRUSH_OPTION_SLIDER_2 = "brushslider2" BRUSH_OPTION_SLIDER_3 = "brushslider3" BRUSH_OPTION_SLIDER_4 = "brushslider4" - + SHOW_GLOBAL_SELECTION_MASK = "show-global-selection-mask" + WRAP_AROUND_MODE = "wrap_around_mode" MIRROR = "mirror_actions" WORKSPACES = "workspaces" - SHOW_BRUSH_EDITOR = "show_brush_editor" USE_PEN_PRESSURE = "disable_pressure" OPEN_FOREGROUND_COLOUR_SELECTOR = "chooseForegroundColor" OPEN_BACKGROUND_COLOUR_SELECTOR = "chooseBackgroundColor" + SWAP_FOREGROUND_AND_BACKGROUND_COLOURS = "toggle_fg_bg" + SET_FOREGROUND_AND_BACKGROUND_COLOURS_TO_BLACK_AND_WHITE = "reset_fg_bg" + + _canvas_operations = Group("Canvas operations") SCALE_IMAGE_TO_NEW_SIZE = "imagesize" RESIZE_CANVAS = "canvassize" SCALE_LAYER_TO_NEW_SIZE = "layersize" SCALE_ALL_LAYERS_TO_NEW_SIZE = "scaleAllLayers" SCALE = "selectionscale" - SHOW_KRITA_LOG_FOR_BUG_REPORTS = "buginfo" - SHOW_SYSTEM_INFORMATION_FOR_BUG_REPORTS = "sysinfo" - SHOW_CRASH_LOG_FOR_BUG_REPORTS = "crashlog" - CLONES_ARRAY = "clones_array" - SELECT_FROM_COLOUR_RANGE = "colorrange" - SELECT_OPAQUE_REPLACE = "selectopaque" - SELECT_OPAQUE_ADD = "selectopaque_add" - SELECT_OPAQUE_SUBTRACT = "selectopaque_subtract" - SELECT_OPAQUE_INTERSECT = "selectopaque_intersect" - CONVERT_IMAGE_COLOUR_SPACE = "imagecolorspaceconversion" - CONVERT_LAYER_COLOUR_SPACE = "layercolorspaceconversion" - EXPLORE_RESOURCES_CACHE_DATABASE = "dbexplorer" - IMAGE_SPLIT = "imagesplit" - MOVE_INTO_PREVIOUS_GROUP = "LayerGroupSwitcher/previous" - MOVE_INTO_NEXT_GROUP = "LayerGroupSwitcher/next" - SPLIT_LAYER = "layersplit" - EDIT_METADATA = "EditLayerMetaData" - GROW_SELECTION = "growselection" - SHRINK_SELECTION = "shrinkselection" - BORDER_SELECTION = "borderselection" - FEATHER_SELECTION = "featherselection" - SMOOTH = "smoothselection" - OFFSET_IMAGE = "offsetimage" - OFFSET_LAYER = "offsetlayer" - START_GMIC_QT = "QMic" - REAPPLY_THE_LAST_GMIC_FILTER = "QMicAgain" - MANAGE_RESOURCE_LIBRARIES = "manage_bundles" - MANAGE_RESOURCES = "manage_resources" + SEPARATE_IMAGE = "separate" + SHEAR_IMAGE = "shearimage" + SHEAR_LAYER = "shearlayer" + SHEAR_ALL_LAYERS = "shearAllLayers" + _image_rotation = Group("Image rotation") ROTATE_IMAGE = "rotateimage" ROTATE_IMAGE_90_TO_THE_RIGHT = "rotateImageCW90" ROTATE_IMAGE_180 = "rotateImage180" @@ -358,6 +349,7 @@ class Action(Enum): MIRROR_IMAGE_HORIZONTALLY = "mirrorImageHorizontal" MIRROR_IMAGE_VERTICALLY = "mirrorImageVertical" + _layer_rotation = Group("Layer rotation") ROTATE_LAYER = "rotatelayer" ROTATE_LAYER_180 = "rotateLayer180" ROTATE_LAYER_90_TO_THE_RIGHT = "rotateLayerCW90" @@ -367,13 +359,22 @@ class Action(Enum): ROTATE_ALL_LAYERS_90_TO_THE_LEFT = "rotateAllLayersCCW90" ROTATE_ALL_LAYERS_180 = "rotateAllLayers180" - SEPARATE_IMAGE = "separate" - SHEAR_IMAGE = "shearimage" - SHEAR_LAYER = "shearlayer" - SHEAR_ALL_LAYERS = "shearAllLayers" - + _tool_specific_actions = Group("Tool specific actions") + MOVE_UP = "movetool-move-up" + MOVE_DOWN = "movetool-move-down" + MOVE_LEFT = "movetool-move-left" + MOVE_RIGHT = "movetool-move-right" + MOVE_UP_MORE = "movetool-move-up-more" + MOVE_DOWN_MORE = "movetool-move-down-more" + MOVE_LEFT_MORE = "movetool-move-left-more" + MOVE_RIGHT_MORE = "movetool-move-right-more" + SHOW_COORDINATES = "movetool-show-coordinates" + CALLIGRAPHY = "KarbonCalligraphyTool" + CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" + CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" + CALLIGRAPHY_INCREASE_ANGLE = "calligraphy_increase_angle" + CALLIGRAPHY_DECREASE_ANGLE = "calligraphy_decrease_angle" WAVELET_DECOMPOSE = "waveletdecompose" - UNDO_POLYGON_SELECTION_POINTS = "undo_polygon_selection" CORNER_POINT = "pathpoint-corner" SMOOTH_POINT = "pathpoint-smooth" @@ -420,55 +421,7 @@ class Action(Enum): SUBTRACT = "object_subtract" SPLIT = "object_split" - MOVE_UP = "movetool-move-up" - MOVE_DOWN = "movetool-move-down" - MOVE_LEFT = "movetool-move-left" - MOVE_RIGHT = "movetool-move-right" - MOVE_UP_MORE = "movetool-move-up-more" - MOVE_DOWN_MORE = "movetool-move-down-more" - MOVE_LEFT_MORE = "movetool-move-left-more" - MOVE_RIGHT_MORE = "movetool-move-right-more" - SHOW_COORDINATES = "movetool-show-coordinates" - CALLIGRAPHY = "KarbonCalligraphyTool" - CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" - CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" - CALLIGRAPHY_INCREASE_ANGLE = "calligraphy_increase_angle" - CALLIGRAPHY_DECREASE_ANGLE = "calligraphy_decrease_angle" - ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" - REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" - HOLD_CONSTANT_VALUE_NO_INTERPOLATION = "interpolation_constant" - LINEAR_INTERPOLATION = "interpolation_linear" - BEZIER_CURVE_INTERPOLATION = "interpolation_bezier" - SHARP_INTERPOLATION_TANGENTS = "tangents_sharp" - SMOOTH_INTERPOLATION_TANGENTS = "tangents_smooth" - ZOOM_VIEW_TO_FIT_CHANNEL_RANGE = "zoom_to_fit_range" - ZOOM_VIEW_TO_FIT_CURVE = "zoom_to_fit_curve" - DROP_FRAMES = "drop_frames" - SHOW_GLOBAL_SELECTION_MASK = "show-global-selection-mask" - RENAME_CURRENT_LAYER = "RenameCurrentLayer" - LAYER_PROPERTIES = "layer_properties" - REMOVE_LAYER = "remove_layer" - MOVE_LAYER_OR_MASK_UP = "move_layer_up" - MOVE_LAYER_OR_MASK_DOWN = "move_layer_down" - SET_COPY_FROM = "set-copy-from" - TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" - CREATE_SNAPSHOT = "create_snapshot" - SWITCH_TO_SELECTED_SNAPSHOT = "switchto_snapshot" - REMOVE_SELECTED_SNAPSHOT = "remove_snapshot" - INSERT_COLUMN_LEFT = "insert_column_left" - INSERT_COLUMN_RIGHT = "insert_column_right" - INSERT_MULTIPLE_COLUMNS = "insert_multiple_columns" - REMOVE_COLUMN_AND_PULL = "remove_columns_and_pull" - REMOVE_COLUMN = "remove_columns" - INSERT_HOLD_COLUMN = "insert_hold_column" - INSERT_MULTIPLE_HOLD_COLUMNS = "insert_multiple_hold_columns" - REMOVE_HOLD_COLUMN = "remove_hold_column" - REMOVE_MULTIPLE_HOLD_COLUMNS = "remove_multiple_hold_columns" - MIRROR_COLUMNS = "mirror_columns" - CLEAR_CACHE = "clear_animation_cache" - COPY_COLUMNS = "copy_columns_to_clipboard" - CUT_COLUMNS = "cut_columns_to_clipboard" - PASTE_COLUMNS = "paste_columns_from_clipboard" + _animation = Group("Animation") CREATE_BLANK_FRAME = "add_blank_frame" CREATE_DUPLICATE_FRAME = "add_duplicate_frame" INSERT_KEYFRAME_LEFT = "insert_keyframe_left" @@ -501,6 +454,7 @@ class Action(Enum): NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" AUTO_FRAME_MODE = "auto_key" + _krita_window = Group("Krita window") DETACH_CANVAS = "view_detached_canvas" SHOW_DOCKER_TITLEBARS = "view_toggledockertitlebars" DOCKERS = "settings_dockers_menu" @@ -514,21 +468,18 @@ class Action(Enum): CLOSE = "file_close" SESSIONS = "file_sessions" SEARCH_ACTIONS = "command_bar_open" + + _krita_misc = Group("Krita misc") KRITA_HANDBOOK = "help_contents" REPORT_BUG = "help_report_bug" SWITCH_APPLICATION_LANGUAGE = "switch_application_language" ABOUT_KRITA = "help_about_app" ABOUT_K_D_E = "help_about_kde" - PREVIEW_CURRENT_LAYER_VISIBILITY = "Preview current layer visibility" - PREVIEW_PROJECTION_BELOW = "Preview projection below" - CYCLE_PAINTING_OPACITY = "Cycle painting opacity" - CYCLE_SELECTION_TOOLS = "Cycle selection tools" COLOUR_SPACE = "color_space" DOCUMENT_TOOLS = "document_tools" - EXPORT_LAYERS = "export_layers" FILTER_MANAGER = "filter_manager" - # Python Scripts + _python_scripts = Group("Python scripts") IMPORT_PYTHON_PLUGIN_FROM_FILE = "plugin_importer_file" IMPORT_PYTHON_PLUGIN_FROM_WEB = "plugin_importer_web" SCRIPTER = "python_scripter" @@ -543,12 +494,7 @@ class Action(Enum): EXECUTE_SCRIPT_8 = "execute_script_8" EXECUTE_SCRIPT_9 = "execute_script_9" EXECUTE_SCRIPT_10 = "execute_script_10" - TRANSFORM_TOOL_FREE = "Transform tool: free" - TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" - TRANSFORM_TOOL_WARP = "Transform tool: warp" - TRANSFORM_TOOL_CAGE = "Transform tool: cage" - TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" - TRANSFORM_TOOL_MESH = "Transform tool: mesh" + EXPORT_LAYERS = "export_layers" RECORD_TIMELAPSE = "recorder_record_toggle" EXPORT_TIMELAPSE = "recorder_export" HIDE_FILE_TOOLBAR = "mainToolBar" @@ -561,8 +507,71 @@ class Action(Enum): SHOW_COMMON_COLOURS = "show_common_colors" UPDATE_COMPOSITION = "update_composition" RENAME_COMPOSITION = "rename_composition" + TRANSFORM_TOOL_FREE = "Transform tool: free" + TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" + TRANSFORM_TOOL_WARP = "Transform tool: warp" + TRANSFORM_TOOL_CAGE = "Transform tool: cage" + TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" + TRANSFORM_TOOL_MESH = "Transform tool: mesh" RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" + _other = Group("Other") + SHOW_KRITA_LOG_FOR_BUG_REPORTS = "buginfo" + SHOW_SYSTEM_INFORMATION_FOR_BUG_REPORTS = "sysinfo" + SHOW_CRASH_LOG_FOR_BUG_REPORTS = "crashlog" + CLONES_ARRAY = "clones_array" + SELECT_FROM_COLOUR_RANGE = "colorrange" + SELECT_OPAQUE_REPLACE = "selectopaque" + SELECT_OPAQUE_ADD = "selectopaque_add" + SELECT_OPAQUE_SUBTRACT = "selectopaque_subtract" + SELECT_OPAQUE_INTERSECT = "selectopaque_intersect" + CONVERT_IMAGE_COLOUR_SPACE = "imagecolorspaceconversion" + CONVERT_LAYER_COLOUR_SPACE = "layercolorspaceconversion" + EXPLORE_RESOURCES_CACHE_DATABASE = "dbexplorer" + IMAGE_SPLIT = "imagesplit" + MOVE_INTO_PREVIOUS_GROUP = "LayerGroupSwitcher/previous" + MOVE_INTO_NEXT_GROUP = "LayerGroupSwitcher/next" + SPLIT_LAYER = "layersplit" + EDIT_METADATA = "EditLayerMetaData" + GROW_SELECTION = "growselection" + SHRINK_SELECTION = "shrinkselection" + BORDER_SELECTION = "borderselection" + FEATHER_SELECTION = "featherselection" + SMOOTH = "smoothselection" + OFFSET_IMAGE = "offsetimage" + OFFSET_LAYER = "offsetlayer" + # --- # + ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" + REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" + # --- # + HOLD_CONSTANT_VALUE_NO_INTERPOLATION = "interpolation_constant" + LINEAR_INTERPOLATION = "interpolation_linear" + BEZIER_CURVE_INTERPOLATION = "interpolation_bezier" + SHARP_INTERPOLATION_TANGENTS = "tangents_sharp" + SMOOTH_INTERPOLATION_TANGENTS = "tangents_smooth" + ZOOM_VIEW_TO_FIT_CHANNEL_RANGE = "zoom_to_fit_range" + ZOOM_VIEW_TO_FIT_CURVE = "zoom_to_fit_curve" + DROP_FRAMES = "drop_frames" + # --- # + SET_COPY_FROM = "set-copy-from" + CREATE_SNAPSHOT = "create_snapshot" + SWITCH_TO_SELECTED_SNAPSHOT = "switchto_snapshot" + REMOVE_SELECTED_SNAPSHOT = "remove_snapshot" + INSERT_COLUMN_LEFT = "insert_column_left" + INSERT_COLUMN_RIGHT = "insert_column_right" + INSERT_MULTIPLE_COLUMNS = "insert_multiple_columns" + REMOVE_COLUMN_AND_PULL = "remove_columns_and_pull" + REMOVE_COLUMN = "remove_columns" + INSERT_HOLD_COLUMN = "insert_hold_column" + INSERT_MULTIPLE_HOLD_COLUMNS = "insert_multiple_hold_columns" + REMOVE_HOLD_COLUMN = "remove_hold_column" + REMOVE_MULTIPLE_HOLD_COLUMNS = "remove_multiple_hold_columns" + MIRROR_COLUMNS = "mirror_columns" + CLEAR_CACHE = "clear_animation_cache" + COPY_COLUMNS = "copy_columns_to_clipboard" + CUT_COLUMNS = "cut_columns_to_clipboard" + PASTE_COLUMNS = "paste_columns_from_clipboard" + def activate(self): """Activate the action.""" try: From be0db0029bf1c2f8d458dc49528aff1eeecd7ca7 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 50/69] Organizes the action EnumGroup better --- shortcut_composer/api_krita/enums/action.py | 151 ++++++++------------ 1 file changed, 59 insertions(+), 92 deletions(-) diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py index 4ae49b6d..7b52a052 100644 --- a/shortcut_composer/api_krita/enums/action.py +++ b/shortcut_composer/api_krita/enums/action.py @@ -18,7 +18,6 @@ class Action(EnumGroup): NEW = "file_new" OPEN = "file_open" QUIT = "file_quit" - OPEN_RECENT = "file_open_recent" SAVE = "file_save" SAVE_AS = "file_save_as" UNDO = "edit_undo" @@ -33,13 +32,10 @@ class Action(EnumGroup): EXPORT = "file_export_file" EXPORT_ADVANCED = "file_export_advanced" DOCUMENT_INFORMATION = "file_documentinfo" - - _saving = Group("Saving") SAVE_INCREMENTAL_VERSION = "save_incremental_version" SAVE_INCREMENTAL_BACKUP = "save_incremental_backup" CREATE_TEMPLATE_FROM_IMAGE = "create_template" CREATE_COPY_FROM_CURRENT_IMAGE = "create_copy" - OPEN_RESOURCES_FOLDER = "open_resources_directory" _canvas_navigation = Group("Canvas navigation") ROTATE_CANVAS_RIGHT = "rotate_canvas_right" @@ -171,38 +167,37 @@ class Action(EnumGroup): STROKE_SELECTION = "stroke_selection" COPY_SELECTION_TO_NEW_LAYER = "copy_selection_to_new_layer" CUT_SELECTION_TO_NEW_LAYER = "cut_selection_to_new_layer" + SELECT_FROM_COLOUR_RANGE = "colorrange" + SELECT_OPAQUE_REPLACE = "selectopaque" + SELECT_OPAQUE_ADD = "selectopaque_add" + SELECT_OPAQUE_SUBTRACT = "selectopaque_subtract" + SELECT_OPAQUE_INTERSECT = "selectopaque_intersect" + GROW_SELECTION = "growselection" + SHRINK_SELECTION = "shrinkselection" + BORDER_SELECTION = "borderselection" + FEATHER_SELECTION = "featherselection" + SMOOTH = "smoothselection" - _edit_layers = Group("Edit layers") - SAVE_GROUP_LAYERS = "save_groups_as_images" - CONVERT_GROUP_TO_ANIMATED_LAYER = "convert_group_to_animated" - TRIM_TO_CURRENT_LAYER = "resizeimagetolayer" - TRIM_TO_IMAGE_SIZE = "trim_to_image" - LAYER_STYLE = "layer_style" - COPY_LAYER_STYLE = "copy_layer_style" - PASTE_LAYER_STYLE = "paste_layer_style" - MIRROR_LAYER_HORIZONTALLY = "mirrorNodeX" - MIRROR_LAYER_VERTICALLY = "mirrorNodeY" - MIRROR_ALL_LAYERS_HORIZONTALLY = "mirrorAllNodesX" - MIRROR_ALL_LAYERS_VERTICALLY = "mirrorAllNodesY" - ACTIVATE_PREVIOUSLY_SELECTED_LAYER = "switchToPreviouslyActiveNode" - SAVE_LAYER_MASK = "save_node_as_image" - SAVE_VECTOR_LAYER_AS_SVG = "save_vector_node_to_svg" - DUPLICATE_LAYER_OR_MASK = "duplicatelayer" + _layer_stack = Group("Layer stack") + LAYER_PROPERTIES = "layer_properties" COPY_LAYER = "copy_layer_clipboard" CUT_LAYER = "cut_layer_clipboard" PASTE_LAYER = "paste_layer_from_clipboard" + DUPLICATE_LAYER_OR_MASK = "duplicatelayer" + RENAME_CURRENT_LAYER = "RenameCurrentLayer" + REMOVE_LAYER = "remove_layer" QUICK_GROUP = "create_quick_group" QUICK_CLIPPING_GROUP = "create_quick_clipping_group" QUICK_UNGROUP = "quick_ungroup" + MIRROR_LAYER_HORIZONTALLY = "mirrorNodeX" + MIRROR_LAYER_VERTICALLY = "mirrorNodeY" + MIRROR_ALL_LAYERS_HORIZONTALLY = "mirrorAllNodesX" + MIRROR_ALL_LAYERS_VERTICALLY = "mirrorAllNodesY" PIN_TO_TIMELINE = "pin_to_timeline" - - _layer_stack = Group("Layer stack") - LAYER_PROPERTIES = "layer_properties" - RENAME_CURRENT_LAYER = "RenameCurrentLayer" - REMOVE_LAYER = "remove_layer" + TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" + SAVE_GROUP_LAYERS = "save_groups_as_images" MOVE_LAYER_OR_MASK_UP = "move_layer_up" MOVE_LAYER_OR_MASK_DOWN = "move_layer_down" - TOGGLE_LAYER_SOLOING = "toggle_layer_soloing" SELECT_ALL_LAYERS = "select_all_layers" SELECT_VISIBLE_LAYERS = "select_visible_layers" SELECT_LOCKED_LAYERS = "select_locked_layers" @@ -214,6 +209,16 @@ class Action(EnumGroup): ACTIVATE_PREVIOUS_LAYER = "activatePreviousLayer" ACTIVATE_PREVIOUS_SIBLING_LAYER = \ "activatePreviousSiblingLayer" + CONVERT_GROUP_TO_ANIMATED_LAYER = "convert_group_to_animated" + TRIM_TO_CURRENT_LAYER = "resizeimagetolayer" + TRIM_TO_IMAGE_SIZE = "trim_to_image" + LAYER_STYLE = "layer_style" + COPY_LAYER_STYLE = "copy_layer_style" + PASTE_LAYER_STYLE = "paste_layer_style" + ACTIVATE_PREVIOUSLY_SELECTED_LAYER = "switchToPreviouslyActiveNode" + SAVE_LAYER_MASK = "save_node_as_image" + SAVE_VECTOR_LAYER_AS_SVG = "save_vector_node_to_svg" + TO_FILE_LAYER = "convert_to_file_layer" _layer_creation = Group("Layer creation") ADD_PAINT_LAYER = "add_new_paint_layer" @@ -233,8 +238,11 @@ class Action(EnumGroup): CONVERT_TO_FILTER_MASK = "convert_to_filter_mask" CONVERT_TO_TRANSPARENCY_MASK = "convert_to_transparency_mask" CONVERT_TO_ANIMATED_LAYER = "convert_to_animated" - TO_FILE_LAYER = "convert_to_file_layer" IMPORT_LAYER = "import_layer_from_file" + AS_PAINT_LAYER = "import_layer_as_paint_layer" + AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" + AS_FILTER_MASK = "import_layer_as_filter_mask" + AS_SELECTION_MASK = "import_layer_as_selection_mask" NEW_LAYER_FROM_VISIBLE = "new_from_visible" _layer_handling = Group("Layer handling") @@ -245,14 +253,6 @@ class Action(EnumGroup): TOGGLE_LAYER_ALPHA = "toggle_layer_alpha_lock" ALPHA_INTO_MASK = "split_alpha_into_mask" WRITE_AS_ALPHA = "split_alpha_write" - SAVE_MERGED = "split_alpha_save_merged" - - _importing_layers = Group("Importing layers") - AS_PAINT_LAYER = "import_layer_as_paint_layer" - AS_TRANSPARENCY_MASK = "import_layer_as_transparency_mask" - AS_FILTER_MASK = "import_layer_as_filter_mask" - AS_SELECTION_MASK = "import_layer_as_selection_mask" - IMAGE_BACKGROUND_COLOUR_AND_TRANSPARENCY = "image_color" _brush_property_editing = Group("Brush property editing") NEXT_FAVOURITE_PRESET = "next_favorite_preset" @@ -303,6 +303,7 @@ class Action(EnumGroup): PROPERTIES = "image_properties" SHOW_SNAP_OPTIONS_POPUP = "show_snap_options_popup" SHOW_BRUSH_PRESETS = "show_brush_presets" + OPEN_RESOURCES_FOLDER = "open_resources_directory" _tool_bars = Group("Tool bars") HIDE_MIRROR_X_LINE = "mirrorX-hideDecorations" @@ -330,26 +331,26 @@ class Action(EnumGroup): SWAP_FOREGROUND_AND_BACKGROUND_COLOURS = "toggle_fg_bg" SET_FOREGROUND_AND_BACKGROUND_COLOURS_TO_BLACK_AND_WHITE = "reset_fg_bg" - _canvas_operations = Group("Canvas operations") + _image_operations = Group("Image operations") SCALE_IMAGE_TO_NEW_SIZE = "imagesize" RESIZE_CANVAS = "canvassize" - SCALE_LAYER_TO_NEW_SIZE = "layersize" - SCALE_ALL_LAYERS_TO_NEW_SIZE = "scaleAllLayers" - SCALE = "selectionscale" - SEPARATE_IMAGE = "separate" - SHEAR_IMAGE = "shearimage" - SHEAR_LAYER = "shearlayer" - SHEAR_ALL_LAYERS = "shearAllLayers" - - _image_rotation = Group("Image rotation") ROTATE_IMAGE = "rotateimage" ROTATE_IMAGE_90_TO_THE_RIGHT = "rotateImageCW90" ROTATE_IMAGE_180 = "rotateImage180" ROTATE_IMAGE_90_TO_THE_LEFT = "rotateImageCCW90" MIRROR_IMAGE_HORIZONTALLY = "mirrorImageHorizontal" MIRROR_IMAGE_VERTICALLY = "mirrorImageVertical" + IMAGE_BACKGROUND_COLOUR_AND_TRANSPARENCY = "image_color" + SEPARATE_IMAGE = "separate" + SHEAR_IMAGE = "shearimage" + OFFSET_IMAGE = "offsetimage" - _layer_rotation = Group("Layer rotation") + _layer_operations = Group("Layer operations") + SCALE_LAYER_TO_NEW_SIZE = "layersize" + SCALE_ALL_LAYERS_TO_NEW_SIZE = "scaleAllLayers" + SHEAR_LAYER = "shearlayer" + SHEAR_ALL_LAYERS = "shearAllLayers" + SPLIT_LAYER = "layersplit" ROTATE_LAYER = "rotatelayer" ROTATE_LAYER_180 = "rotateLayer180" ROTATE_LAYER_90_TO_THE_RIGHT = "rotateLayerCW90" @@ -358,6 +359,7 @@ class Action(EnumGroup): ROTATE_ALL_LAYERS_90_TO_THE_RIGHT = "rotateAllLayersCW90" ROTATE_ALL_LAYERS_90_TO_THE_LEFT = "rotateAllLayersCCW90" ROTATE_ALL_LAYERS_180 = "rotateAllLayers180" + OFFSET_LAYER = "offsetlayer" _tool_specific_actions = Group("Tool specific actions") MOVE_UP = "movetool-move-up" @@ -369,6 +371,7 @@ class Action(EnumGroup): MOVE_LEFT_MORE = "movetool-move-left-more" MOVE_RIGHT_MORE = "movetool-move-right-more" SHOW_COORDINATES = "movetool-show-coordinates" + SCALE = "selectionscale" CALLIGRAPHY = "KarbonCalligraphyTool" CALLIGRAPHY_INCREASE_WIDTH = "calligraphy_increase_width" CALLIGRAPHY_DECREASE_WIDTH = "calligraphy_decrease_width" @@ -453,6 +456,16 @@ class Action(EnumGroup): PREVIOUS_UNFILTERED_KEYFRAME = "previous_unfiltered_keyframe" NEXT_UNFILTERED_KEYFRAME = "next_unfiltered_keyframe" AUTO_FRAME_MODE = "auto_key" + ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" + REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" + HOLD_CONSTANT_VALUE_NO_INTERPOLATION = "interpolation_constant" + LINEAR_INTERPOLATION = "interpolation_linear" + BEZIER_CURVE_INTERPOLATION = "interpolation_bezier" + SHARP_INTERPOLATION_TANGENTS = "tangents_sharp" + SMOOTH_INTERPOLATION_TANGENTS = "tangents_smooth" + ZOOM_VIEW_TO_FIT_CHANNEL_RANGE = "zoom_to_fit_range" + ZOOM_VIEW_TO_FIT_CURVE = "zoom_to_fit_curve" + DROP_FRAMES = "drop_frames" _krita_window = Group("Krita window") DETACH_CANVAS = "view_detached_canvas" @@ -469,16 +482,6 @@ class Action(EnumGroup): SESSIONS = "file_sessions" SEARCH_ACTIONS = "command_bar_open" - _krita_misc = Group("Krita misc") - KRITA_HANDBOOK = "help_contents" - REPORT_BUG = "help_report_bug" - SWITCH_APPLICATION_LANGUAGE = "switch_application_language" - ABOUT_KRITA = "help_about_app" - ABOUT_K_D_E = "help_about_kde" - COLOUR_SPACE = "color_space" - DOCUMENT_TOOLS = "document_tools" - FILTER_MANAGER = "filter_manager" - _python_scripts = Group("Python scripts") IMPORT_PYTHON_PLUGIN_FROM_FILE = "plugin_importer_file" IMPORT_PYTHON_PLUGIN_FROM_WEB = "plugin_importer_web" @@ -507,51 +510,15 @@ class Action(EnumGroup): SHOW_COMMON_COLOURS = "show_common_colors" UPDATE_COMPOSITION = "update_composition" RENAME_COMPOSITION = "rename_composition" - TRANSFORM_TOOL_FREE = "Transform tool: free" - TRANSFORM_TOOL_PERSPECTIVE = "Transform tool: perspective" - TRANSFORM_TOOL_WARP = "Transform tool: warp" - TRANSFORM_TOOL_CAGE = "Transform tool: cage" - TRANSFORM_TOOL_LIQUIFY = "Transform tool: liquify" - TRANSFORM_TOOL_MESH = "Transform tool: mesh" RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" _other = Group("Other") - SHOW_KRITA_LOG_FOR_BUG_REPORTS = "buginfo" - SHOW_SYSTEM_INFORMATION_FOR_BUG_REPORTS = "sysinfo" - SHOW_CRASH_LOG_FOR_BUG_REPORTS = "crashlog" - CLONES_ARRAY = "clones_array" - SELECT_FROM_COLOUR_RANGE = "colorrange" - SELECT_OPAQUE_REPLACE = "selectopaque" - SELECT_OPAQUE_ADD = "selectopaque_add" - SELECT_OPAQUE_SUBTRACT = "selectopaque_subtract" - SELECT_OPAQUE_INTERSECT = "selectopaque_intersect" CONVERT_IMAGE_COLOUR_SPACE = "imagecolorspaceconversion" CONVERT_LAYER_COLOUR_SPACE = "layercolorspaceconversion" EXPLORE_RESOURCES_CACHE_DATABASE = "dbexplorer" IMAGE_SPLIT = "imagesplit" MOVE_INTO_PREVIOUS_GROUP = "LayerGroupSwitcher/previous" MOVE_INTO_NEXT_GROUP = "LayerGroupSwitcher/next" - SPLIT_LAYER = "layersplit" - EDIT_METADATA = "EditLayerMetaData" - GROW_SELECTION = "growselection" - SHRINK_SELECTION = "shrinkselection" - BORDER_SELECTION = "borderselection" - FEATHER_SELECTION = "featherselection" - SMOOTH = "smoothselection" - OFFSET_IMAGE = "offsetimage" - OFFSET_LAYER = "offsetlayer" - # --- # - ADD_SCALAR_KEYFRAMES = "add_scalar_keyframes" - REMOVE_SCALAR_KEYFRAME = "remove_scalar_keyframe" - # --- # - HOLD_CONSTANT_VALUE_NO_INTERPOLATION = "interpolation_constant" - LINEAR_INTERPOLATION = "interpolation_linear" - BEZIER_CURVE_INTERPOLATION = "interpolation_bezier" - SHARP_INTERPOLATION_TANGENTS = "tangents_sharp" - SMOOTH_INTERPOLATION_TANGENTS = "tangents_smooth" - ZOOM_VIEW_TO_FIT_CHANNEL_RANGE = "zoom_to_fit_range" - ZOOM_VIEW_TO_FIT_CURVE = "zoom_to_fit_curve" - DROP_FRAMES = "drop_frames" # --- # SET_COPY_FROM = "set-copy-from" CREATE_SNAPSHOT = "create_snapshot" From 61a51e627f5b55f9d0f3034ea2062affd74897f9 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 51/69] Add toggles to action enum --- shortcut_composer/api_krita/enums/action.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/shortcut_composer/api_krita/enums/action.py b/shortcut_composer/api_krita/enums/action.py index 7b52a052..6402c87f 100644 --- a/shortcut_composer/api_krita/enums/action.py +++ b/shortcut_composer/api_krita/enums/action.py @@ -512,6 +512,21 @@ class Action(EnumGroup): RENAME_COMPOSITION = "rename_composition" RELOAD_SHORTCUT_COMPOSER = "Reload Shortcut Composer" + _toggles = Group("Toggles") + ERASER = "erase_action" + PRESERVE_ALPHA = "preserve_alpha" + MIRROR_CANVAS = "mirror_canvas" + SOFT_PROOFING = "softProof" + ISOLATE_LAYER = "isolate_active_layer" + VIEW_REFERENCE_IMAGES = "view_toggle_reference_images" + VIEW_ASSISTANTS = "view_toggle_painting_assistants" + VIEW_ASSISTANTS_PREVIEWS = "view_toggle_assistant_previews" + VIEW_GRID = "view_grid" + VIEW_RULER = "view_ruler" + VIEW_ONION_SKIN = "toggle_onion_skin" + SNAP_ASSISTANT = "toggle_assistant" + SNAP_TO_GRID = "view_snap_to_grid" + _other = Group("Other") CONVERT_IMAGE_COLOUR_SPACE = "imagecolorspaceconversion" CONVERT_LAYER_COLOUR_SPACE = "layercolorspaceconversion" @@ -519,7 +534,6 @@ class Action(EnumGroup): IMAGE_SPLIT = "imagesplit" MOVE_INTO_PREVIOUS_GROUP = "LayerGroupSwitcher/previous" MOVE_INTO_NEXT_GROUP = "LayerGroupSwitcher/next" - # --- # SET_COPY_FROM = "set-copy-from" CREATE_SNAPSHOT = "create_snapshot" SWITCH_TO_SELECTED_SNAPSHOT = "switchto_snapshot" From fca282c4195dba1610643df6a6804d5f5a34c27e Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 52/69] Define pies for triggering krita actions --- shortcut_composer/actions.action | 14 ++++++++-- shortcut_composer/actions.py | 46 ++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/shortcut_composer/actions.action b/shortcut_composer/actions.action index 48b0bc74..4ac0d54f 100755 --- a/shortcut_composer/actions.action +++ b/shortcut_composer/actions.action @@ -90,9 +90,19 @@ krita_tool_grid - + 1 - + palette-edit + + + + 1 + palette-edit + + + + 1 + palette-edit diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index ef7d7807..16cd9f63 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -196,14 +196,50 @@ def create_actions() -> List[templates.RawInstructions]: return [ # Use pie menu to pick one of the actions templates.PieMenu( - name="Activate krita action", + name="Activate krita action (red)", controller=controllers.ActionController(), values=[ - Action.BLUR, - Action.SAVE, - Action.UNDO, - Action.TOGGLE_LAYER_ALPHA + Action.HORIZONTAL_MIRROR_TOOL, + Action.WRAP_AROUND_MODE, + Action.ERASER, + Action.PRESERVE_ALPHA, + Action.MIRROR_CANVAS, ], + background_color=QColor(140, 90, 90, 190), + active_color=QColor(200, 70, 70), + pie_opacity=50, + ), + + # Use pie menu to pick one of the actions + templates.PieMenu( + name="Activate krita action (green)", + controller=controllers.ActionController(), + values=[ + Action.CREATE_BLANK_FRAME, + Action.CREATE_DUPLICATE_FRAME, + Action.REMOVE_KEYFRAME, + Action.VIEW_ONION_SKIN, + Action.RECORD_TIMELAPSE, + ], + background_color=QColor(80, 130, 80, 190), + active_color=QColor(70, 200, 70), + pie_opacity=50, + ), + + # Use pie menu to pick one of the actions + templates.PieMenu( + name="Activate krita action (blue)", + controller=controllers.ActionController(), + values=[ + Action.ADD_PAINT_LAYER, + Action.TOGGLE_LAYER_VISIBILITY, + Action.TOGGLE_LAYER_ALPHA_INHERITANCE, + Action.TOGGLE_LAYER_ALPHA, + Action.TOGGLE_LAYER_LOCK, + ], + background_color=QColor(90, 90, 140, 190), + active_color=QColor(110, 160, 235), + pie_opacity=50, ), # Use pie menu to pick one of the brush blending modes. From 2c04da0ff77778b038219f49c57be39b1456080c Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 53/69] WIP: LabelWidget borders, same gray background for all labels --- .../templates/pie_menu_utils/label_widget.py | 54 +++++++++++++++---- .../label_widget_impl/image_label_widget.py | 33 ++---------- .../label_widget_impl/text_label_widget.py | 26 +-------- .../templates/pie_menu_utils/pie_style.py | 18 +++---- 4 files changed, 61 insertions(+), 70 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 53ec46c9..13828ffb 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -5,9 +5,9 @@ from PyQt5.QtCore import Qt, QMimeData, QEvent from PyQt5.QtWidgets import QWidget -from PyQt5.QtGui import QDrag, QPixmap, QMouseEvent +from PyQt5.QtGui import QDrag, QPixmap, QMouseEvent, QPaintEvent -from api_krita.pyqt import PixmapTransform, BaseWidget +from api_krita.pyqt import Painter, PixmapTransform, BaseWidget from .pie_style import PieStyle from .label import Label @@ -51,6 +51,47 @@ def add_instruction(self, instruction: WidgetInstructions): """Add additional logic to do on entering and leaving widget.""" self._instructions.append(instruction) + def paintEvent(self, event: QPaintEvent) -> None: + with Painter(self, event) as painter: + self.paint(painter) + + def paint(self, painter: Painter): + """ + Paint the entire widget using the Painter wrapper. + + Paint a background behind a label its border, and image itself. + """ + painter.paint_wheel( + center=self.center, + outer_radius=self.icon_radius-1, + color=self._style.icon_color) + + painter.paint_wheel( + center=self.center, + outer_radius=self.icon_radius, + color=self._style.border_color, + thickness=self._thickness) + + if not self.enabled: + painter.paint_wheel( + center=self.center, + outer_radius=self.icon_radius, + color=self._style.active_color_dark, + thickness=self._thickness//3*2) + + if self.forced or (self._hovered and self.draggable): + painter.paint_wheel( + center=self.center, + outer_radius=self.icon_radius, + color=self._border_active_color, + thickness=self._thickness//3*2) + + @property + def _thickness(self): + if self._is_unscaled: + return self._style.unscaled_border_thickness + return self._style.border_thickness + @property def draggable(self) -> bool: """Return whether the label can be dragged.""" @@ -129,15 +170,10 @@ def leaveEvent(self, e: QEvent) -> None: self.repaint() @property - def _border_color(self): - """Return border color which differs when enabled or hovered.""" + def _border_active_color(self): if self.forced: return self._style.active_color - if not self.enabled: - return self._style.active_color_dark - if self._hovered and self.draggable: - return self._style.active_color - return self._style.border_color + return self._style.active_color @property def icon_radius(self): diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py index adeb00e6..33acfcb9 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py @@ -1,10 +1,7 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -from PyQt5.QtGui import ( - QPixmap, - QPaintEvent, -) +from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QWidget from api_krita.pyqt import Painter, PixmapTransform @@ -26,29 +23,9 @@ def __init__( super().__init__(label, style, parent, is_unscaled) self.ready_image = self._prepare_image() - def paintEvent(self, event: QPaintEvent) -> None: - """ - Paint the entire widget using the Painter wrapper. - - Paint a background behind a label its border, and image itself. - """ - with Painter(self, event) as painter: - painter.paint_wheel( - center=self.center, - outer_radius=self.icon_radius, - color=self._style.icon_color) - - if self._is_unscaled: - thickness = self._style.unscaled_border_thickness - else: - thickness = self._style.border_thickness - painter.paint_wheel( - center=self.center, - outer_radius=( - self.icon_radius-thickness//3), - color=self._border_color, - thickness=thickness) - painter.paint_pixmap(self.center, self.ready_image) + def paint(self, painter: Painter): + super().paint(painter) + painter.paint_pixmap(self.center, self.ready_image) def _prepare_image(self) -> QPixmap: """Return image after scaling and reshaping it to circle.""" @@ -60,4 +37,4 @@ def _prepare_image(self) -> QPixmap: rounded_image = PixmapTransform.make_pixmap_round(to_display) return PixmapTransform.scale_pixmap( pixmap=rounded_image, - size_px=round(self.icon_radius*1.725)) + size_px=round((self.icon_radius-self._thickness)*2)) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py index 01431c14..5fa59a04 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py @@ -2,15 +2,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later from PyQt5.QtCore import Qt -from PyQt5.QtGui import ( - QFont, - QColor, - QFontDatabase, - QPaintEvent, -) +from PyQt5.QtGui import QFont, QColor, QFontDatabase from PyQt5.QtWidgets import QLabel, QWidget -from api_krita.pyqt import Painter, Text +from api_krita.pyqt import Text from ..pie_style import PieStyle from ..label import Label from ..label_widget import LabelWidget @@ -29,23 +24,6 @@ def __init__( super().__init__(label, style, parent, is_unscaled) self._pyqt_label = self._create_pyqt_label() - def paintEvent(self, event: QPaintEvent) -> None: - """ - Paint the entire widget using the Painter wrapper. - - Paint a background behind a label and its border. - """ - with Painter(self, event) as painter: - painter.paint_wheel( - center=self.center, - outer_radius=self.icon_radius, - color=self._style.icon_color) - painter.paint_wheel( - center=self.center, - outer_radius=self.icon_radius, - color=self._border_color, - thickness=self._style.border_thickness) - def _create_pyqt_label(self) -> QLabel: """Create and show a new Qt5 label. Does not need redrawing.""" to_display = self.label.display_value diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 793b3a5e..f31bc419 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -95,12 +95,12 @@ def widget_radius(self) -> int: @property def border_thickness(self): """Thickness of border around icons.""" - return round(self.icon_radius*0.09) + return round(self.icon_radius*0.12) @property def unscaled_border_thickness(self): """Thickness of border of the pie.""" - return round(self.unscaled_icon_radius*0.09) + return round(self.unscaled_icon_radius*0.12) @property def area_thickness(self): @@ -168,23 +168,23 @@ def active_color_dark(self): @property def icon_color(self): """Color of icon background.""" - color = copy(self.background_color) - color.setAlpha(255) - return color + if Krita.is_light_theme_active: + return QColor(220, 220, 220) + return QColor(75, 75, 75) @property def border_color(self): """Color of icon borders.""" return QColor( - min(self.icon_color.red()+15, 255), - min(self.icon_color.green()+15, 255), - min(self.icon_color.blue()+15, 255), + min(self.background_color.red()+15, 255), + min(self.background_color.green()+15, 255), + min(self.background_color.blue()+15, 255), 255) @property def pie_border_color(self): """Color of dark border of the pie.""" - color = self.icon_color + color = self.background_color color = QColor(color.red()-5, color.green()-5, color.blue()-5, 120) return color From 79f84ec44e76d40e630e4eba3132b83ea94569f4 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 54/69] Thin label borders --- shortcut_composer/templates/pie_menu_utils/label_widget.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 13828ffb..baf1e841 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -63,14 +63,15 @@ def paint(self, painter: Painter): """ painter.paint_wheel( center=self.center, - outer_radius=self.icon_radius-1, + outer_radius=self.icon_radius-self._thickness, color=self._style.icon_color) + border_size = self._thickness//3 painter.paint_wheel( center=self.center, - outer_radius=self.icon_radius, + outer_radius=self.icon_radius-self._thickness+border_size, color=self._style.border_color, - thickness=self._thickness) + thickness=border_size) if not self.enabled: painter.paint_wheel( From bfeb1382860142f0bdee68ff059b6b7ae7a76ec0 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 55/69] Thin border which switches side of pie when activate --- shortcut_composer/actions.py | 10 +++----- .../templates/pie_menu_utils/label_widget.py | 4 +++ .../templates/pie_menu_utils/pie_style.py | 8 ------ .../pie_widget_utils/pie_painter.py | 25 +++++++++++-------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index 16cd9f63..a568c58f 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -194,6 +194,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ pie_radius_scale=0.9 ), + # Use pie menu to pick one of the actions templates.PieMenu( name="Activate krita action (red)", @@ -205,9 +206,8 @@ def create_actions() -> List[templates.RawInstructions]: return [ Action.PRESERVE_ALPHA, Action.MIRROR_CANVAS, ], - background_color=QColor(140, 90, 90, 190), + background_color=QColor(95, 65, 65, 190), active_color=QColor(200, 70, 70), - pie_opacity=50, ), # Use pie menu to pick one of the actions @@ -221,9 +221,8 @@ def create_actions() -> List[templates.RawInstructions]: return [ Action.VIEW_ONION_SKIN, Action.RECORD_TIMELAPSE, ], - background_color=QColor(80, 130, 80, 190), + background_color=QColor(65, 95, 65, 190), active_color=QColor(70, 200, 70), - pie_opacity=50, ), # Use pie menu to pick one of the actions @@ -237,9 +236,8 @@ def create_actions() -> List[templates.RawInstructions]: return [ Action.TOGGLE_LAYER_ALPHA, Action.TOGGLE_LAYER_LOCK, ], - background_color=QColor(90, 90, 140, 190), + background_color=QColor(70, 70, 105, 190), active_color=QColor(110, 160, 235), - pie_opacity=50, ), # Use pie menu to pick one of the brush blending modes. diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index baf1e841..96d7822a 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -61,11 +61,13 @@ def paint(self, painter: Painter): Paint a background behind a label its border, and image itself. """ + # label background painter.paint_wheel( center=self.center, outer_radius=self.icon_radius-self._thickness, color=self._style.icon_color) + # label thin border border_size = self._thickness//3 painter.paint_wheel( center=self.center, @@ -73,6 +75,7 @@ def paint(self, painter: Painter): color=self._style.border_color, thickness=border_size) + # label thick border when label when disabled if not self.enabled: painter.paint_wheel( center=self.center, @@ -80,6 +83,7 @@ def paint(self, painter: Painter): color=self._style.active_color_dark, thickness=self._thickness//3*2) + # label thick border when hovered (or it is forced) if self.forced or (self._hovered and self.draggable): painter.paint_wheel( center=self.center, diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index f31bc419..58b9e610 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -4,7 +4,6 @@ import math import platform from typing import TYPE_CHECKING -from copy import copy from PyQt5.QtGui import QColor @@ -181,13 +180,6 @@ def border_color(self): min(self.background_color.blue()+15, 255), 255) - @property - def pie_border_color(self): - """Color of dark border of the pie.""" - color = self.background_color - color = QColor(color.red()-5, color.green()-5, color.blue()-5, 120) - return color - @property def font_multiplier(self): """Multiplier to apply to the font depending on the used OS.""" diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index d687179d..b4aa2166 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -25,7 +25,6 @@ def __post_init__(self): self._paint_deadzone_indicator() self._paint_base_wheel() self._paint_active_pie() - self._paint_base_border() @property def _center(self) -> QPoint: @@ -58,26 +57,20 @@ def _paint_base_wheel(self) -> None: outer_radius=self.style.widget_radius, color=QColor(128, 128, 128, 1)) + # base wheel self.painter.paint_wheel( center=self._center, outer_radius=self.style.no_border_radius, color=self.style.background_color, thickness=self.style.area_thickness) - def _paint_base_border(self) -> None: - """Paint a border on the inner edge of base circle.""" + # base wheel decorator + thickness = self.style.border_thickness self.painter.paint_wheel( center=self._center, outer_radius=self.style.inner_edge_radius, color=self.style.border_color, - thickness=self.style.border_thickness) - - thickness = self.style.border_thickness - self.painter.paint_wheel( - center=self._center, - outer_radius=self.style.inner_edge_radius + thickness, - color=self.style.pie_border_color, - thickness=thickness) + thickness=thickness//3) def _paint_active_pie(self) -> None: """Paint a pie behind a label which is active or during animation.""" @@ -89,6 +82,7 @@ def _paint_active_pie(self) -> None: 0.15 * label.activation_progress.value * self.style.area_thickness) + # active pie self.painter.paint_pie( center=self._center, outer_radius=self.style.no_border_radius + thickness_addition, @@ -97,6 +91,15 @@ def _paint_active_pie(self) -> None: color=self._pick_pie_color(label), thickness=self.style.area_thickness + thickness_addition) + # active pie border + self.painter.paint_pie( + center=self._center, + outer_radius=self.style.no_border_radius + thickness_addition, + angle=label.angle, + span=360//len(self.labels), + color=self.style.border_color, + thickness=self.style.border_thickness//3) + def _pick_pie_color(self, label: Label) -> QColor: """Pick color of pie based on widget mode and animation progress.""" return self._overlay_colors( From 8018c7185131f27d9a869967c8e585810b5980c0 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 56/69] Use theme color for icon background --- shortcut_composer/templates/pie_menu_utils/label_widget.py | 3 ++- .../pie_menu_utils/label_widget_impl/text_label_widget.py | 4 +++- shortcut_composer/templates/pie_menu_utils/pie_style.py | 7 ------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 96d7822a..4315342f 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -7,6 +7,7 @@ from PyQt5.QtWidgets import QWidget from PyQt5.QtGui import QDrag, QPixmap, QMouseEvent, QPaintEvent +from api_krita import Krita from api_krita.pyqt import Painter, PixmapTransform, BaseWidget from .pie_style import PieStyle from .label import Label @@ -65,7 +66,7 @@ def paint(self, painter: Painter): painter.paint_wheel( center=self.center, outer_radius=self.icon_radius-self._thickness, - color=self._style.icon_color) + color=Krita.get_main_color_from_theme()) # label thin border border_size = self._thickness//3 diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py index 5fa59a04..54063339 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/text_label_widget.py @@ -5,6 +5,7 @@ from PyQt5.QtGui import QFont, QColor, QFontDatabase from PyQt5.QtWidgets import QLabel, QWidget +from api_krita import Krita from api_krita.pyqt import Text from ..pie_style import PieStyle from ..label import Label @@ -41,7 +42,8 @@ def _create_pyqt_label(self) -> QLabel: label.move(self.center.x()-heigth, self.center.y()-heigth//2) label.setStyleSheet(f''' - background-color:rgba({self._color_to_str(self._style.icon_color)}); + background-color:rgba({self._color_to_str( + Krita.get_main_color_from_theme())}); color:rgba({self._color_to_str(to_display.color)}); ''') diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 58b9e610..a16cd9fb 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -164,13 +164,6 @@ def active_color_dark(self): round(self.active_color.green()*0.8), round(self.active_color.blue()*0.8)) - @property - def icon_color(self): - """Color of icon background.""" - if Krita.is_light_theme_active: - return QColor(220, 220, 220) - return QColor(75, 75, 75) - @property def border_color(self): """Color of icon borders.""" From a9afac8da3afc452168e6353e8a04a1940f8e395 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 57/69] Refactor border thickness attributes --- .../templates/pie_menu_utils/label_widget.py | 20 ++++++++++++------- .../label_widget_impl/image_label_widget.py | 5 ++++- .../templates/pie_menu_utils/pie_style.py | 9 ++------- .../pie_widget_utils/pie_painter.py | 13 ++++++------ 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 4315342f..0245a3d3 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -65,16 +65,18 @@ def paint(self, painter: Painter): # label background painter.paint_wheel( center=self.center, - outer_radius=self.icon_radius-self._thickness, + outer_radius=( + self.icon_radius + - self._thin_border_thickness + - self._active_indicator_thickness), color=Krita.get_main_color_from_theme()) # label thin border - border_size = self._thickness//3 painter.paint_wheel( center=self.center, - outer_radius=self.icon_radius-self._thickness+border_size, + outer_radius=self.icon_radius-self._active_indicator_thickness, color=self._style.border_color, - thickness=border_size) + thickness=self._thin_border_thickness) # label thick border when label when disabled if not self.enabled: @@ -82,7 +84,7 @@ def paint(self, painter: Painter): center=self.center, outer_radius=self.icon_radius, color=self._style.active_color_dark, - thickness=self._thickness//3*2) + thickness=self._active_indicator_thickness) # label thick border when hovered (or it is forced) if self.forced or (self._hovered and self.draggable): @@ -90,14 +92,18 @@ def paint(self, painter: Painter): center=self.center, outer_radius=self.icon_radius, color=self._border_active_color, - thickness=self._thickness//3*2) + thickness=self._active_indicator_thickness) @property - def _thickness(self): + def _thin_border_thickness(self): if self._is_unscaled: return self._style.unscaled_border_thickness return self._style.border_thickness + @property + def _active_indicator_thickness(self): + return self._thin_border_thickness*2 + @property def draggable(self) -> bool: """Return whether the label can be dragged.""" diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py index 33acfcb9..4c44c498 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py @@ -37,4 +37,7 @@ def _prepare_image(self) -> QPixmap: rounded_image = PixmapTransform.make_pixmap_round(to_display) return PixmapTransform.scale_pixmap( pixmap=rounded_image, - size_px=round((self.icon_radius-self._thickness)*2)) + size_px=round(( + self.icon_radius + - self._thin_border_thickness + - self._active_indicator_thickness)*2)) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index a16cd9fb..fad0e42f 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -94,12 +94,12 @@ def widget_radius(self) -> int: @property def border_thickness(self): """Thickness of border around icons.""" - return round(self.icon_radius*0.12) + return round(self.icon_radius*0.05) @property def unscaled_border_thickness(self): """Thickness of border of the pie.""" - return round(self.unscaled_icon_radius*0.12) + return round(self.unscaled_icon_radius*0.05) @property def area_thickness(self): @@ -111,11 +111,6 @@ def inner_edge_radius(self): """Radius at which the base area starts.""" return self.pie_radius - self.area_thickness - @property - def no_border_radius(self): - """Radius at which pie decoration border starts.""" - return self.pie_radius - self.border_thickness//2 - @property def setting_button_radius(self) -> int: """Radius of the button which activates edit mode.""" diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index b4aa2166..56231d94 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -60,17 +60,16 @@ def _paint_base_wheel(self) -> None: # base wheel self.painter.paint_wheel( center=self._center, - outer_radius=self.style.no_border_radius, + outer_radius=self.style.pie_radius, color=self.style.background_color, thickness=self.style.area_thickness) # base wheel decorator - thickness = self.style.border_thickness self.painter.paint_wheel( center=self._center, outer_radius=self.style.inner_edge_radius, color=self.style.border_color, - thickness=thickness//3) + thickness=self.style.border_thickness) def _paint_active_pie(self) -> None: """Paint a pie behind a label which is active or during animation.""" @@ -85,7 +84,7 @@ def _paint_active_pie(self) -> None: # active pie self.painter.paint_pie( center=self._center, - outer_radius=self.style.no_border_radius + thickness_addition, + outer_radius=self.style.pie_radius + thickness_addition, angle=label.angle, span=360//len(self.labels), color=self._pick_pie_color(label), @@ -94,11 +93,11 @@ def _paint_active_pie(self) -> None: # active pie border self.painter.paint_pie( center=self._center, - outer_radius=self.style.no_border_radius + thickness_addition, + outer_radius=self.style.pie_radius + thickness_addition, angle=label.angle, span=360//len(self.labels), - color=self.style.border_color, - thickness=self.style.border_thickness//3) + color=self.style.active_color_dark, + thickness=self.style.border_thickness) def _pick_pie_color(self, label: Label) -> QColor: """Pick color of pie based on widget mode and animation progress.""" From cbd7c42f922534e9cabda0a1dac8525169f91e70 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 58/69] Fix artifacts near borders --- shortcut_composer/templates/pie_menu_utils/label_widget.py | 4 ++-- .../templates/pie_menu_utils/pie_widget_utils/pie_painter.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 0245a3d3..165b4ee5 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -67,8 +67,8 @@ def paint(self, painter: Painter): center=self.center, outer_radius=( self.icon_radius - - self._thin_border_thickness - - self._active_indicator_thickness), + - self._active_indicator_thickness + - self._thin_border_thickness//2), color=Krita.get_main_color_from_theme()) # label thin border diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index 56231d94..94955842 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -62,9 +62,9 @@ def _paint_base_wheel(self) -> None: center=self._center, outer_radius=self.style.pie_radius, color=self.style.background_color, - thickness=self.style.area_thickness) + thickness=self.style.area_thickness+self.style.border_thickness//2) - # base wheel decorator + # base wheel border self.painter.paint_wheel( center=self._center, outer_radius=self.style.inner_edge_radius, From 6071843cd26e730e8141be96b03f4456600618db Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 59/69] Adds decorator near the inner pie edge --- .../templates/pie_menu_utils/pie_style.py | 7 +++++++ .../pie_menu_utils/pie_widget_utils/pie_painter.py | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index fad0e42f..68538318 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -168,6 +168,13 @@ def border_color(self): min(self.background_color.blue()+15, 255), 255) + @property + def pie_decorator_color(self): + """Color of decorator near inner pie edge.""" + color = self.background_color + color = QColor(color.red()-5, color.green()-5, color.blue()-5, 60) + return color + @property def font_multiplier(self): """Multiplier to apply to the font depending on the used OS.""" diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index 94955842..f8a6dbb3 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -71,6 +71,14 @@ def _paint_base_wheel(self) -> None: color=self.style.border_color, thickness=self.style.border_thickness) + # base wheel decorator + decorator_thickness = self.style.border_thickness*4 + self.painter.paint_wheel( + center=self._center, + outer_radius=self.style.inner_edge_radius + decorator_thickness, + color=self.style.pie_decorator_color, + thickness=decorator_thickness) + def _paint_active_pie(self) -> None: """Paint a pie behind a label which is active or during animation.""" for label in self.labels: @@ -97,7 +105,7 @@ def _paint_active_pie(self) -> None: angle=label.angle, span=360//len(self.labels), color=self.style.active_color_dark, - thickness=self.style.border_thickness) + thickness=self.style.border_thickness*1.5) def _pick_pie_color(self, label: Label) -> QColor: """Pick color of pie based on widget mode and animation progress.""" From 7ffc8fa6a773dca324c12abc0ffe088b18fe3017 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 60/69] Adds pie decorator --- .../templates/pie_menu_utils/pie_style.py | 16 +++++++++++-- .../pie_widget_utils/pie_painter.py | 23 ++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 68538318..143a5891 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -96,6 +96,11 @@ def border_thickness(self): """Thickness of border around icons.""" return round(self.icon_radius*0.05) + @property + def decorator_thickness(self): + """Thickness of decorators near edges.""" + return self.border_thickness*4 + @property def unscaled_border_thickness(self): """Thickness of border of the pie.""" @@ -169,12 +174,19 @@ def border_color(self): 255) @property - def pie_decorator_color(self): - """Color of decorator near inner pie edge.""" + def background_decorator_color(self): + """Color of decorator near inner edge.""" color = self.background_color color = QColor(color.red()-5, color.green()-5, color.blue()-5, 60) return color + @property + def pie_decorator_color(self): + """Color of pie decorator near outer pie edge.""" + color = self.active_color_dark + color = QColor(color.red()-5, color.green()-5, color.blue()-5, 60) + return color + @property def font_multiplier(self): """Multiplier to apply to the font depending on the used OS.""" diff --git a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py index f8a6dbb3..ce72bc87 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_widget_utils/pie_painter.py @@ -72,12 +72,13 @@ def _paint_base_wheel(self) -> None: thickness=self.style.border_thickness) # base wheel decorator - decorator_thickness = self.style.border_thickness*4 self.painter.paint_wheel( center=self._center, - outer_radius=self.style.inner_edge_radius + decorator_thickness, - color=self.style.pie_decorator_color, - thickness=decorator_thickness) + outer_radius=( + self.style.inner_edge_radius + + self.style.decorator_thickness), + color=self.style.background_decorator_color, + thickness=self.style.decorator_thickness) def _paint_active_pie(self) -> None: """Paint a pie behind a label which is active or during animation.""" @@ -98,14 +99,24 @@ def _paint_active_pie(self) -> None: color=self._pick_pie_color(label), thickness=self.style.area_thickness + thickness_addition) - # active pie border + # pie decorator self.painter.paint_pie( center=self._center, outer_radius=self.style.pie_radius + thickness_addition, angle=label.angle, span=360//len(self.labels), + color=self.style.pie_decorator_color, + thickness=self.style.border_thickness*4) + + # active pie border + self.painter.paint_pie( + center=self._center, + outer_radius=self.style.pie_radius + + thickness_addition + self.style.border_thickness//2, + angle=label.angle, + span=360//len(self.labels), color=self.style.active_color_dark, - thickness=self.style.border_thickness*1.5) + thickness=self.style.border_thickness) def _pick_pie_color(self, label: Label) -> QColor: """Pick color of pie based on widget mode and animation progress.""" From 9a8b16afdc6c07208ebe81c56bf2abb71a3ca4c8 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 61/69] bugfix: Make key release work in non-latin keyboard layouts --- .../input_adapter/shortcut_adapter.py | 17 +++++++++--- shortcut_composer/input_adapter/translator.py | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 shortcut_composer/input_adapter/translator.py diff --git a/shortcut_composer/input_adapter/shortcut_adapter.py b/shortcut_composer/input_adapter/shortcut_adapter.py index 5dcc174d..f0a34cc3 100644 --- a/shortcut_composer/input_adapter/shortcut_adapter.py +++ b/shortcut_composer/input_adapter/shortcut_adapter.py @@ -7,6 +7,7 @@ from .api_krita import Krita from .complex_action_interface import ComplexActionInterface +from .translator import Translator class ShortcutAdapter: @@ -24,6 +25,9 @@ class ShortcutAdapter: - on_every_key_release (called after short or long release callback) """ + TRANSLATOR = Translator() + """Non-latin siqns need to be substituted with their QWERTY equivalents.""" + def __init__(self, action: ComplexActionInterface) -> None: self.action = action self.key_released = True @@ -71,9 +75,14 @@ def tool_shortcut(self) -> QKeySequence: def _key_sequence_from_event(event: QKeyEvent): return QKeySequence(event.modifiers() | event.key()) # type: ignore - @staticmethod - def _match_shortcuts(_a: QKeySequence, _b: QKeySequence, /) -> bool: + table = str.maketrans( + u"ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ.", + u"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./") + + @classmethod + def _match_shortcuts(cls, _a: QKeySequence, _b: QKeySequence, /) -> bool: """Custom match pattern - one string is preset in another one.""" - parsed_a = _a.toString() - parsed_b = _b.toString() + parsed_a = cls.TRANSLATOR.translate(_a.toString()) + parsed_b = cls.TRANSLATOR.translate(_b.toString()) + return parsed_a in parsed_b or parsed_b in parsed_a diff --git a/shortcut_composer/input_adapter/translator.py b/shortcut_composer/input_adapter/translator.py new file mode 100644 index 00000000..a09f7eaf --- /dev/null +++ b/shortcut_composer/input_adapter/translator.py @@ -0,0 +1,26 @@ + + +class Translator: + """Substitutes non-latin signs with their QWERTY equivalents.""" + + DEFAULT = "`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./" + """Template of signs in latin QWERTY layout.""" + + LAYOUTS = { + "cyryllic": "ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ.", + "arabic": "ذضصثقفغعهخحجدشسيبلاتنمكطئءؤرﻻىةوزظ", + } + """Signs in non-latin alphabets in the same order as in QWERTY.""" + + def __init__(self) -> None: + signs_to_translate = "" + for layout in self.LAYOUTS.values(): + signs_to_translate += layout + + self.table = str.maketrans( + signs_to_translate, + self.DEFAULT*len(self.LAYOUTS)) + + def translate(self, x: str): + """Substitute non-latin signs with their QWERTY equivalents.""" + return x.translate(self.table) From 528f78adf4c39324ec41a31aa2d9d61a6af4e274 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 62/69] Reorganize input_adapter package --- shortcut_composer/input_adapter/action_manager.py | 4 +--- .../input_adapter/action_manager_utils/__init__.py | 10 ++++++++++ .../{ => action_manager_utils}/api_krita.py | 0 .../release_key_event_filter.py} | 0 .../{ => action_manager_utils}/shortcut_adapter.py | 2 +- .../{ => action_manager_utils}/translator.py | 3 ++- 6 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 shortcut_composer/input_adapter/action_manager_utils/__init__.py rename shortcut_composer/input_adapter/{ => action_manager_utils}/api_krita.py (100%) rename shortcut_composer/input_adapter/{event_filter.py => action_manager_utils/release_key_event_filter.py} (100%) rename shortcut_composer/input_adapter/{ => action_manager_utils}/shortcut_adapter.py (98%) rename shortcut_composer/input_adapter/{ => action_manager_utils}/translator.py (88%) diff --git a/shortcut_composer/input_adapter/action_manager.py b/shortcut_composer/input_adapter/action_manager.py index 98a5c97a..e4ad718a 100644 --- a/shortcut_composer/input_adapter/action_manager.py +++ b/shortcut_composer/input_adapter/action_manager.py @@ -11,10 +11,8 @@ from PyQt5.QtWidgets import QWidgetAction -from .api_krita import Krita +from .action_manager_utils import Krita, ReleaseKeyEventFilter, ShortcutAdapter from .complex_action_interface import ComplexActionInterface -from .event_filter import ReleaseKeyEventFilter -from .shortcut_adapter import ShortcutAdapter @dataclass diff --git a/shortcut_composer/input_adapter/action_manager_utils/__init__.py b/shortcut_composer/input_adapter/action_manager_utils/__init__.py new file mode 100644 index 00000000..04052a7f --- /dev/null +++ b/shortcut_composer/input_adapter/action_manager_utils/__init__.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Utils used by core ActionManager.""" + +from .api_krita import Krita +from .release_key_event_filter import ReleaseKeyEventFilter +from .shortcut_adapter import ShortcutAdapter + +__all__ = ["Krita", "ReleaseKeyEventFilter", "ShortcutAdapter"] diff --git a/shortcut_composer/input_adapter/api_krita.py b/shortcut_composer/input_adapter/action_manager_utils/api_krita.py similarity index 100% rename from shortcut_composer/input_adapter/api_krita.py rename to shortcut_composer/input_adapter/action_manager_utils/api_krita.py diff --git a/shortcut_composer/input_adapter/event_filter.py b/shortcut_composer/input_adapter/action_manager_utils/release_key_event_filter.py similarity index 100% rename from shortcut_composer/input_adapter/event_filter.py rename to shortcut_composer/input_adapter/action_manager_utils/release_key_event_filter.py diff --git a/shortcut_composer/input_adapter/shortcut_adapter.py b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py similarity index 98% rename from shortcut_composer/input_adapter/shortcut_adapter.py rename to shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py index f0a34cc3..da90cfb5 100644 --- a/shortcut_composer/input_adapter/shortcut_adapter.py +++ b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py @@ -5,8 +5,8 @@ from PyQt5.QtGui import QKeyEvent, QKeySequence +from ..complex_action_interface import ComplexActionInterface from .api_krita import Krita -from .complex_action_interface import ComplexActionInterface from .translator import Translator diff --git a/shortcut_composer/input_adapter/translator.py b/shortcut_composer/input_adapter/action_manager_utils/translator.py similarity index 88% rename from shortcut_composer/input_adapter/translator.py rename to shortcut_composer/input_adapter/action_manager_utils/translator.py index a09f7eaf..3fc893d5 100644 --- a/shortcut_composer/input_adapter/translator.py +++ b/shortcut_composer/input_adapter/action_manager_utils/translator.py @@ -1,4 +1,5 @@ - +# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus +# SPDX-License-Identifier: GPL-3.0-or-later class Translator: """Substitutes non-latin signs with their QWERTY equivalents.""" From 93afc2a92429c07ee27299802911913d1eb37be5 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 63/69] Adds support for new keyboard layouts --- .../action_manager_utils/shortcut_adapter.py | 4 -- .../action_manager_utils/translator.py | 48 ++++++++++++++----- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py index da90cfb5..72f90a7c 100644 --- a/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py +++ b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py @@ -75,10 +75,6 @@ def tool_shortcut(self) -> QKeySequence: def _key_sequence_from_event(event: QKeyEvent): return QKeySequence(event.modifiers() | event.key()) # type: ignore - table = str.maketrans( - u"ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ.", - u"QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./") - @classmethod def _match_shortcuts(cls, _a: QKeySequence, _b: QKeySequence, /) -> bool: """Custom match pattern - one string is preset in another one.""" diff --git a/shortcut_composer/input_adapter/action_manager_utils/translator.py b/shortcut_composer/input_adapter/action_manager_utils/translator.py index 3fc893d5..56c1a875 100644 --- a/shortcut_composer/input_adapter/action_manager_utils/translator.py +++ b/shortcut_composer/input_adapter/action_manager_utils/translator.py @@ -1,26 +1,48 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -class Translator: - """Substitutes non-latin signs with their QWERTY equivalents.""" +from typing import NamedTuple + - DEFAULT = "`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./" +class Translation(NamedTuple): + language: str + """Signs in non-latin alphabets in the same order as in QWERTY.""" + template: str """Template of signs in latin QWERTY layout.""" - LAYOUTS = { - "cyryllic": "ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ.", - "arabic": "ذضصثقفغعهخحجدشسيبلاتنمكطئءؤرﻻىةوزظ", + +class Translator: + """Substitutes non-latin signs with their QWERTY equivalents.""" + + TRANSLATIONS = { + "cyryllic": Translation( + language="ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ", + template="`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,."), + "german": Translation( + language="ÜÖÄ", + template="[;'"), + "greek": Translation( + language="ΣΕΡΤΥΘΙΟΠΑΔΦΓΗΞΚΛΖΧΨΩΒΝΜ", + template="WERTYUIOPADFGHJKLZXCVBNM"), + "georgian": Translation( + language="„ქწერტყუიოპასდფგჰჯკლზხცვბნმ", + template="`QWERTYUIOPASDFGHJKLZXCVBNM"), + "arabic": Translation( + language="ذضصثقفغعهخحجدشسيبلاتنمكطئءؤرﻻىةوزظ", + template="`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./"), } - """Signs in non-latin alphabets in the same order as in QWERTY.""" def __init__(self) -> None: - signs_to_translate = "" - for layout in self.LAYOUTS.values(): - signs_to_translate += layout + full_language = "" + full_template = "" + for language, template in self.TRANSLATIONS.values(): + if len(language) != len(template): + raise RuntimeError( + f"Length of language does not match: {language, template}") + full_language += language + full_template += template - self.table = str.maketrans( - signs_to_translate, - self.DEFAULT*len(self.LAYOUTS)) + self.table = str.maketrans(full_language, full_template) def translate(self, x: str): """Substitute non-latin signs with their QWERTY equivalents.""" From db3177ba7020a48ec1313b10ce11e3f7a9524ea5 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 64/69] Close all actions on any key release --- .../input_adapter/action_manager.py | 7 +-- .../action_manager_utils/shortcut_adapter.py | 59 +++++-------------- .../action_manager_utils/translator.py | 49 --------------- 3 files changed, 17 insertions(+), 98 deletions(-) delete mode 100644 shortcut_composer/input_adapter/action_manager_utils/translator.py diff --git a/shortcut_composer/input_adapter/action_manager.py b/shortcut_composer/input_adapter/action_manager.py index e4ad718a..f7f7f3ee 100644 --- a/shortcut_composer/input_adapter/action_manager.py +++ b/shortcut_composer/input_adapter/action_manager.py @@ -72,8 +72,8 @@ def bind_action(self, action: ComplexActionInterface) -> None: krita_action=Krita.create_action( window=self._window, name=action.name), - shortcut=self._create_adapter(action) - ) + shortcut=self._create_adapter(action)) + self._stored_actions[action.name] = container def _create_adapter(self, action: ComplexActionInterface) \ @@ -85,6 +85,5 @@ def _create_adapter(self, action: ComplexActionInterface) \ """ shortcut_adapter = ShortcutAdapter(action) self._event_filter.register_release_callback( - shortcut_adapter.event_filter_callback # type: ignore - ) + shortcut_adapter.event_filter_callback) # type: ignore return shortcut_adapter diff --git a/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py index 72f90a7c..2dae22f5 100644 --- a/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py +++ b/shortcut_composer/input_adapter/action_manager_utils/shortcut_adapter.py @@ -3,11 +3,9 @@ from time import time -from PyQt5.QtGui import QKeyEvent, QKeySequence +from PyQt5.QtGui import QKeyEvent from ..complex_action_interface import ComplexActionInterface -from .api_krita import Krita -from .translator import Translator class ShortcutAdapter: @@ -23,62 +21,33 @@ class ShortcutAdapter: - on_short_key_release (release directly after the press) - on_long_key_release (release long time after the press) - on_every_key_release (called after short or long release callback) - """ - TRANSLATOR = Translator() - """Non-latin siqns need to be substituted with their QWERTY equivalents.""" + Only one instance of ShortcutAdapter can handle key at a time. All + others are blocked. + """ def __init__(self, action: ComplexActionInterface) -> None: self.action = action - self.key_released = True + self.local_lock = False self.last_press_time = time() def on_key_press(self) -> None: """Run action's on_key_press() and remember the time of it.""" - self.key_released = False + self.local_lock = True self.last_press_time = time() self.action.on_key_press() + def event_filter_callback(self, release_event: QKeyEvent) -> None: + """Handle key release if the event is related to the action.""" + if self.local_lock and not release_event.isAutoRepeat(): + self._on_key_release() + def _on_key_release(self) -> None: """Run proper key release methods based on time elapsed from press.""" - self.key_released = True - if time() - self.last_press_time < self._short_vs_long_press_time: + elapsed_time = time() - self.last_press_time + if elapsed_time < self.action.short_vs_long_press_time: self.action.on_short_key_release() else: self.action.on_long_key_release() self.action.on_every_key_release() - - def _is_event_key_release(self, release_event: QKeyEvent) -> bool: - """Decide if the key release event is matches shortcut and is valid.""" - return (not release_event.isAutoRepeat() - and not self.key_released - and self._match_shortcuts( - self._key_sequence_from_event(release_event), - self.tool_shortcut)) - - def event_filter_callback(self, release_event: QKeyEvent) -> None: - """Handle key release if the event is related to the action.""" - if self._is_event_key_release(release_event): - self._on_key_release() - - @property - def _short_vs_long_press_time(self) -> float: - """Time in seconds distinguishing short key presses from long ones.""" - return self.action.short_vs_long_press_time - - @property - def tool_shortcut(self) -> QKeySequence: - """Return shortcut assigned to shortcut red from krita settings.""" - return Krita.get_action_shortcut(self.action.name) - - @staticmethod - def _key_sequence_from_event(event: QKeyEvent): - return QKeySequence(event.modifiers() | event.key()) # type: ignore - - @classmethod - def _match_shortcuts(cls, _a: QKeySequence, _b: QKeySequence, /) -> bool: - """Custom match pattern - one string is preset in another one.""" - parsed_a = cls.TRANSLATOR.translate(_a.toString()) - parsed_b = cls.TRANSLATOR.translate(_b.toString()) - - return parsed_a in parsed_b or parsed_b in parsed_a + self.local_lock = False diff --git a/shortcut_composer/input_adapter/action_manager_utils/translator.py b/shortcut_composer/input_adapter/action_manager_utils/translator.py deleted file mode 100644 index 56c1a875..00000000 --- a/shortcut_composer/input_adapter/action_manager_utils/translator.py +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus -# SPDX-License-Identifier: GPL-3.0-or-later - -from typing import NamedTuple - - -class Translation(NamedTuple): - language: str - """Signs in non-latin alphabets in the same order as in QWERTY.""" - template: str - """Template of signs in latin QWERTY layout.""" - - -class Translator: - """Substitutes non-latin signs with their QWERTY equivalents.""" - - TRANSLATIONS = { - "cyryllic": Translation( - language="ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ", - template="`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,."), - "german": Translation( - language="ÜÖÄ", - template="[;'"), - "greek": Translation( - language="ΣΕΡΤΥΘΙΟΠΑΔΦΓΗΞΚΛΖΧΨΩΒΝΜ", - template="WERTYUIOPADFGHJKLZXCVBNM"), - "georgian": Translation( - language="„ქწერტყუიოპასდფგჰჯკლზხცვბნმ", - template="`QWERTYUIOPASDFGHJKLZXCVBNM"), - "arabic": Translation( - language="ذضصثقفغعهخحجدشسيبلاتنمكطئءؤرﻻىةوزظ", - template="`QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./"), - } - - def __init__(self) -> None: - full_language = "" - full_template = "" - for language, template in self.TRANSLATIONS.values(): - if len(language) != len(template): - raise RuntimeError( - f"Length of language does not match: {language, template}") - full_language += language - full_template += template - - self.table = str.maketrans(full_language, full_template) - - def translate(self, x: str): - """Substitute non-latin signs with their QWERTY equivalents.""" - return x.translate(self.table) From f9c9fae42f3b1eadfbb3ee988adee725346d2555 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Thu, 3 Aug 2023 21:06:08 +0200 Subject: [PATCH 65/69] Update manual for 1.4.0 release --- README.md | 13 ++++++++----- shortcut_composer/manual.html | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 29415720..3f65c589 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Shortcut composer **v1.3.2** +# Shortcut composer **v1.4.0** [![python](https://img.shields.io/badge/Python-3.8-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org) [![Code style: black](https://img.shields.io/badge/code%20style-autopep8-333333.svg)](https://pypi.org/project/autopep8/) @@ -28,16 +28,19 @@ The plugin adds new shortcuts of the following types: - [Join community discussion 👥](https://krita-artists.org/t/shortcut-composer-v1-2-2-plugin-for-pie-menus-multiple-key-assignment-mouse-trackers-and-more/55314) - [Report a bug 🦗](https://github.com/wojtryb/Shortcut-Composer/issues) - [Request a new feature 💡](https://github.com/wojtryb/Shortcut-Composer/discussions) +- [What's new in latest version? ⭐](https://github.com/wojtryb/Shortcut-Composer/releases) -## What's new in the latest release? - -Watch the video below, or read the [changelog](https://github.com/wojtryb/Shortcut-Composer/releases). +## Changelog videos [![PIE MENUS - introducing Shortcut Composer](https://user-images.githubusercontent.com/51094047/244950488-83bd44ff-87f6-4b95-82c7-0f5031bb1b8e.png)](https://www.youtube.com/watch?v=eHK5LBMNiU0 "Managing BRUSHES with Shortcut Composer 1.3") +[![PIE MENUS - introducing Shortcut Composer](https://github-production-user-asset-6210df.s3.amazonaws.com/51094047/238015603-3143fc2d-0fa7-4da1-868d-2ec054ccaeb3.png)](https://www.youtube.com/watch?v=Tkf2-U0OyG4 "PIE MENUS - introducing Shortcut Composer") + +[![PIE MENUS - release video](https://github-production-user-asset-6210df.s3.amazonaws.com/51094047/238179887-87c00d86-0e65-46c2-94c4-52bb02c99501.png)](https://youtu.be/hrjBycVYFZM "PIE MENUS - introducing Shortcut Composer") + ## Requirements - Version of krita on plugin release: **5.1.5** -- Required version of krita: **5.1.0** +- Required version of krita: **5.1.0** or later OS support state: - [x] Windows (10, 11) diff --git a/shortcut_composer/manual.html b/shortcut_composer/manual.html index 79e8b864..5d579aeb 100644 --- a/shortcut_composer/manual.html +++ b/shortcut_composer/manual.html @@ -9,7 +9,7 @@ -

Shortcut composer v1.3.2

+

Shortcut composer v1.4.0


Extension for painting application Krita, which allows to create custom, complex keyboard shortcuts.

The plugin adds new shortcuts of the following types:

@@ -23,7 +23,7 @@

Shortcut composer v1.3.2

Requirements
  • Version of krita on plugin release: 5.1.5
  • -
  • Required version of krita: 5.1.0
  • +
  • Required version of krita: 5.1.0 or later

OS support state:

    From ebbb05e78735798370709e435b26f101044741fe Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Sun, 6 Aug 2023 10:24:40 +0200 Subject: [PATCH 66/69] Do not scale icon and pie borders --- .../templates/pie_menu_utils/label_widget.py | 12 +++--------- .../label_widget_impl/image_label_widget.py | 2 +- .../templates/pie_menu_utils/pie_style.py | 9 ++------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget.py index 165b4ee5..34601198 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget.py @@ -68,7 +68,7 @@ def paint(self, painter: Painter): outer_radius=( self.icon_radius - self._active_indicator_thickness - - self._thin_border_thickness//2), + - self._style.border_thickness//2), color=Krita.get_main_color_from_theme()) # label thin border @@ -76,7 +76,7 @@ def paint(self, painter: Painter): center=self.center, outer_radius=self.icon_radius-self._active_indicator_thickness, color=self._style.border_color, - thickness=self._thin_border_thickness) + thickness=self._style.border_thickness) # label thick border when label when disabled if not self.enabled: @@ -94,15 +94,9 @@ def paint(self, painter: Painter): color=self._border_active_color, thickness=self._active_indicator_thickness) - @property - def _thin_border_thickness(self): - if self._is_unscaled: - return self._style.unscaled_border_thickness - return self._style.border_thickness - @property def _active_indicator_thickness(self): - return self._thin_border_thickness*2 + return self._style.border_thickness*2 @property def draggable(self) -> bool: diff --git a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py index 4c44c498..8247c6db 100644 --- a/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py +++ b/shortcut_composer/templates/pie_menu_utils/label_widget_impl/image_label_widget.py @@ -39,5 +39,5 @@ def _prepare_image(self) -> QPixmap: pixmap=rounded_image, size_px=round(( self.icon_radius - - self._thin_border_thickness + - self._style.border_thickness - self._active_indicator_thickness)*2)) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_style.py b/shortcut_composer/templates/pie_menu_utils/pie_style.py index 143a5891..e6e38797 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_style.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_style.py @@ -93,19 +93,14 @@ def widget_radius(self) -> int: @property def border_thickness(self): - """Thickness of border around icons.""" - return round(self.icon_radius*0.05) + """Thickness of border of the pie and icons.""" + return round(self.unscaled_icon_radius*0.05) @property def decorator_thickness(self): """Thickness of decorators near edges.""" return self.border_thickness*4 - @property - def unscaled_border_thickness(self): - """Thickness of border of the pie.""" - return round(self.unscaled_icon_radius*0.05) - @property def area_thickness(self): """Thickness of the base area of pie menu.""" From f44e89bce3f15dbd6b7866aef1ca6e35e6ac9f30 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Sun, 6 Aug 2023 10:24:40 +0200 Subject: [PATCH 67/69] Use default strategies for preset and blending mode pies --- shortcut_composer/actions.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shortcut_composer/actions.py b/shortcut_composer/actions.py index a568c58f..4f82b13e 100644 --- a/shortcut_composer/actions.py +++ b/shortcut_composer/actions.py @@ -191,7 +191,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ Tool.MULTI_BRUSH, Tool.ASSISTANTS, ], - pie_radius_scale=0.9 + pie_radius_scale=0.9, ), @@ -246,6 +246,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ name="Pick painting blending modes", controller=controllers.BlendingModeController(), instructions=[instructions.SetBrushOnNonPaintable()], + deadzone_strategy=DeadzoneStrategy.PICK_TOP, values=[ BlendingMode.NORMAL, BlendingMode.OVERLAY, @@ -255,7 +256,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ BlendingMode.SCREEN, BlendingMode.DARKEN, BlendingMode.LIGHTEN, - ] + ], ), # Use pie menu to create painting layer with selected blending mode. @@ -296,6 +297,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ name="Pick brush presets (red)", controller=controllers.PresetController(), instructions=[instructions.SetBrushOnNonPaintable()], + deadzone_strategy=DeadzoneStrategy.PICK_PREVIOUS, values=Tag("★ My Favorites"), background_color=QColor(95, 65, 65, 190), active_color=QColor(200, 70, 70), @@ -307,6 +309,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ name="Pick brush presets (green)", controller=controllers.PresetController(), instructions=[instructions.SetBrushOnNonPaintable()], + deadzone_strategy=DeadzoneStrategy.PICK_PREVIOUS, values=Tag("RGBA"), background_color=QColor(65, 95, 65, 190), active_color=QColor(70, 200, 70), @@ -318,6 +321,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ name="Pick brush presets (blue)", controller=controllers.PresetController(), instructions=[instructions.SetBrushOnNonPaintable()], + deadzone_strategy=DeadzoneStrategy.PICK_PREVIOUS, values=Tag("Erasers"), background_color=QColor(70, 70, 105, 190), active_color=QColor(110, 160, 235), @@ -330,6 +334,7 @@ def create_actions() -> List[templates.RawInstructions]: return [ name="Pick local brush presets", controller=controllers.PresetController(), instructions=[instructions.SetBrushOnNonPaintable()], + deadzone_strategy=DeadzoneStrategy.PICK_PREVIOUS, values=[], save_local=True, active_color=QColor(234, 172, 0), From 82df4c86172fbc95225bc9716e437501556a7a07 Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Sun, 6 Aug 2023 10:24:40 +0200 Subject: [PATCH 68/69] bugfix: allow ScrollArea to expand horizontally --- .../pie_settings_impl/common_utils/offset_grid_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py index a55c834c..1693521c 100644 --- a/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py +++ b/shortcut_composer/templates/pie_menu_utils/pie_settings_impl/common_utils/offset_grid_layout.py @@ -38,7 +38,7 @@ def __init__(self, max_columns: int, owner: QWidget) -> None: self._max_columns = max_columns self._items_in_group = 2*max_columns - 1 self._owner = owner - self.setAlignment(Qt.AlignTop | Qt.AlignLeft) # type: ignore + self.setAlignment(Qt.AlignTop) # type: ignore self.setVerticalSpacing(5) self.setHorizontalSpacing(5) From 0c047d7e3786fac6ac81cddd15696ae28e8c5a5a Mon Sep 17 00:00:00 2001 From: Wojciech Trybus Date: Sun, 6 Aug 2023 10:24:40 +0200 Subject: [PATCH 69/69] Bump plugin version --- shortcut_composer/INFO.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut_composer/INFO.py b/shortcut_composer/INFO.py index acf1f139..d87a0d9a 100644 --- a/shortcut_composer/INFO.py +++ b/shortcut_composer/INFO.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus # SPDX-License-Identifier: GPL-3.0-or-later -__version__ = "1.4.0dev" +__version__ = "1.4.0" __author__ = "Wojciech Trybus" __license__ = "GPL-3.0-or-later"