Skip to main content

ListModel

Struct ListModel 

Source
pub struct ListModel<T: 'static> { /* private fields */ }
Expand description

A concrete reactive list that stores items in a Vec<T>.

ListModel<T> is Clone — cloning produces a second handle to the same data. Multiple widgets can hold clones and all see the same items.

Every mutation method modifies the internal Vec, drops the mutable borrow, then notifies observers. By the time any observer runs, the borrow is released and shared borrows (len(), with_item()) are safe.

Implementations§

Source§

impl<T: 'static> ListModel<T>

Source

pub fn new() -> Self

Create an empty list model.

Source

pub fn from_vec(items: Vec<T>) -> Self

Create a list model from an existing vector.

Source

pub fn len(&self) -> usize

Number of items in the list.

Source

pub fn is_empty(&self) -> bool

Whether the list is empty.

Source

pub fn with_item<R>(&self, index: usize, f: impl FnOnce(&T) -> R) -> Option<R>

Access an item by index via a callback. Returns None if out of bounds.

The callback pattern avoids returning a reference that would need to outlive the RefCell borrow guard.

Source

pub fn push(&self, item: T)

Append an item to the end of the list.

Source

pub fn insert(&self, index: usize, item: T)

Insert an item at the given index.

§Panics

Panics if index > len().

Source

pub fn remove(&self, index: usize) -> T

Remove and return the item at the given index.

§Panics

Panics if index >= len().

Source

pub fn set(&self, index: usize, item: T)

Replace the item at the given index.

§Panics

Panics if index >= len().

Source

pub fn move_item(&self, from: usize, to: usize)

Move an item from one index to another.

The item at from is removed, then inserted at to (post-removal index).

§Panics

Panics if either index is out of bounds.

Source

pub fn move_items(&self, indices: &[usize], insert_gap: usize) -> bool

Move a set of items so they land contiguously at a drop gap, preserving their relative order — the multi-row same-view reorder commit. indices are the items’ current positions (any order; out-of-range entries are ignored); insert_gap is the destination in 0..=len expressed in the pre-move indexing (i.e. “land before the item currently at insert_gap”; len = at the end).

Returns whether anything moved (false if indices held no in-range entry). A contiguous source block emits a single DataChange::ItemsMoved — so index-based selection follows the moved rows; a non-contiguous set emits DataChange::Reset (that permutation is not expressible as one ItemsMoved, and selection is dropped). For a single index prefer move_item.

Source

pub fn replace_all(&self, items: Vec<T>)

Replace the entire list contents.

Source

pub fn clear(&self)

Remove all items from the list.

Source

