diff --git a/pyblish_qml/control.py b/pyblish_qml/control.py index eb1f20c1..ca8a5586 100644 --- a/pyblish_qml/control.py +++ b/pyblish_qml/control.py @@ -46,8 +46,8 @@ class Controller(QtCore.QObject): saved = QtCore.Signal() finished = QtCore.Signal() initialised = QtCore.Signal() - commented = QtCore.Signal() - commenting = QtCore.Signal(str, arguments=["comment"]) + commented = QtCore.Signal(str, arguments=["name"]) + commenting = QtCore.Signal(str, str, arguments=["comment", "name"]) state_changed = QtCore.Signal(str, arguments=["state"]) @@ -75,6 +75,7 @@ def __init__(self, host, parent=None, targets=None): "result": models.ResultModel(), }, "comment": "", + "instancesComment": dict(), "firstRun": True, } @@ -293,19 +294,18 @@ def setup_statemachine(self): machine.start() return machine - @QtCore.Slot(result=str) - def comment(self): + @QtCore.Slot(str, result=str) + def comment(self, name): """Return first line of comment""" - return self.data["comment"] + if name == "Context": + return self.data["comment"] + else: + return self.data["instancesComment"].get(name, "") @QtCore.Property(str, notify=state_changed) def state(self): return self.data["state"]["current"] - @QtCore.Property(bool, notify=commented) - def hasComment(self): - return True if self.data["comment"] else False - @QtCore.Property(bool, constant=True) def commentEnabled(self): return "comment" in self.host.cached_context.data @@ -665,10 +665,17 @@ def echo(self, data): """Append `data` to result model""" self.data["models"]["result"].add_item(data) - def comment_sync(self, comment): + def comment_sync(self, comment, item): """Update comments to host and notify subscribers""" - self.host.update(key="comment", value=comment) - self.host.emit("commented", comment=comment) + name = item.name + model_item = self.data["models"]["item"].instances[item.id] + model_item.hasComment = bool(comment) + + self.host.update(key="comment", value=comment, name=name) + if name == "Context": + self.host.emit("commented", comment=comment) + else: + self.host.emit("instanceCommented", comment=comment, name=name) def is_ready(self): count = self.data["state"]["readyCount"] @@ -677,18 +684,26 @@ def is_ready(self): # Event handlers - def on_commenting(self, comment): + def on_commenting(self, comment, name): """The user is entering a comment""" def update(): context = self.host.cached_context - context.data["comment"] = comment - self.data["comment"] = comment - # Notify subscribers of the comment - self.comment_sync(comment) + if name == "Context": + context.data["comment"] = comment + self.data["comment"] = comment + item = context - self.commented.emit() + else: + instance = next(it for it in context if name == it.name) + instance.data["comment"] = comment + self.data["instancesComment"][name] = comment + item = instance + + # Notify subscribers of the comment + self.comment_sync(comment, item) + self.commented.emit(name) # Update local cache a little later util.schedule(update, 100, channel="commenting") @@ -815,17 +830,27 @@ def on_finished(plugins, context): self.data["models"]["item"].update_compatibility() # Remember comment across resets + # comment = self.data["comment"] - if comment: + instances_com = self.data["instancesComment"] + ch_context = self.host.cached_context + + if comment or instances_com: print("Installing local comment..") - self.host.cached_context.data["comment"] = comment + ch_context.data["comment"] = comment + for ch_it in ch_context: + ch_it.data["comment"] = instances_com.get(ch_it.name, "") else: print("No local comment, reading from context..") - comment = self.host.cached_context.data.get("comment", "") - self.data["comment"] = comment + self.data["comment"] = ch_context.data.get("comment", "") + for ch_it in ch_context: + instances_com[ch_it.name] = ch_it.data.get("comment", "") # Notify subscribers of the comment - self.comment_sync(comment) + self.comment_sync(comment, ch_context) + for ch_it in ch_context: + ch_it_com = instances_com.get(ch_it.name, "") + self.comment_sync(ch_it_com, ch_it) first_run = self.data["firstRun"] if first_run: diff --git a/pyblish_qml/ipc/client.py b/pyblish_qml/ipc/client.py index 8a908911..07b966d7 100644 --- a/pyblish_qml/ipc/client.py +++ b/pyblish_qml/ipc/client.py @@ -92,8 +92,8 @@ def discover(self): def emit(self, signal, **kwargs): self._dispatch("emit", args=[signal, kwargs]) - def update(self, key, value): - self._dispatch("update", args=[key, value]) + def update(self, key, value, name): + self._dispatch("update", args=[key, value, name]) def _self_destruct(self): """Auto quit exec if parent process failed diff --git a/pyblish_qml/ipc/service.py b/pyblish_qml/ipc/service.py index 168f4829..5c53ea0c 100644 --- a/pyblish_qml/ipc/service.py +++ b/pyblish_qml/ipc/service.py @@ -141,9 +141,14 @@ def emit(self, signal, kwargs): pyblish.api.emit(signal, **kwargs) - def update(self, key, value): + def update(self, key, value, name): """Write data to context from GUI""" - self._context.data[key] = value + context = self._context + if name == "Context": + context.data[key] = value + else: + instance = next(it for it in context if name == it.name) + instance.data[key] = value class MockService(Service): diff --git a/pyblish_qml/models.py b/pyblish_qml/models.py index 98d3d205..2092a89e 100644 --- a/pyblish_qml/models.py +++ b/pyblish_qml/models.py @@ -53,6 +53,7 @@ "category": None, "niceName": "default", "compatiblePlugins": list(), + "hasComment": False, }, "result": { "type": "default", diff --git a/pyblish_qml/qml/Footer.qml b/pyblish_qml/qml/Footer.qml index 28438061..1d9ff53d 100644 --- a/pyblish_qml/qml/Footer.qml +++ b/pyblish_qml/qml/Footer.qml @@ -10,6 +10,7 @@ View { // 0 = Default; 1 = Publishing; 2 = Finished property int mode: 0 property bool paused: false + property bool hasComment: false signal publish signal validate @@ -47,7 +48,7 @@ View { tooltip: visible ? "Comment.." : "" - name: app.hasComment ? "comment" : "comment-o" + name: hasComment ? "comment" : "comment-o" onClicked: footer.comment() visible: mode == 0 ? true : false } diff --git a/pyblish_qml/qml/List.qml b/pyblish_qml/qml/List.qml index 19b677aa..5038ca8d 100644 --- a/pyblish_qml/qml/List.qml +++ b/pyblish_qml/qml/List.qml @@ -69,6 +69,14 @@ ListView { : Theme.dark.successColor }, + Action { + name: "comment" + iconName: object.hasComment ? "comment" : "comment-o" + iconSize: 12 + enabled: object.itemType == "instance" ? true : false + onTriggered: actionTriggered(this, index) + }, + Action { name: "enter" iconName: "angle-right" diff --git a/pyblish_qml/qml/Overview.qml b/pyblish_qml/qml/Overview.qml index 175f52d9..b0494564 100644 --- a/pyblish_qml/qml/Overview.qml +++ b/pyblish_qml/qml/Overview.qml @@ -18,6 +18,7 @@ Item { property bool validated: false + signal commentEntered(int index) signal instanceEntered(int index) signal pluginEntered(int index) @@ -45,6 +46,11 @@ Item { footer.message.animation.start() } + function setComment(text) { + app.commenting(text, "Context") + footer.hasComment = text ? true : false + } + TabBar { id: tabBar @@ -106,6 +112,8 @@ Item { app.repairInstance(index) else if (action.name == "enter") overview.instanceEntered(index) + else if (action.name == "comment") + overview.commentEntered(index) } onItemToggled: app.toggleInstance(index) @@ -197,10 +205,12 @@ Item { bottom: footer.top left: parent.left right: parent.right - top: (isMaximised && height == parent.height - footer.height) ? tabBar.top : undefined + top: undefined } - height: isMaximised ? parent.height - footer.height : isUp ? 150 : 0 + height: isUp ? 150 : 0 + + onCommentChanged: setComment(text) } Footer { @@ -229,7 +239,13 @@ Item { onFirstRun: { app.commentEnabled ? commentBox.up() : null - commentBox.text = app.comment() + commentBox.text = app.comment("Context") + } + + onCommented: { + if (name == "Context") { + commentBox.text = app.comment(name) + } } onStateChanged: { diff --git a/pyblish_qml/qml/Perspective/Footer.qml b/pyblish_qml/qml/Perspective/Footer.qml new file mode 100644 index 00000000..31b8bd75 --- /dev/null +++ b/pyblish_qml/qml/Perspective/Footer.qml @@ -0,0 +1,49 @@ +import QtQuick 2.3 +import Pyblish 0.1 + + +View { + id: footer + + property alias message: __message + + // 0 = Default; 1 = Publishing; 2 = Finished + property int mode: 0 + property bool hasComment: false + + signal comment + + width: 200 + height: 40 + + Message { + id: __message + anchors.verticalCenter: parent.verticalCenter + } + + Row { + id: row + + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + margins: 5 + } + + spacing: 3 + + AwesomeButton { + elevation: 1 + + size: 25 + iconSize: 14 + + tooltip: visible ? "Comment.." : "" + + name: hasComment ? "comment" : "comment-o" + onClicked: footer.comment() + visible: mode == 0 ? true : false + } + } +} \ No newline at end of file diff --git a/pyblish_qml/qml/Perspective/Page.qml b/pyblish_qml/qml/Perspective/Page.qml index a6dff53e..8074fdae 100644 --- a/pyblish_qml/qml/Perspective/Page.qml +++ b/pyblish_qml/qml/Perspective/Page.qml @@ -8,6 +8,33 @@ Item { id: root property QtObject item + property QtObject overview + property bool commenting: false + + function setComment(text) { + if (overview.state == "") { + app.commenting(text, item.name) + footer.hasComment = text ? true : false + } + } + + function getComment() { + // GUI may freeze if querying comment while publishing + // - note, this triggers commentChanged signal + if (overview.state == "publishing") { + return "publishing, could not edit/read comment at this moment.." + } + else { + return app.comment(item.name) + } + } + + function restoreComment() { + // Ensure comment restored from the above placeholder text + if (overview.state == "finished") { + commentBox.text = app.comment(item.name) + } + } ActionBar { id: actionBar @@ -35,7 +62,7 @@ Item { left: parent.left top: actionBar.bottom right: parent.right - bottom: parent.bottom + bottom: item.itemType == "instance" ? commentBox.top : parent.bottom topMargin: -1 } @@ -126,6 +153,47 @@ Item { } } + CommentBox { + id: commentBox + + visible: item.itemType == "instance" + + //Enable editing only when the GUI is not busy with something else + readOnly: overview.state != "" + text: item.itemType == "instance" ? getComment() : "" + + isUp: root.commenting + + anchors { + bottom: footer.top + left: parent.left + right: parent.right + top: undefined + } + + height: isUp ? 150 : 0 + + onCommentChanged: setComment(text) + + Connections { + target: app + onStateChanged: restoreComment() + } + } + + Footer { + id: footer + + visible: item.itemType == "instance" ? overview.state != "initialising": false + + mode: overview.state == "publishing" ? 1 : overview.state == "finished" ? 2 : 0 + + width: parent.width + anchors.bottom: parent.bottom + + onComment: commentBox.height > 75 ? commentBox.down() : commentBox.up() + } + Binding { target: body.item property: "item" diff --git a/pyblish_qml/qml/CommentBox.qml b/pyblish_qml/qml/Pyblish/CommentBox.qml similarity index 91% rename from pyblish_qml/qml/CommentBox.qml rename to pyblish_qml/qml/Pyblish/CommentBox.qml index 075d5ff2..9f623241 100644 --- a/pyblish_qml/qml/CommentBox.qml +++ b/pyblish_qml/qml/Pyblish/CommentBox.qml @@ -1,7 +1,6 @@ import QtQuick 2.6 import QtQuick.Layouts 1.1 -import Pyblish 0.1 Rectangle { id: root @@ -11,12 +10,13 @@ Rectangle { color: Theme.backgroundColor property bool isUp - property bool isMaximised property alias text: textBox.text property var readOnly: false + signal commentChanged + Behavior on height { NumberAnimation { duration: 500 @@ -31,7 +31,6 @@ Rectangle { function down() { isUp = false - isMaximised = false } function toggle() { @@ -68,7 +67,7 @@ Rectangle { KeyNavigation.priority: KeyNavigation.BeforeItem - onTextChanged: app.commenting(text) + onTextChanged: root.commentChanged() } } } diff --git a/pyblish_qml/qml/Message.qml b/pyblish_qml/qml/Pyblish/Message.qml similarity index 100% rename from pyblish_qml/qml/Message.qml rename to pyblish_qml/qml/Pyblish/Message.qml diff --git a/pyblish_qml/qml/Pyblish/qmldir b/pyblish_qml/qml/Pyblish/qmldir index 49d4c8f4..71fc7f35 100644 --- a/pyblish_qml/qml/Pyblish/qmldir +++ b/pyblish_qml/qml/Pyblish/qmldir @@ -9,10 +9,12 @@ AwesomeIcon 0.1 AwesomeIcon.qml AwesomeButton 0.1 AwesomeButton.qml Button 0.1 Button.qml CheckBox 0.1 CheckBox.qml +CommentBox 0.1 CommentBox.qml Ink 0.1 Ink.qml Icon 0.1 Icon.qml IconButton 0.1 IconButton.qml Label 0.1 Label.qml +Message 0.1 Message.qml Object 0.1 Object.qml ProgressBar 0.1 ProgressBar.qml SpinBox 0.1 SpinBox.qml diff --git a/pyblish_qml/qml/app.qml b/pyblish_qml/qml/app.qml index 464401bd..8c14846c 100644 --- a/pyblish_qml/qml/app.qml +++ b/pyblish_qml/qml/app.qml @@ -20,7 +20,7 @@ StackView { * Format relevant proxy-models to display information * relevant to the currently entered item. */ - function setup(item) { + function setup(item, commenting) { app.recordProxy.clear_inclusion() app.recordProxy.add_inclusion("type", "record") app.recordProxy.add_inclusion(item.itemType, item.name) @@ -35,16 +35,22 @@ StackView { stack.push({ item: perspective, - properties: {"item": item} + properties: { + "item": item, + "overview": overview, + "commenting": commenting, + } }) } initialItem: Overview { + id: overview width: stack.width height: stack.height - onInstanceEntered: setup(app.instanceProxy.item(index)) - onPluginEntered: setup(app.pluginProxy.item(index)) + onCommentEntered: setup(app.instanceProxy.item(index), true) + onInstanceEntered: setup(app.instanceProxy.item(index), false) + onPluginEntered: setup(app.pluginProxy.item(index), false) } Component { diff --git a/pyblish_qml/qml/delegates/RecordDelegate.qml b/pyblish_qml/qml/delegates/RecordDelegate.qml index 8fca0082..d35dc1c9 100644 --- a/pyblish_qml/qml/delegates/RecordDelegate.qml +++ b/pyblish_qml/qml/delegates/RecordDelegate.qml @@ -11,29 +11,29 @@ BaseDelegate { expandable: true property var levels: { - "DEBUG": { + "debug": { "color": Qt.lighter("steelblue", 1.3), "icon": "log-debug-16x16" }, - "INFO": { + "info": { "color": Qt.lighter("steelblue", 1.5), "icon": "log-info-16x16" }, - "WARNING": { + "warning": { "color": Qt.lighter("red", 1.6), "icon": "log-warning-16x16" }, - "ERROR": { + "error": { "color": Qt.lighter("red", 1.4), "icon": "log-error-16x16" }, - "CRITICAL": { + "critical": { "color": Qt.lighter("red", 1.2), "icon": "log-critical-16x16" } } - color: levels[object.levelname].color + color: levels[object.levelname.toLowerCase()].color body: Row { property alias icon: mask.name @@ -42,7 +42,7 @@ BaseDelegate { Icon { id: mask - name: levels[object.levelname].icon + name: levels[object.levelname.toLowerCase()].icon } Column { diff --git a/pyblish_qml/version.py b/pyblish_qml/version.py index d0154edd..4dc455a6 100644 --- a/pyblish_qml/version.py +++ b/pyblish_qml/version.py @@ -1,7 +1,7 @@ VERSION_MAJOR = 1 -VERSION_MINOR = 11 -VERSION_PATCH = 5 +VERSION_MINOR = 12 +VERSION_PATCH = 0 version_info = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) version = '%i.%i.%i' % version_info