mirror of
https://github.com/rivo/tview.git
synced 2026-04-26 13:25:51 +03:00
[GH-ISSUE #213] How to insert or reorder rows of a List or Table #168
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/tview#168
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @ardnew on GitHub (Dec 23, 2018).
Original GitHub issue: https://github.com/rivo/tview/issues/213
I'm trying to implement a navigable sorted list of specific files that exist on a file system. Being a file system, there can be tens or thousands of these files. But the real problem arises from the fact that the files may be added or removed at runtime (external from this application) after the list has been constructed and displayed to the user.
Assuming these file system changes can be detected reliably, which widget or primitive would you suggest to handle reordering the rows of data?
Both the
ListandTabledo not support inserting rows at arbitrary locations, nor do they support moving rows. This forces the application to basically reconstruct the table entirely if an item needs to be inserted near the beginning of the list.For a
List, inserting an item near the beginning would amount to a singleAddItem()followed by aSetItemText(i+1, GetItemText(i))for all items following the new item. Luckily I'm not using the shortcut rune feature, as that appears impossible to reassign.I believe
Tablewould be similar, but with the added effort of having to re-populate each column for each row being moved.I haven't actually attempted implementing either of these approaches yet, and I was hoping you might have some insight into which method might perform better (considering how many rows there could be).
Alternatively, maybe you can suggest a better way to accomplish this?
Also, I guess I should request an
insertRow()feature be added to either or both ofListandTableso that client applications don't have to juggle all of the items manually.@rivo commented on GitHub (Dec 26, 2018):
This requirement makes sense. I think the signatures for
ListandTablewould be different, though. I would suggest the following:List.InsertItem(row int, mainText, secondaryText string, shortcut rune, selected func()) *ListwhereAddItem()is then simply a special case of this new functionTable.InsertRow(row int) *TableTable.InsertColumn(column int) *TableThe
Tablefunctions would simply shift the contents. You'd still need to callSetCell()on the new row/column to define your content.If these work for you, I can add them.
@ardnew commented on GitHub (Dec 28, 2018):
I'm leaning towards
Listfor my purposes, so if you could implement that, I would be forever grateful.I did try implementing it myself (see below), but something isn't working quite right. From a goroutine, I'm calling
GetItemCount()andGetItemText()to iterate over all list items to determine where to insert the new list item with myInsertItem()below. It seems to work fine up until the point it needs to insert an item outside the range of what's visible on screen, and then it starts acting funny -- i.e., it determines the wrong insertion index. I've implemented this search and insert inside of aQueueUpdate(), but I didn't implement any further resource protection. Haven't had a chance to debug any further yet.I'm interested to see how your implementation differs from mine.
@rivo commented on GitHub (Jan 12, 2019):
I've added
InsertItem()as well asFindItems()based on your comments and your code. My changes are more comprehensive (as is the documentation) and handle some things differently. I also modifiedSetCurrentItem()andRemoveItem()so negative indices can be provided like withInsertItem().Thanks for the PR nonetheless. It was good to have a comparison. I hope you don't mind that I'm not merging your version.
If the troubles you mention in your last comment are still there, it would be good to see some example code so I can reproduce them.
I'm reopening this issue because the suggested
Tablefunctions are still open items.@ardnew commented on GitHub (Jan 12, 2019):
Of course I don't mind, I appreciate you actually implementing the request.
I do have one other suggestion based on your changes though. After seeing your
FindItems()addition and wishing it had a callback, I think it would be very slick to generalize that capability with a higher-order map operation:Then
FindItems()would be a convenient utility function based on this. Theboolreturn value of the callback offers the user a way to break out, halting the list traversal.It's the next-best (and safer) interface without actually exposing
List.itemsto the user. This is also better in the sense that the caller doesn't have to wait for the entire traversal to complete before reacting.@ardnew commented on GitHub (Jan 12, 2019):
And no, btw, the list troubles I originally mentioned were bugs on my side unrelated to this change -- they've been resolved.
@rivo commented on GitHub (Jan 23, 2019):
FindItems()does a search, though. Your suggestion forMapItems()takes no search strings. So you simply want to iterate the list items?Since it also doesn't return a new value (except an indication that you want to stop the iteration), a better name would be
Walk(), similar toTreeNode.Walk().@ardnew commented on GitHub (Jan 23, 2019):
No, what I mean is that
FindItems()performs its own search with a built-in comparison function. I'm suggesting allowing the caller to provide its own comparison function as a callback. Consider something like the following defined in the library code:And the caller could then provide their own search criteria:
But to further generalize this, I was suggesting a
MapItems()so that the iterator wouldn't necessarily stop after finding the first element that matches the search criteria. This way, it's possible to find multiple items in the list with a single list traversal (e.g. for filtering a list), letting the caller decide when to stop iterating. Something like the following:And now that first
FindItem()implementation I offered would just be a special case of thisMapItems()routine:@rivo commented on GitHub (Jan 24, 2019):
Ok, I understand. But again,
mapis the wrong name for this. What you're suggesting iswalk,iterate, ortraverse. A mapping function would emit a new value for each list element. That's not what your example code does.Semantics aside, everything you describe can already be achieved today with little code:
I'd like to see real-life requirements for more functions here before I add them on a suspicion. Maybe
FindItems()was even a step too far? Anyway, if there's an actualtviewfunctionality you're missing in your application, please open a new issue.@ardnew commented on GitHub (Jan 24, 2019):
Ah yes, you're absolutely right with the name "map", that wasn't a distinction I was considering in my head. Although the routine I offered could be used to transform a list, it admittedly wasn't my intent.
And true, even the
FindItems()you implemented could have been implemented withGetItemText(), I don't think you're taking it too far -- there is value and utility in simplifying these common operations.Furthermore, one real benefit (which you are not taking advantage of) I think could be achieved with the library performing these list traversals is to make them thread-safe, as you've recently addressed by offering
QueueUpdate(). These list traversals take time. Time that people would be inclined to offload to a goroutine, which at the moment is unsafe. It may be too much responsibility to put on the caller application to protect that internal item slice ofListsince they have no direct access to it anyway. MaybeFindItems()would be more valuable if the caller could safely call it from any goroutine. Just a thought