CodeEdit icon indicating copy to clipboard operation
CodeEdit copied to clipboard

✨ Remember Workspace State Per Window

Open austincondiff opened this issue 2 years ago • 15 comments

Describe the solution you'd like

We need to persist workspace state per window after the user quits and opens CodeEdit again.

We will need to also remember

  • [x] editor layout
  • [x] for each editor
    • [x] size (width/height where applicable)
    • [x] which tabs/files are open
    • [x] which tab is active
    • [x] order of tabs
  • [ ] the navigator, inspector, and utility areas
    • [ ] size (width/height respectively)
      • [x] navigator area
      • [ ] inspector area
      • [ ] utility area
    • [x] opened/closed state
      • [x] navigator area
      • [x] inspector area
      • [x] utility area
    • [ ] which tab is active
      • [ ] navigator area
      • [ ] inspector area
      • [ ] utility area
    • [ ] order of tabs
      • [ ] navigator area
      • [ ] inspector area
      • [ ] utility area

Related

  • #904
  • #882

austincondiff avatar Jan 10 '23 20:01 austincondiff

@Code-DJ would you be able to look at this?

austincondiff avatar Jan 10 '23 20:01 austincondiff

As this is a View-related problem, I'd suggest looking into using SceneStorage instead of storing this state in the NSWindow instance.

Wouter01 avatar Jan 10 '23 20:01 Wouter01

@Code-DJ would you be able to look at this?

Yes I can take a stab at it. I assume, right now, Workspace = Open Folder, so all the settings need to saved by an identifier = "path of the folder".

Note: I am new to Apple API - so Wouter01 pointing to SceneStorage already helps.

Code-DJ avatar Jan 10 '23 21:01 Code-DJ

@Code-DJ would you be able to look at this?

Yes I can take a stab at it. I assume, right now, Workspace = Open Folder, so all the settings need to saved by an identifier = "path of the folder".

Note: I am new to Apple API - so Wouter01 pointing to SceneStorage already helps.

That's indeed a good idea. I'd split it up in a few SceneStorage variables. For example, You could save the workspace navigator state as @SceneStorage("navigatorFolder\(url.path())") var isOpened: Bool. This way, if another SceneStorage variable will depend on the URL, there won't be conflicts

Wouter01 avatar Jan 10 '23 21:01 Wouter01

That is a good question, should we use the path as the workspace identifier? What if the user renames a parent directory? What if we decide later down the road (like VS Code) that we want to support multiple folders per workspace.

This can be a later discussion and we can use the path as the identifier for the time being but I did want to point that out.

austincondiff avatar Jan 10 '23 21:01 austincondiff

That is a good question, should we use the path as the workspace identifier? What if the user renames a parent directory? What if we decide later down the road (like VS Code) that we want to support multiple folders per workspace.

This can be a later discussion and we can use the path as the identifier for the time being but I did want to point that out.

We can assume the user doesn't rename it. If he/she does, the window frames will be reset, but it's not a huge deal. I think the project won't even appear in the recent items list anymore. You don't really have another way of doing it.

I'd guess if you want multiple folders in a workspace, you'd need to create a workspace with the parent folder of both.

Wouter01 avatar Jan 10 '23 22:01 Wouter01

Here are my thoughts:

  • The key for SceneStorage will be the "path to the workspace folder" - Is there still uncertainty about this?
  • save all settings in Dictionary<String, Any> in WorkspaceDocument.
  • add two new methods to WorkspaceDocument - writeToSceneStorage, readFromSceneStorage need better names, this will allow views e.g. InspectorSidebar to read on init and write on change.
  • restore values from SceneStorage in WorkspaceDocument.initWorkspaceState.
  • save the dictionary to SceneStorage in a new method WorkspaceDocument.saveWorkspaceState.
  • call WorkspaceDocument.saveWorkspaceState from WorkspaceDocument.write.

In addition to the above, do we need to periodically save state (in case the user keeps CodeEdit open for days)?

Code-DJ avatar Jan 11 '23 16:01 Code-DJ

Is it somehow possible to bind everything to this state? The state should be saved when anything changes.

austincondiff avatar Jan 11 '23 16:01 austincondiff

It looks like SceneStorage automatically does the saving and restoring of the state - I am not sure when it does that - for details see the link below.

Based on this, it appears we don't need to do any of initWorkspaceState and saveWorkspaceState stuff I mentioned above.

If we prefix the dictionary variable with @SceneStorage(url) var viewState = Dictionary<String, Any>(), it may just work. @Wouter01 any thoughts?

Taken from SceneStorage Example

struct DevTechieSceneStorageExample: View {
    @SceneStorage("name") var name = ""
    @SceneStorage("email")  var email = ""
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Personal Information")) {
                    TextField("Enter name", text: $name)
                    TextField("Enter email", text: $email)
                }
            }
            .navigationTitle("DevTechie")
        }
    }
}

Code-DJ avatar Jan 11 '23 16:01 Code-DJ

Maybe we save on window/app close then?

austincondiff avatar Jan 11 '23 17:01 austincondiff

@austincondiff I didn't realize this was ready for me. Can you assign it to me. Thanks!

Code-DJ avatar Jan 15 '23 04:01 Code-DJ

Here are my thoughts:

  • The key for SceneStorage will be the "path to the workspace folder" - Is there still uncertainty about this?

  • save all settings in Dictionary<String, Any> in WorkspaceDocument.

  • add two new methods to WorkspaceDocument - writeToSceneStorage, readFromSceneStorage need better names, this will allow views e.g. InspectorSidebar to read on init and write on change.

  • restore values from SceneStorage in WorkspaceDocument.initWorkspaceState.

  • save the dictionary to SceneStorage in a new method WorkspaceDocument.saveWorkspaceState.

  • call WorkspaceDocument.saveWorkspaceState from WorkspaceDocument.write.

In addition to the above, do we need to periodically save state (in case the user keeps CodeEdit open for days)?

Apologies for the late response, I must've missed your mention. Regarding SceneStorage, it's meant to be used in SwiftUI views only. On second thought, this may not play well with the current state of the app. I don't know if there's an appkit alternative, or if you had figured it out already?

What I can tell though, in my test build with the SwiftUI lifecycle, lots of this is done automatically, liking saving the sidebar and inspector width. Other things, like the state of the file navigator, won't be saved automatically, as these are done in AppKit. Maybe it's worth a try looking into those first?

Wouter01 avatar Jan 15 '23 05:01 Wouter01

Is it somehow possible to bind everything to this state? The state should be saved when anything changes.

You can see SceneStorage as a variant of AppStorage, except it's done for a window instance instead of the whole app. SceneStorage will load the precious data in automatically, and will save new data automatically. No need to worry about when to save it.

Wouter01 avatar Jan 15 '23 05:01 Wouter01

Important to notice:

If the Scene is explicitly destroyed (e.g. the switcher snapshot is destroyed on iPadOS or the window is closed on macOS), the data is also destroyed.

from: https://developer.apple.com/documentation/swiftui/scenestorage

lukepistrol avatar Jan 15 '23 11:01 lukepistrol