Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
533 changes: 531 additions & 2 deletions src/backend/DeckManagement/DeckController.py

Large diffs are not rendered by default.

34 changes: 31 additions & 3 deletions src/backend/DeckManagement/InputIdentifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,43 @@ def __init__(self, json_identifier: str):
self.index = str(json_identifier)
super().__init__(self.input_type, json_identifier, self.controller_class_name)

All = (Key, Dial, Touchscreen)
class TouchKey(InputIdentifier):
input_type = "touch_keys"
controller_class_name = "ControllerTouchKey"

class Events(InputEvent):
DOWN = "Touch Key Down"
UP = "Touch Key Up"
SHORT_UP = "Touch Key Short Up"
HOLD_START = "Touch Key Hold Start"
HOLD_STOP = "Touch Key Hold Stop"

def __init__(self, json_identifier: str):
self.index = int(json_identifier)
super().__init__(self.input_type, json_identifier, self.controller_class_name)

class Screen(InputIdentifier):
input_type = "screens"
controller_class_name = "ControllerScreen"

class Events(InputEvent):
UPDATE = "Screen Update"

def __init__(self, json_identifier: str):
self.index = str(json_identifier)
super().__init__(self.input_type, json_identifier, self.controller_class_name)

All = (Key, Dial, Touchscreen, TouchKey, Screen)
KeyTypes = [key_type.input_type for key_type in All]

@staticmethod
def FromTypeIdentifier(input_type: str, json_identifier: str):
input_map = {
"keys": Input.Key,
"dials": Input.Dial,
"touchscreens": Input.Touchscreen
"touchscreens": Input.Touchscreen,
"touch_keys": Input.TouchKey,
"screens": Input.Screen,
}
if input_type in input_map:
return input_map[input_type](json_identifier)
Expand Down
30 changes: 24 additions & 6 deletions src/backend/DeckManagement/Subclasses/FakeDeck.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ def __init__(self, serial_number = None, deck_type = None):
self._key_layout = gl.settings_manager.get_deck_settings(self.serial_number).get("key-layout", [3, 5])
self._key_layout = [2, 4]

self._is_touch = True
self._dial_count = 4
self._is_touch = False
self._dial_count = 0
self._touch_key_count = 2

def deck_type(self):
return self._deck_type
Expand Down Expand Up @@ -85,18 +86,35 @@ def is_visual(self) -> bool:
return True

def is_touch(self) -> bool:
return self.is_touch
return self._is_touch

def dial_count(self) -> int:
return self._dial_count


def touch_key_count(self) -> int:
return self._touch_key_count

def touchscreen_image_format(self) -> dict:
return{
"size": (800, 100),
"format": "JPEG",
"flip": (False, False),
"rotation": 0
}


def screen_image_format(self) -> dict:
return {
"size": (248, 58),
"format": "JPEG",
"flip": (False, False),
"rotation": 0
}

def set_touchscreen_image(self, *args, **kwargs):
return

def set_screen_image(self, *args, **kwargs):
return

def set_key_color(self, *args, **kwargs):
return
27 changes: 22 additions & 5 deletions src/backend/DeckManagement/Subclasses/RemoteDeck.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,35 @@ def is_visual(self) -> bool:
return True

def is_touch(self) -> bool:
return self.is_touch
return self._is_touch

def dial_count(self) -> int:
return self._dial_count


def touch_key_count(self) -> int:
return 0

