diff --git a/CHANGELOG.md b/CHANGELOG.md index 7272a15..1edfa6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # UI Changelog +## 2023-01-20 + +- Removed "Use selection as mask" option; Using the selection to mask generated images is now default behaviour. +- Added "Add transparency mask" option; Choose to mask generated images by adding transparency mask, or directly flatten/crop image. +- Top paintlayer will now be set as active layer when generation is complete. + ## 2022-12-28 - Added "Alt Dock Behaviour" under "SD Plugin Config". diff --git a/README.md b/README.md index 3942f11..5d02ac8 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,12 @@ A: Unfortunately no, all plugins so far have different APIs. The official API is See [CHANGELOG.md](./CHANGELOG.md) for the full changelog. +### 2023-01-20 + +- Removed "Use selection as mask" option; Using the selection to mask generated images is now default behaviour. +- Added "Add transparency mask" option; Choose to mask generated images by adding transparency mask, or directly flatten/crop image. +- Top paintlayer will now be set as active layer when generation is complete. + ### 2022-12-28 - Added "Alt Dock Behaviour" under "SD Plugin Config". diff --git a/frontends/krita/krita_diff/defaults.py b/frontends/krita/krita_diff/defaults.py index e9df043..fa48c7f 100644 --- a/frontends/krita/krita_diff/defaults.py +++ b/frontends/krita/krita_diff/defaults.py @@ -21,8 +21,7 @@ ETA_REFRESH_INTERVAL = 1000 # 1 second between eta refresh CFG_FOLDER = "krita" # which folder in ~/.config to store config CFG_NAME = "krita_diff_plugin" # name of config file EXT_CFG_NAME = "krita_diff_plugin_scripts" # name of config file -# selection mask can only be added after image is added, so timeout is needed -ADD_MASK_TIMEOUT = 200 +ADD_MASK_TIMEOUT = 50 THREADED = True ROUTE_PREFIX = "/sdapi/interpause/" OFFICIAL_ROUTE_PREFIX = "/sdapi/v1/" diff --git a/frontends/krita/krita_diff/pages/config.py b/frontends/krita/krita_diff/pages/config.py index 429bfe1..ac2aad9 100644 --- a/frontends/krita/krita_diff/pages/config.py +++ b/frontends/krita/krita_diff/pages/config.py @@ -31,7 +31,7 @@ class ConfigPage(QWidget): script.cfg, "just_use_yaml", "(unrecommended) Ignore settings" ) self.create_mask_layer = QCheckBox( - script.cfg, "create_mask_layer", "Use selection as mask" + script.cfg, "create_mask_layer", "Add transparency mask" ) self.save_temp_images = QCheckBox( script.cfg, "save_temp_images", "Save images for debug" diff --git a/frontends/krita/krita_diff/script.py b/frontends/krita/krita_diff/script.py index bccd5f8..ebccbe1 100644 --- a/frontends/krita/krita_diff/script.py +++ b/frontends/krita/krita_diff/script.py @@ -335,22 +335,54 @@ class Script(QObject): def transparency_mask_inserter(self): """Mask out extra regions due to adjust_selection().""" orig_selection = self.selection.duplicate() if self.selection else None + create_mask = self.cfg("create_mask_layer", bool) - def add_mask(layers): - self.doc.waitForDone() - cur_selection = self.selection - cur_layer = self.doc.activeNode() - for layer in layers: - self.doc.setActiveNode(layer) - self.doc.setSelection(orig_selection) - self.app.action("add_new_transparency_mask").trigger() - self.doc.setSelection(cur_selection) # reset to current selection - self.doc.setActiveNode(cur_layer) # reset to current layer + add_mask_action = self.app.action("add_new_transparency_mask") + merge_mask_action = self.app.action("flatten_layer") - def trigger_mask_adding(layers): - if self.cfg("create_mask_layer", bool): - # need timeout to ensure layer exists first else crash - QTimer.singleShot(ADD_MASK_TIMEOUT, lambda: add_mask(layers)) + # This function is recursive to workaround race conditions when calling Krita's actions + def add_mask(layers: list, cur_selection): + if len(layers) < 1: + self.doc.setSelection(cur_selection) # reset to current selection + return + layer = layers.pop() + + orig_visible = layer.visible() + orig_name = layer.name() + + def restore(): + # assume newly flattened layer is active + result = self.doc.activeNode() + result.setVisible(orig_visible) + result.setName(orig_name) + + add_mask(layers, cur_selection) + + layer.setVisible(True) + self.doc.setActiveNode(layer) + self.doc.setSelection(orig_selection) + add_mask_action.trigger() + + if create_mask: + # collapse transparency mask by default + layer.setCollapsed(True) + layer.setVisible(orig_visible) + QTimer.singleShot( + ADD_MASK_TIMEOUT, lambda: add_mask(layers, cur_selection) + ) + else: + # flatten transparency mask into layer + merge_mask_action.trigger() + QTimer.singleShot(ADD_MASK_TIMEOUT, lambda: restore()) + + def trigger_mask_adding(layers: list): + layers = layers[::-1] # causes final active layer to be the top one + + def handle_mask(): + cur_selection = self.selection.duplicate() if self.selection else None + add_mask(layers, cur_selection) + + QTimer.singleShot(ADD_MASK_TIMEOUT, lambda: handle_mask()) return trigger_mask_adding