pub fn observe_changes( &self, f: impl Fn(&DataChange) + 'static, ) -> ObserverHandle

Register an observer that is called on every mutation. Returns an ObserverHandle — dropping it removes the callback.

Source§

impl<T: PartialEq + 'static> ListModel<T>

Source

pub fn reconcile_by_key<K: Eq + Hash>( &self, new_items: Vec<T>, key_fn: impl Fn(&T) -> K, )

Reconcile the list’s contents with new_items, matching old and new rows by key (key_fn) instead of wholesale-replacing them, and emitting the minimal set of granular DataChanges needed to reach that state — never DataChange::Reset.

This is the primitive a live view needs when a peer process (or any other out-of-band writer) reloads a backing file and the merged result must land in a ListModel that a ListView is currently displaying, without wiping the user’s selection or keyboard focus mid-interaction. replace_all/clear always emit Reset, and a Reset unconditionally clears a positional SelectionModel (RowSelection::from_index) — reconcile_by_key is how a caller avoids that.

Emits, in this order, coalescing contiguous runs into a single event each:

  • DataChange::ItemsRemoved for keys present in the old list but absent from new_items;
  • DataChange::ItemsMoved (single-row blocks) to re-order the surviving rows into new_items’s relative order — skipped entirely for rows already in the right place, so an append-only or remove-only reload emits no moves at all;
  • DataChange::ItemsInserted for keys present in new_items but not in the old list;
  • DataChange::ItemUpdated for a row whose key is unchanged but whose content differs (T: PartialEq) — the row’s stored value is replaced with the incoming one.

If new_items is identical (same keys, same order, same content, by PartialEq) to the current contents, no change is emitted and no observer runs — reconciling with unchanged data is silent.

§Preconditions

key_fn must be a pure, stable function of an item’s identity (not its content) and keys must be unique within both the current list and new_items. See # Panics below — violating either is a caller bug, not a silently-tolerated edge case.

§Panics

Panics (via an internal .expect) if key_fn is not stable — it returns a different key for the same item across the two calls this method makes to it (once while snapshotting the current list’s keys, once while re-deriving a key during the write pass) — or if a key is duplicated within the current list or within new_items. Both break the same invariant the write pass relies on: “the item that was accounted for under this key is still findable at or after the write cursor.” A duplicate key means two different items raced to claim one key slot, so by the time the second one is processed the slot the accounting expected is already gone. This is this crate’s usual documented-panic-on-contract-violation style (see e.g. TreeModel::remove) — a caller-side bug surfaced immediately as a panic, not silently wrong data.

§Complexity

Re-ordering is a straightforward left-to-right pass that moves each out-of-place survivor into its target slot; it is correct and always granular, but is not guaranteed to emit the mathematically fewest possible ItemsMoved events for an adversarial permutation (an LIS-based scheme could do slightly better there). For the common case this primitive targets — a peer append/remove/edit merged back in — the existing relative order of untouched rows is preserved as-is, so no moves are emitted at all.

Source§

impl<T: Debug + 'static> ListModel<T>

Source

pub fn debug_named(self, _name: impl Into<String>) -> Self

Register this model with the debug inspector under name. In release builds (!cfg(debug_assertions)) this is a no-op pass-through so call sites stay free of #[cfg] lines.

Idempotent on repeated calls — the latest registration wins. The registration drops automatically when the last ListModel handle is freed (the adapter the registry holds is Weak).

Trait Implementations§

Source§

impl<T: 'static> Clone for ListModel<T>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug + 'static> Debug for ListModel<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: 'static> Default for ListModel<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<T: 'static> ListDataSource for ListModel<T>

ListModel is the built-in fully-resident, in-memory ListDataSource. Identity is positional (Key = usize); a SameView drop reorders via move_item. Into and Foreign drops are rejected (a flat list does not nest, and a bare model knows no foreign payloads).

Source§

type Item = T

The item type exposed by this data source.
Source§

type Key = usize

The stable per-row identity. In-memory ListModel uses usize (the index); external sources use their own domain key so keyed selection / DnD survive reorders without a mirror model.
Source§

fn len(&self) -> usize

Number of rows (the total, including not-yet-loaded ones for a windowed source — the scrollbar needs it).
Source§

fn with_item<R>(&self, index: usize, f: impl FnOnce(&T) -> R) -> Option<R>

Access the item at index via a callback. Returns None for an out-of-bounds index OR an in-bounds index whose data is still Loading (see row_state).
Source§

fn key_at(&self, index: usize) -> Option<usize>

The stable key of the row at index. Default None (no identity); sources that support keyed selection / DnD override it.
Source§

fn index_of(&self, key: &usize) -> Option<usize>

The index of a key, if currently present. Default None.
Source§

fn observe_changes(&self, f: impl Fn(&DataChange) + 'static) -> ObserverHandle

Register an observer that is called on every mutation; dropping the returned [ObserverHandle] unregisters the callback automatically.
Source§

fn drag(&self, _key: &usize) -> DragEligibility

Whether the row may begin a drag (the transferable gate).
Source§

fn can_accept(&self, query: &DropQuery<'_, usize>) -> DropResponse

Whether a hovered drop is permitted (and where) — the pre-commit verdict.
Source§

fn accept_drop(&self, commit: DropCommit<'_, usize>) -> bool

Apply a committed drop. Returns whether it was applied.
Source§

fn reorder_within( &self, sources: &[usize], target: &usize, position: DropPosition, ) -> bool

Reorder a whole set of this source’s OWN rows so they land contiguously at a drop gap — the multi-row same-view reorder commit. sources are the dragged rows’ keys in the origin’s visible order; target / position name the drop gap. Returns whether anything moved. Read more
Source§

fn on_drag_out(&self, key: &usize)

Called on the origin source after one of its rows was accepted by a different view (source-side completion). Shared/command-backed sources no-op this; independent models use it to drop the moved row.
Source§

fn is_empty(&self) -> bool

Whether the source is empty.
Source§

fn first_changed_index(&self) -> Option<usize>

First index whose content may differ after the change just delivered — rows 0..index are unchanged. None means unknown (full change).
Source§

fn row_state(&self, _index: usize) -> RowState

Whether the row at index is loaded.
Source§

fn request_window(&self, _range: Range<usize>)

Nudge the source to load the given range (the view calls this each build with its visible + buffer window).
Source§

fn can_fetch_more(&self) -> bool

Whether more rows can be appended (infinite scroll).
Source§

fn fetch_more(&self)

Fetch the next page (append-only growth).

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for ListModel<T>

§

impl<T> !Send for ListModel<T>

§

impl<T> !Sync for ListModel<T>

§

impl<T> !UnwindSafe for ListModel<T>

§

impl<T> Freeze for ListModel<T>

§

impl<T> Unpin for ListModel<T>

§

impl<T> UnsafeUnpin for ListModel<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.