Skip to content
Snippets Groups Projects

Store task UI geometry in project file

Merged John Tourtellott requested to merge john.tourtellott/smtk:john/task-ui-geometry into task-node
3 unresolved threads

Updates various parts of the task subsystem in order to save and restore the UI node positions when projects are written and read in.

Merge request reports

Merge request pipeline #326027 failed

Merge request pipeline failed for 9ba3cc25

Merged by David ThompsonDavid Thompson 2 years ago (Mar 31, 2023 6:04am UTC)

Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • added 1 commit

    • 5deafbf0 - Add deserialization of UI configuration

    Compare with previous version

  • added 1 commit

    • 72bd4616 - Add #define option to rate-limit nodeMoved signals

    Compare with previous version

  • John Tourtellott marked this merge request as draft

    marked this merge request as draft

  • @dcthomp the current logic is functional but there are issues. We should discuss when you have time.

    1. Klugey: The logic to support multiple UI options includes a "uiLabel" that is stored in the task manager to identify which option is currently in use. Maybe there is a better way to do this?
    2. Serialization: There is an intermittent problem that occurs when saving/reloading the task manager that causes the task dependencies to get scrambled. I don't have a way to reliably repeat the symptom, it just shows up out of the blue sometimes. I don't think the new code in this branch creates the problem, instead it shows up now because I am reading/changing/writing the task manager contents for the first time.
    3. Rate limiting: There is a #define LIMIT_MOVE_SIGNALS item in qtTaskNode that can be used to limit the rate that nodeMoved signals emitted by qtTaskNode. It is current set to the off state because it creates a lag in the corresponding arc updates. It might make sense to have separate signals for (i) updating args at full speed and (ii) updating the project at rate-limited speed.
88 /// Store geometry changes from UI components
89 void setUiLabel(const std::string& label) { m_uiLabel = label; }
90 std::string uiLabel() const { return m_uiLabel; }
91 void uiGeometryChanged(smtk::string::Token taskId, const nlohmann::json& data);
92 void setUiConfig(const std::string& label, smtk::string::Token taskId, const nlohmann::json& j);
93 nlohmann::json uiConfig(smtk::string::Token taskId) const;
94 void dumpUiConfig() const;
95
86 96 private:
87 97 TaskInstances m_taskInstances;
88 98 AdaptorInstances m_adaptorInstances;
89 99 Active m_active;
90 100 std::weak_ptr<smtk::common::Managers> m_managers;
91 101 nlohmann::json m_styles;
102 std::string m_uiLabel; // for storing geometry objects
103 std::map<std::string, std::map<smtk::string::Token, nlohmann::json>> m_uiConfigs;
  • Comment on lines +88 to +103

    Instead of adding lots of API to the task::Manager, add a single class that manages UI state.

    class Manager
    { // ...
      const UIState& uiState() const { return *m_uiState; }
      UIState& uiState() { return *m_uiState; }
    protected:
      std::shared_ptr<UIState> m_uiState;
    };
    
    class UIState
    {
      std::unordered_map<smtk::string::Token, std::unordered_map<smtk::string::Token, nlohmann::json>> m_data;
    };

    As an alternative to tracking node state with every move, consider holding a set of functors that can generate state when it comes time to save tasks:

    class UIStateGenerator
    {
      /// Fetch state not tied to a particular task (e.g. background color).
      virtual nlohmann::json globalState() const = 0;
      /// Fetch state attached to a particular \a task.
      virtual nlohmann::json taskState (const smtk::task::Task& task) const = 0;
    }
    
    class UIState
    {
      std::unordered_map<smtk::string::Token, std::shared_ptr<UIStateGenerator>> m_data;
    };

    Then qtTaskEditor can pass the task-manager's UIState object a custom functor to fetch node positions only when the project is written to disk.

    class TaskEditorState : UIStateGenerator
    {
      TaskEditorState(qtTaskEditor* editor) : m_editor(editor) { }
    
      nlohmann::json globalState() const override { return nlohmann::json(); }
      nlohmann::json taskState(const smtk::task::Task& task) const override
      {
        return m_editor->uiStateForTask(task);
      }
    
    protected:
      qtTaskEditor* m_editor{ nullptr };
    }
    Edited by David Thompson
  • John Tourtellott changed this line in version 4 of the diff

    changed this line in version 4 of the diff

  • Please register or sign in to reply
  • 33 33 #include <QVBoxLayout>
    34 34 #include <QWidget>
    35 35
    36 #include <string>
    37
    38 namespace
    39 {
    40 // String constant used to designate UI features in serialized task manager.
    41 const std::string JSON_LABEL = "qtTaskEditor";
  • 83 100 }
    84 101
    85 void computeNodeLayout()
    102 // Returns true if UI configuration was modified
    103 bool computeNodeLayout()
    86 104 {
    105 if (m_taskIndex.empty())
    106 {
    107 return false;
    108 }
    109
    110 // Check if taskManager has UI config objects first
    111 bool configured = false;
    112 auto* firstTask = m_taskIndex.begin()->first;
    113 auto* taskManager = firstTask->manager();
    114 taskManager->setUiLabel(JSON_LABEL);
  • Mostly fyi I started down the second approach (TaskEditorState) but will be switching to the first one (encapsulating UIState in smtk::task).

    I didn't think about this beforehand, but in terms of reducing the API impact to smtk::task::Manaager, I ran into a chicken & egg problem using the TaskEditorState approach. In brief, I can't instantiate a TaskEditorState until the qtTaskEditor is created, which doesn't occur until after the project has been loaded. So I need some object to store the UI state when the project is read in in order to provide that to the task editor when it is ready.

  • John Tourtellott added 4 commits

    added 4 commits

    • 2274a0e5 - Implement separate signals rate-limited nodeMoved and nodeMovedImmediate
    • f19d306e - Add UIState class to encapsulate UI features in smtk::task
    • c2fd692d - Add logic to write node positions to project file
    • 574d85bc - Add code to check if node position has changed from initial position

    Compare with previous version

  • @dcthomp This "later & greater" version is functional, though I should do more testing this afternoon.

    • I'm also going to review the code this afternoon, but wanted to get this online asap.
    • UIState class has separate maps for (i) data read from file and (ii) generators for writing current geometry to file.
    • Had a problem with the code always marking a project modified when loaded, whether or not code did automatic layout. I presume it has to do with when the nodeModified signals get issued on first placement of each node. Resolved in qtTaskEditor::onNodeGeometryChanged() by comparing current geom with original geom loaded from file (if any).
    • I'm also still seeing the intermittent problem on file read that seems to mess up dependencies. Still cannot reproduce reliably. (so beware :)
    Edited by John Tourtellott
  • added 1 commit

    Compare with previous version

  • added 1 commit

    Compare with previous version

  • David Thompson marked this merge request as ready

    marked this merge request as ready

  • I have some things I want to change, but will add them after merging.

    Do: merge

  • David Thompson mentioned in commit 119f3395

    mentioned in commit 119f3395

  • Please register or sign in to reply
    Loading