Skip to content

Conversation

@quarterstar
Copy link
Contributor

This PR implements a new dynamic keybinding system that makes the controller class more flexible. Specifically, its primary purpose is to fill the needs of issue #41 and lay down the road for implementing custom keybinds in the future.

The PR implements an action-based system. However, this only addresses the shortcuts defined in the controller itself. For the other hard-coded keybinds in the tool files, further adjustment will be needed.

Currently, the shortcuts implemented are Space + Left Click for using the move tool and Right Click for using the erase tool. The implementation is not finished and I am requesting comments before I proceed further.

@Prayag2
Copy link
Owner

Prayag2 commented Nov 29, 2025

Could you please explain how this action based system works in detail?

@quarterstar
Copy link
Contributor Author

quarterstar commented Nov 29, 2025

@Prayag2, an action is essentially a task that is executed when one or a combination of keyboard shortcuts is pressed. For one task, there can be many shortcuts. The task class only encapsulates the execution logic and the keyboard shortcuts are decoupled from it. You can see this from the Facade class that is a container for both the data of the task (ActionData) and the task executor itself (classes that implement IActionTask interface).

The TaskContext is global state that is passed to all task executors. Conversely, ActionState is unique to every task and is stored in the Facade class. The reason it is not stored inside ActionData is because that is only meant to be used for data imported from JSON, which is described below.

This system is meant to allow Drawy to store keyboard shortcut data in a JSON format like this:

[
  {
    "action": "Use Tool", // The ID of the action type in the code
    "actionParameters": {
      "tool": "Move" // The name of the tool to be used
    },
    "keys": [
      {
        "id": 1234, // Qt input tokens (mouse or keyboard)
        "type": "Mouse",
        "count": 1 // Must be >= 1 (fallbacks to 1)
      }
    ],
    "holdRequired": false, // Whether to stop using the tool on key release
    "maxGapMs": 50 // The time threshold in which the keys need to be pressed, if they are not pressed in their defined order; the threshold time the other keys need to be pressed in (0 means they need to be pressed in-order, -1 means infinite threshold)
  }
]

You can see the default actions that are created in the getDefaultActions function. One change I need to make, however, is make the shortcuts stored in ActionData be able to store multiple separate key combinations. For example, we might want to have both Middle Click and Space + Left Click as acceptable default shortcuts for using the move tool temporarily. This is now implemented; refer to getDefaultActions, where you can see Middle Click and Space + Left Click being synonymous for a single action.

Actions are just not limited to tools. I have made the code abstract so that we can define actions for other things that we may need in the future.

The main issue with this implementation, as I mentioned, is that the shortcuts inside the tools themselves remain hard-coded. I have only decoupled the logic for the shortcuts in the controller class, and I need further advice for how we can make the system cover those as well, and if it needs to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Available Tasks

Development

Successfully merging this pull request may close these issues.

2 participants