A New Palette Docker for Krita
This is a Google Summer of Code project.
The proposal is on the task page.
The new palette docker is designed and written to improve artists' experience of color management when using Krita.
The original palette docker enables users to create new palettes that are shared with all works and to maintain those palettes as a list of colors, though they are shown as matrices of colors.
Compared to the original palette docker, the new one is better mainly in these 3 ways:
- The colors are now not only shown, but also actually managed in matrices. Users can move them around freely in those matrices. They can leave empty slots or let the colors from some pattern if they want.
- The palettes are associated with the .kra painting documents. Artists can create temporary palettes for a single file but don't need to worry about polluting the environment when editing other paintings. If they want to reuse palettes, they can also set the palettes to be 'global'.
- The docker now comes with a more intuitive GUI. Artists can now better edit the palettes and easily export and import them.
Merged and not Merged
In the early phase of GSoC, I noticed that some base classes of the palettes can be reused in other places. Therefore, I did some refactoring to reuse them. The code reviews are here:
These 2 code reviews got merged, however, most of my work are in this code review, not yet merged.
That is because merging parts of the change will break some functions. This project involves several widgets that cooperate with each other. If only some of the widgets got merged, master will have widgets that don't work with each other well. Therefore, we decided to merge after everything is done.
Details of usage
Each palette have the following attributes:
|Column number||Number of columns.|
|Name||Name of the palette.|
|Filename||Path to where the palette is stored. For non-global palettes it is just a file name.|
|Is global||A palette is global if it isn't stored inside a .kra file, and can be accessed by all documents.|
|Is read only||Whether a palette is read only. Palettes that ship with Krita are usually read only.|
Also, each palette can have more than one groups. Each group has the same column number as the palette itself, and they each have a row number.
Every palette always has a main group that has no name. This group cannot be renamed or removed. One can set the row number of it to 0, though.
Below is a picture of the docker.
By clicking the Edit this palette button on the palette docker, one can open a dialog. In the dialog, one can set the attributes of the palette and the groups.
Each swatch in the palette docker also have more attributes than position and color. They each can have a name and string id. I actually don't know what the id is for... By double clicking a swatch, a dialog will pop up and let users edit it. One can also call the dialog by clicking the modify this spot button on the docker.
The docker also supports drag and drop. By dragging and dropping one can move a swatch to a new position or switch it with another swatch.
On the bottom left corner of the docker, there is a pop up button. After clicking it, the palette list widget will pop up.
At the bottom of the palette list widget, there are 4 buttons. From left to right, they are respectively Add a new palette, Export current palette to file, Import a new palette from file, and Remove current palette. By default, Add and Import create non-global palettes. Remove can remove all palettes, global or non-global, except for those read only ones.
Done and To Do
All the features that are in the proposal are done. This involves the palette docker itself, the base classes in the underlying libraries relating to it, and other classes using these base classes. There are probably still some bugs, so I will need to work on locating and solving them.
While working on the palette docker, I got some new ideas, and also found some problems in the code base that I want to solve. Those include:
- Making it possible to select multiple swatches in the palette so that they can be drag/dropped or removed.
- Making it possible to drag the group names rows in the palette docker to reorder them.
- Use a list view (instead of a combo box) to show the groups in the dialog used to edit them, and making it easier to rename, delete and reorder groups. It should be like the layers docker.
- Use KisDlgInternalColorSelector to replace KoColorSetWidget. This way, the dependency that holds palette classes in kritawidgets will be resolved and it can be moved up into kritaui, and have some better tools to use.
- Improve KisDlgInternalColorSelector, making the triangular selector more smooth and the spin boxes show color values normally.
Details of what has been done
KoColorSet and KisPaletteModel have been heavily modified. Along with the new classes KisSwatch and KisSwatchGroup, they form the underlying data structure of palettes, which are grouped color matrices.
The API of KoColorSet and KisPaletteModel have been modified. What I want is to let all the data manipulation be done in KisPaletteModel. This is not done yet.
Surely, KisPaletteView and KisPaletteDelegate are modified to show the new matrices of colors.
The above group form the MVC system of palettes.
For making the modification of the palette tell krita that the document has changed, KisChangePaletteCommand inheriting from KUndo2Command is created.
For managing the list of palettes, KisPaletteListWidget is created to replace KisColorsetChooser.
For the search box in the palette docker and the internal color selector, KisPaletteComboBox is created. It syncs with a KisPaletteView assigned to it.
Due the change in API and usage of classes, other classes that use the palettes are also affected. Those classes include KoColorSetWidget, KisDlgInternalColorSelector, KoEditColorSetDialog, KisToolLazyBrushOptionsWidget, and LayerSplit.
KoEditColorSetDialog is removed because its functions can totally be replaced by the palette docker. KoColorSetWidget is planned to be replaced by KisDlgInternalColorSelector. LayerSplit and KisToolLazyBrushOptionsWidget are modified to adapt to the new API and tools.
In order to make it possible to store palettes in .kra, KisDocument is modified. It have a new member called paletteList, a list of pointers to palettes that belong to it. In KoColorSet, toByteArray() and fromByteArray() are implemented so that it can save itself into a buffer and then be transferred and stored into .kra.
Inside the docker, the setCanvas() and unsetCanvas() callbacks manipulates the resources server following the paletteList of the document being opened and closed.
Original .kpl: https://phabricator.kde.org/T4121
An example for the new colorset.xml in the .kpl:
<ColorSet columns="16" comment="" readonly="false" rows="3" version="1.0" name="PALETTE"> <ColorSetEntry bitdepth="U8" id="" spot="false" name="hair bright light"> <RGB g="0.666666686534882" space="sRGB-elle-V2-srgbtrc.icc" r="0.898039221763611" b="0.549019634723663"/> <Position column="0" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="hair 1"> <RGB g="0.313725501298904" space="sRGB-elle-V2-srgbtrc.icc" r="0.501960813999176" b="0.239215686917305"/> <Position column="1" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="hair mid"> <RGB g="0.321568638086319" space="sRGB-elle-V2-srgbtrc.icc" r="0.533333361148834" b="0.250980406999588"/> <Position column="2" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="hair dark"> <RGB g="0.129411771893501" space="sRGB-elle-V2-srgbtrc.icc" r="0.30588236451149" b="0.109803922474384"/> <Position column="3" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="hair 3"> <RGB g="0.211764708161354" space="sRGB-elle-V2-srgbtrc.icc" r="0.380392163991928" b="0.18823529779911"/> <Position column="4" row="0"/> </ColorSetEntry> <Group rows="3" name="skin"> <ColorSetEntry bitdepth="U8" id="" spot="false" name="skin 0"> <RGB g="0.992156863212585" space="sRGB-elle-V2-srgbtrc.icc" r="1" b="0.925490200519562"/> <Position column="0" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="skin 1"> <RGB g="0.949019610881805" space="sRGB-elle-V2-srgbtrc.icc" r="1" b="0.874509811401367"/> <Position column="1" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="skin 2"> <RGB g="0.82745099067688" space="sRGB-elle-V2-srgbtrc.icc" r="0.941176474094391" b="0.796078443527222"/> <Position column="2" row="0"/> </ColorSetEntry> <ColorSetEntry bitdepth="U8" id="" spot="false" name="skin 4"> <RGB g="0.65490198135376" space="sRGB-elle-V2-srgbtrc.icc" r="0.819607853889465" b="0.619607865810394"/> <Position column="3" row="0"/> </ColorSetEntry> </Group> </ColorSet>
Following are the differences from the original .kpl:
The new rows and readonly attribute in the ColorSet tag. rows here means the row number of the main group. This attribute also exists and have the same meaning in the Group tags.
A Position tag now exists for each ColorSetEntry. It decides the position of the swatch defined by the ColorSetEntry tag.
Other Related links