NSBrowser / NSTreeController with async data - cocoa

I have a NSBrowser that needs to display data that comes from a REST API. Sometimes this API takes a while to return results, so it would be nice to handle the network traffic in the background (lazy fetching). Sometimes a subtree needs to be refresh to reflect server side changes.
I've tried two different approaches:
Using NSBrowserDelegate. When [browserDelegate browser:child:ofItem:] or similar function ends up requesting data that is not yet loaded, I return 0 count and kick off background processing. When the data becomes available I have the columns reloaded (not very efficient, messes with the user's selection as new data comes in).
Using NSTreeController. I got this to work well using a blocking/synchronous approach. However, whenever I update the model, NSBrowser resets the tree and moves the selection up to the parent. I've tried [obj mutableArrayValueForKey] approach. I've tried the [treecontroller insertObject:atArrangedObjectIndexPath:] approach. I've toggled "preserve selections" flag. I've toggled "select on insert". No matter what I do, NSBrowser doesn't want to cleanly update only the relevant subtree.
Bottom line: what is the best practice for asynchronously loading data into an NSBrowser?

Related

How to prevent content jumping/scrolling in Angular 5 with NGRX

Let's say I have a long page with a table (or to be correct with a set of containers that contain tables) and there is a lot of data in these tables, so that it doesn't fit into a screen and therefore it is possible to scroll the page with scrollbars.
Now I have a checkbox in each row of the table so that I can select some rows and perform some actions with them.
These checkboxes are tied to the state so that when I check one of them, the state gets updated and entire page gets re-rendered. If I understand correctly, this leads to unexpected content scrolling (jumping).
So my question is: is there any common approach to avoid this unexpected behavior? I'm thinking of separating user interactions into a separate state, but it feels like a reinventing the wheel a bit. Any other ideas or standard approaches?
#Simon_Weaver, #user2216584 your comments were correct: just need to add trackBy to prevent Angular from redrawing containers

Removing redo actions in NSUndoManager

I am facing an architecture question and I am wondering if anyone know if my idea is feasible or has a better architecture idea.
My situation is that I have CoreData data model for tracking some financial data, imagine it is bank accounts (it's not so don't worry about the security thing). Data for the core data objects is mainly user input.
I know well how to make user input screens (implemented as sheets on top of the main window) and how to implement undo for these input sheets. For simple sheets like edit account i simply copy data from the relevant core data object to the sheet's controller and write back on OK-Close. The write back to the CoreData object is wrapped in an undo grouping which gives me a single undoable action to restore the state to before the edit. This works fine.
Now I am contemplating a more complex edit control where the transactions on the account would be shown in an editable grid (like excel) and it would be possible to edit, add or remove actions. For this control it seem difficult to copy all data to a new data structure. Instead I was thinking of using the core data store as the data source directly, meaning core data objects would change, be created and deleted as the user edit rows in the control. Within this control i then know how to get step by step undo.
The issue comes upon end of the edit session if the user then clicks cancel. I figure to handle this instance I could wrap all actions in the control into one undo group and undo that.
The issue is that after this the canceled actions are then available as a redo on the redo stack. This is cleared as soon as the user does another undoable action but I would like to ensure that this redo action is never recorded at all.
A work around I have thought about is to do some (no-op) action that is undoable after the cancel to clear the redo stack, but then this no-op shows up in the undo stack instead which is also undesirable.
Basically what I'd like to do is to do some form of core data rollback to the state before the complex edit sheet is activated without this rollback being re-doable. The edit sheet is modal and it can be guaranteed that no other changes will happen in the core data model while the edit sheet is active.
Also worth knowing may be that the core data context is not guaranteed to be saved before the edit sheet is activated so simply throwing away the context and reloading from persistent store is not an option. (And would also lose previous undo history as well which I do not want)
The simple question is then, how do I clear the redo stack of the core data NSUndoManager? More generally does anyone know if this is the right solution to my problem or where should I look for a better design solution?

Hiding Checkboxes with a Tri-State TreeView control

I have a project that I'm working on that has a TreeView Control in it. For everything underneath Level 1, those nodes are being used as a bookmark function, so we want a Tri-State CheckBox there. I found a control that I'm using here.
However, as the top two levels in the TreeView do not need bookmarking, the customer wants an Image there. I found this page who's response gets CheckBoxes to disappear on specific Nodes. Unfortunately, the API calls clash with the ones used in the above Tri-State TreeView and they both can't work at the same time.
Basically, what I'm looking for is how to get Images in nodes on the top two levels, and Tri-State CheckBoxes on the lower levels. I'd be more than willing to try another Tri-State CheckBox if necessary.
Also, when the user clicks on the Image in the top two levels, the image shouldn't change. I found this page which does keep the image from changing, but removes the PlusMinus, Lines, and RootLines from the control.
Sorry in advance that this is such a ridiculous and specific request.
Ok, I finally got this figured out. Using the Tri-State Treeview Control, I am able to still have access to the AfterClick event on the Treeview.
I had added a series of Checkbox icons to the ImageList, and after determining which CheckState (of the 3) my underlying object was in. Then I set the ImageIndex, SelectedImageIndex, and StateImageIndex (probably don't need the last one, but I was being thorough) to the proper index of the ImageList.
I won't post any code for now, but if you are trying to do the same thing, let me know and I'll upload until it becomes clear.

Core Data: trigger a long-running operation on change of an attribute

I have in my model an object, that when modified requires a large number of other objects to recompute values based on those changes.
The way this is currently set up, is that this one object can only be modified in one place. This is a sheet with a Cancel and an OK button. Once the user commits the change, the sheet shows a progress bar and starts processing the objects affected by the change. The presentation and dismissal of the sheet are wrapped in a NSUndoManager group. The user may undo all changes in one pass after dismissing the sheet.
What bothers me is that I keep thinking that all this should happen at the business level. Rather than at the controller level. I.e. I should be able to modify my business object any place in the UI and code and have it trigger the necessary computations.
So I would set up KVO to watch my object and trigger the long running operation when needed. Once I go down that path, I start hitting walls.
How do I coalesce changes? My object has several attributes. I don't want to start a computation when the first attribute is changed and the second is likely to change next. Basically I need an edit sheet and some control point to commit all changes at once.
How do I add a UI to this long running operation? I could have an NSOperationQueue attached to the NSManagedObjectContext and have my window controller observe that. When the queue is not empty, I would pop up a sheet with a progress bar monitoring the current operation.
How can I implement Undo/Redo support? If I delay recomputation to an operation running after the fact, I cannot imagine how to undo the initial change and the propagated once at the same time. I can only imagine undoing the original change and having that trigger another reevaluation of all other object.
In short:
What is the best practice for such dependancies?
Is the propagation a job for the model layer or the control layer?
I believe I came up with a solution:
the center-piece model object watches itself for changes
on change, it creates or amends a ToDo object
the controller watches for new ToDo objects
the controller dequeues the ToDo, presents a progress-bar and performs the operation

Tetris and pretty graphics

Say you're building a Tetris game. As any proper programmer, you have your view logic on one side, and your business logic on the other side; probably a full-on MVC going on.
When the model sends its update(), the view redraws itself, as expected.
But then... if you wanted to add, say, an animation to vanish a line, how would you implement that in the view?
Make any assumptions you want---excepting that "Everything is properly encapsulated".
Personally, I would separate draw the screen as often as possible, even if there was no update of the block position. So I would have a loop somewhere with an "update" and a "render" part. Update plays the ball to the logic which does or does not any update of positions and/or block removal. Render plays the ball to the graphics part, which draws the blocks where they should be.
Now if there are lines to erase, the logic knows and can mark those lines to be removed. I assume here, that every piece consists of 4 single blocks and any of these blocks is a single object. Now when this block has the "die"-flag set, you may take some render-parts to vanish the block (let's say, 500ms to explode). After this time, the object may be disposed and the block a line above falls down. Why 500ms? Well, you should definitely use time-based movement as this keeps the game speed the same on different computers.
Btw, there are already so called game engines which provide such an update-render-loop. For example XNA, if you go the .NET line. You may also code your own engine but beware, it's not an easy task and it's very time consuming. I did this once and don't expect it to be an engine like the Source Engine ;-)
Most games execute a loop that constantly redraws the view of the game as fast as possible, rather than waiting for a change in the model state and then refreshing the view.
If you like the model view pattern, then it might work well for the view to continue to draw some types of objects after they are removed from the model, fading them out over a few milliseconds.
Another approach would be to combine class MVC with something like differential execution - the 'view' is a model of what is presented, but the drawing code compares the stream of events the 'view' creates with the stream from the previous rendering. So if in one stream there's a line, and the next there isn't, the drawing code can animate the difference. This allows the drawing to be abstracted away from the view . Frequently the 'view' in MVC is a collection of widgets, rather than being something which draws the display directly, so you end up with nested MVC hierarchies anyway: the application is MVC ( data model, view objects, app controller ), where the view object has a collection of widgets each of which is MVC ( widget state (eg button pressed ), look and feel/toolkit binding, mapping of toolkit events -> widget state ).
I've often wondered this myself.
My own thoughts have been along this line:
1) The view is given the state of the blocks (shape, yada-yada), but with extra "transitional" data:
2) The fact that a line must be removed is encoded in the state, NOT computed in the view.
3) The view knows how to draw transitions now:
No change: state is the same for this particular block
Change from "falling" to "locked": state is "locked in" (by a dropping block)
Change from "locked" to "remove": state is "removed" (by a line completion)
Change from "falling" to "remove": state is "removed", but old state was "falling"
Its interesting to think of a game as an MVC. Thats a perspective I've never taken (for some odd reason), but definitely an intriguing one that makes a lot of sense. Assuming you do implement your Tetris game with an MVC, I think there are two things you might want to take into account in regards to communication between your controller and your view: There is state, and there are events.
Your controller is obviously the central point of interaction for the user. When they issue keyboard commands, your controller will interpret them, and make the appropriate state adjustments. However, sometimes the game will enter a state that coincides with a particular event...such as filling a line with blocks that should now be removed.
Scoregraphic has given you a great foundation. Your view should operate on a fixed cycle to maintain consistent speed across computers. But in addition to updating the screen to render new state, it should also have a queue of events that it can perform animations in response to. In the case of filling lines in Tetris, your controller could issue strongly typed event objects that derive from some kind of base event type into the view event queue, which could then be used by the view to perform the appropriate animated responses.

Categories

Resources