def touchscreen_image_format(self) -> dict:
return{
return {
"size": (800, 100),
"format": "JPEG",
"flip": (False, False),
"rotation": 0
}


def screen_image_format(self) -> dict:
return {
"size": (0, 0),
"format": "JPEG",
"flip": (False, False),
"rotation": 0
}

def set_touchscreen_image(self, *args, **kwargs):
return

def set_screen_image(self, *args, **kwargs):
return

def set_key_color(self, *args, **kwargs):
return
6 changes: 3 additions & 3 deletions src/backend/PluginManager/ActionCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def on_update(self):
def set_media(self, image = None, media_path=None, size: float = None, valign: float = None, halign: float = None, fps: int = 30, loop: bool = True, update: bool = True):
self.raise_error_if_not_ready()

if type(self.input_ident) not in [Input.Key, Input.Dial]:
if type(self.input_ident) not in [Input.Key, Input.Dial, Input.Screen]:
return

if not self.get_is_present(): return
Expand Down Expand Up @@ -259,9 +259,9 @@ def set_label(self, text: str, position: str = "bottom", color: list[int]=None,
update: bool=True):
self.raise_error_if_not_ready()

if type(self.input_ident) not in [Input.Key, Input.Dial]:
if type(self.input_ident) not in [Input.Key, Input.Dial, Input.Screen]:
return

if self.get_state() is None:
log.error(f"Could not find state, action: {self.action_id}, state: {self.state}")
return
Expand Down
15 changes: 13 additions & 2 deletions src/backend/PluginManager/ActionHolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ def __init__(self,
action_support = {
Input.Key: ActionInputSupport.UNTESTED,
Input.Dial: ActionInputSupport.UNTESTED,
Input.Touchscreen: ActionInputSupport.UNTESTED
Input.Touchscreen: ActionInputSupport.UNTESTED,
Input.TouchKey: ActionInputSupport.UNTESTED,
Input.Screen: ActionInputSupport.UNTESTED,
},
*args, **kwargs):

Expand Down Expand Up @@ -100,4 +102,13 @@ def init_and_get_action(self, deck_controller: DeckController, page: Page, state
)

def get_input_compatibility(self, identifier: InputIdentifier) -> ActionInputSupport:
return self.action_support.get(type(identifier), ActionInputSupport.UNSUPPORTED)
ident_type = type(identifier)
if ident_type in self.action_support:
return self.action_support[ident_type]
# TouchKeys behave like regular keys
if ident_type == Input.TouchKey and Input.Key in self.action_support:
return self.action_support[Input.Key]
# Neo Screen falls back to Key support since it renders images/labels like keys
if ident_type == Input.Screen and Input.Key in self.action_support:
return self.action_support[Input.Key]
return ActionInputSupport.UNSUPPORTED
175 changes: 175 additions & 0 deletions src/windows/mainWindow/DeckNeo/ScreenBar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import gi

from PIL import Image

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from src.backend.DeckManagement.DeckController import ControllerScreen
from src.backend.DeckManagement.InputIdentifier import Input, InputIdentifier
from src.backend.DeckManagement.ImageHelpers import image2pixbuf
from src.backend.DeckManagement.HelperMethods import recursive_hasattr

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")

import globals as gl

from gi.repository import Gtk, Adw, Gdk, GLib, Gio

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from src.windows.mainWindow.elements.PageSettingsPage import PageSettingsPage

class ScreenBar(Gtk.Frame):
def __init__(self, page_settings_page: "PageSettingsPage", identifier: Input.Screen, **kwargs):
self.page_settings_page = page_settings_page
self.deck_controller = page_settings_page.deck_controller
self.identifier = identifier

super().__init__(**kwargs)
self.set_css_classes(["key-button-frame-hidden"])
self.set_halign(Gtk.Align.CENTER)
self.set_hexpand(True)

self.pixbuf = None

self.image = ScreenBarImage(self)
self.image.set_image(Image.new("RGBA", (248, 58), (0, 0, 0, 0)))
self.set_child(self.image)

focus_controller = Gtk.EventControllerFocus()
self.image.add_controller(focus_controller)
focus_controller.connect("enter", self.on_focus_in)

self.click_ctrl = Gtk.GestureClick().new()
self.click_ctrl.connect("pressed", self.on_click)
self.click_ctrl.set_button(0)
self.image.add_controller(self.click_ctrl)

self.set_focus_child(self.image)
self.image.set_focusable(True)

## Actions
self.action_group = Gio.SimpleActionGroup()
self.insert_action_group("screen", self.action_group)

self.remove_action = Gio.SimpleAction.new("remove", None)
self.remove_action.connect("activate", self.on_remove)
self.action_group.add_action(self.remove_action)

## Shortcuts
self.shortcut_controller = Gtk.ShortcutController()
self.add_controller(self.shortcut_controller)

remove_shortcut_action = Gtk.CallbackAction.new(self.on_remove)
self.remove_shortcut = Gtk.Shortcut.new(Gtk.ShortcutTrigger.parse_string("Delete"), remove_shortcut_action)
self.shortcut_controller.add_shortcut(self.remove_shortcut)

self.connect("map", self.on_map)
self.load_from_changes()

def on_map(self, widget):
self.load_from_changes()

def load_from_changes(self) -> None:
if not hasattr(self.deck_controller, "ui_image_changes_while_hidden"):
return
tasks = self.deck_controller.ui_image_changes_while_hidden
if self.identifier in tasks:
self.image.set_image(tasks[self.identifier])
tasks.pop(self.identifier)

def on_click(self, gesture, n_press, x, y):
if gesture.get_current_button() == 1 and n_press == 1:
self.image.grab_focus()

controller_input = self.page_settings_page.deck_controller.get_input(self.identifier)
state = controller_input.get_active_state().state
gl.app.main_win.sidebar.load_for_identifier(self.identifier, state)

def on_focus_in(self, *args):
self.set_border_active(True)

def set_border_active(self, active: bool):
if active:
if self.page_settings_page.deck_config.active_widget not in [self, None]:
self.page_settings_page.deck_config.active_widget.set_border_active(False)
self.page_settings_page.deck_config.active_widget = self
self.set_css_classes(["key-button-frame"])
else:
self.set_css_classes(["key-button-frame-hidden"])
self.page_settings_page.deck_config.active_widget = None

def on_remove(self, *args) -> None:
controller = gl.app.main_win.get_active_controller()
if controller is None:
return

active_page = controller.active_page
if active_page is None:
return

screen = controller.get_input(self.identifier)

if str(screen.state) not in active_page.dict.get(self.identifier.input_type, {}).get(self.identifier.json_identifier, {}).get("states", {}):
return

del active_page.dict[self.identifier.input_type][self.identifier.json_identifier]["states"][str(screen.state)]
active_page.save()
active_page.load()

active_page.reload_similar_pages(identifier=self.identifier, reload_self=True)

gl.app.main_win.sidebar.load_for_identifier(self.identifier, screen.state)


class ScreenBarImage(Gtk.Picture):
def __init__(self, screenbar: ScreenBar, **kwargs):
super().__init__(keep_aspect_ratio=True, can_shrink=True, content_fit=Gtk.ContentFit.SCALE_DOWN,
halign=Gtk.Align.CENTER, hexpand=False, width_request=80, height_request=10,
valign=Gtk.Align.CENTER, vexpand=False, css_classes=["plus-screenbar-image"],
**kwargs)

self.screenbar = screenbar

self.on_map_tasks: list[callable] = []
self.connect("map", self.on_map)

self.latest_task_id: int = None

def on_map(self, *args):
for task in self.on_map_tasks:
task()
self.on_map_tasks.clear()

def get_controller_screen(self) -> "ControllerScreen":
controller = gl.app.main_win.get_active_controller()
return controller.get_input(Input.Screen("sd-neo"))

def get_new_task_id(self):
if self.latest_task_id is None:
return 0
return self.latest_task_id + 1

def set_image(self, image: Image.Image):
if not self.get_mapped():
self.on_map_tasks = [lambda: self.set_image(image)]
return

width = 385
thumbnail = image.copy()
thumbnail.thumbnail((width, int(width * 58 / 248)))

pixbuf = image2pixbuf(thumbnail.convert("RGBA"), force_transparency=True)
self.latest_task_id = self.get_new_task_id()
GLib.idle_add(self.set_pixbuf_and_del, pixbuf, self.latest_task_id, priority=GLib.PRIORITY_HIGH)

thumbnail.close()
del thumbnail

def set_pixbuf_and_del(self, pixbuf, task_id: int = None):
if task_id is not None:
if task_id != self.latest_task_id:
return
self.set_pixbuf(pixbuf)
del pixbuf
Empty file.
Loading