Actions in Krita
QActions are an abstraction to associate some data like name, icon, and tooltip with a callback method, in order to provide an interface for widgets and keyboard shortcuts.
There has never been guidelines of how to use these until now, and because they can be easily tacked on and forgotten about, the system is prone to rotting. To counteract this, a coherent manager for actions in Krita has been designed. Because the system will remain complex even after existing code is cleaned up, this page also documents the way things are currently done.
I will use the term "action" to refer to objects that inherit from QAction. This does not include the classes PrimaryAction/SecondaryAction, which could perhaps stand a bit of renaming when they are refactored in the future.
All action metadata should be contained in one of the .action XML files. Each action should be identified by a unique name. Otherwise these are in a somewhat flexible "long" format. Much of the rationale of the format is the extractrc program used to extract translation strings. In particular, only text contained in fields <text></text>, <tooltip></tooltip> and some others will be translated.
The global static class KisActionRegistry reads these data files. The class can serve as a factory of actions.
To create a QAction, most of the time, we want to use the method
QAction *action = KisActionRegistry::instance()->makeQAction("name");
Calls like action->setTooltip(i18n("tooltip")); should be completely obviated. The latter is an example of static data which should be removed from the code out to the .action description files. KisActionRegistry will also have a template method for creating derived classes of actions, such as QWidgetActions.
Right now, KisActionRegistry exposes the DOM data, though that might perhaps better be encapsulated another way. The system should be robust against requests for a property that is not in the data, e.g. requesting the shortcut for an action that has no <shortcut> tag.
Saving custom shortcut schemes is next up on the to-do list.
There are general ideas of how the actions system works, but no hard and fast rules.
There are many distinct groups of actions inside Krita.
Collections of actions are stored in a Widgetutils/XMLGUIClient class called KActionCollection. This class has an enormous amount of structure and functionality, to the point which it can be annoying. This is ripe for refactoring, if you ever feel like doing so.
The main KActionCollection is owned by the KisMainWindow, since that class inherits from XMLGUIClient. This main collection gets passed around and referenced from tons and tons of different clients. This is the one we share with KisActionManager, KisViewManager, all the KisXxxManagers inject actions into this collection. (KisViewManager->actionCollection() is referenced 48 times in the code base.) The behavior of this when multiple main windows are open will be discussed later.
There are a number of QActions that are associated with tools. These show up in the Flake library. Tools have their own action collections, their own system (ToolHelper) and their own interface for requesting actions. This is sketchy and probably does no good anymore, since Krita can now use its own action abstractions. Tool actions are also registered to the MainWindow subsystem already in a questionable way.
KStandardActions is supposed to provide a repository of universal actions, like "Save As...". We have our own copy inside of Widgetutils and it doesn't play nice with our system. It should be fixed.
KisActions are a derived class. They use callbacks when they are executed, have ActivationFlags/ActivationConditions to enable/disable actions based on the state of the program.
KisMainWindowObserver is a class that tracks a main window around. It should be examined more.
Information about the macro subsystem:
[17:47] <slangkamp> abrahams: well the idea behind it was that we could identify operations with ids [17:48] <slangkamp> abrahams: some operation might be executed from the gui with an action or by a macro functionality [17:51] <slangkamp> abrahams: if you look e.g. at KisSelectionManager there is a lot of code in the form: [17:51] <slangkamp> KisSelectAllActionFactory factory; [17:51] <slangkamp> factory.run(m_view); [17:52] <slangkamp> Usually that code should not exist, but it should get the factory from a registry [17:53] <slangkamp> so there are two cases action calls it and the ui is put into a dialog [17:53] <slangkamp> or macro editor uses it (which doesn't exist yet)