mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-11-03 21:24:17 +00:00 
			
		
		
		
	* Set `accessible-action-default` on `ListView` items * Set `accessible-role` on `ListItem` for Qt style * Set `accessible-label` on `ListItem` for Qt style
		
			
				
	
	
		
			220 lines
		
	
	
	
		
			7.1 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
	
		
			7.1 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
// Copyright © SixtyFPS GmbH <info@slint.dev>
 | 
						|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
 | 
						|
 | 
						|
import { ListItem, ScrollView } from "std-widgets-impl.slint";
 | 
						|
 | 
						|
export component ListView inherits ScrollView {
 | 
						|
    @children
 | 
						|
    accessible-role: list;
 | 
						|
}
 | 
						|
 | 
						|
component StandardListViewBase inherits ListView {
 | 
						|
    in property <[StandardListViewItem]> model;
 | 
						|
    in-out property <int> current-item: -1;
 | 
						|
 | 
						|
    callback current-item-changed(current-item: int);
 | 
						|
    callback item-pointer-event(item: int, event: PointerEvent, position: Point);
 | 
						|
 | 
						|
    public function set-current-item(index: int) {
 | 
						|
        if index < 0 || index >= model.length || index == root.current-item {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        bring-into-view(index);
 | 
						|
 | 
						|
        current-item = index;
 | 
						|
        focus-item = index;
 | 
						|
        current-item-changed(current-item);
 | 
						|
    }
 | 
						|
 | 
						|
    private property <length> item-height: self.viewport-height / self.model.length;
 | 
						|
    private property <int> into-view-item: 0;
 | 
						|
    private property <length> into-view-item-y: root.item-y(root.into-view-item);
 | 
						|
    private property <length> current-item-y: root.item-y(root.focus-item);
 | 
						|
    private property <int> focus-item: 0;
 | 
						|
    private property <bool> has-item-been-selected: false;
 | 
						|
 | 
						|
    accessible-delegate-focus: root.focus-item;
 | 
						|
    accessible-item-count: root.model.length;
 | 
						|
 | 
						|
    pure function first-visible-item() -> int {
 | 
						|
        return min(root.model.length - 1, max(0, round(-root.viewport-y / root.item-height)));
 | 
						|
    }
 | 
						|
 | 
						|
    pure function last-visible-item() -> int {
 | 
						|
        return min(root.model.length - 1, max(0, round((-root.viewport-y + root.height - root.item-height) / root.item-height)));
 | 
						|
    }
 | 
						|
 | 
						|
    pure function item-y(index: int) -> length {
 | 
						|
        return root.viewport-y + index * root.item-height;
 | 
						|
    }
 | 
						|
 | 
						|
    pure function item-at-y(y: length) -> int {
 | 
						|
        return min(root.model.length - 1, max(0, round(y / root.item-height)));
 | 
						|
    }
 | 
						|
 | 
						|
    function bring-into-view(index: int) {
 | 
						|
        if (index < 0 || index >= model.length) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        into-view-item = index;
 | 
						|
 | 
						|
        if (into-view-item-y < 0) {
 | 
						|
            self.viewport-y += 0 - into-view-item-y;
 | 
						|
        }
 | 
						|
 | 
						|
        if (into-view-item-y + item-height > self.visible-height) {
 | 
						|
            self.viewport-y -= into-view-item-y + item-height - self.visible-height;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-up() {
 | 
						|
        root.set-focus-item(root.focus-item - 1);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-page-up() {
 | 
						|
        if root.focus-item != root.first-visible-item() {
 | 
						|
            root.set-focus-item(root.first-visible-item())
 | 
						|
        } else {
 | 
						|
            root.set-focus-item(root.item-at-y(root.item-y(root.first-visible-item()) - root.height));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-first() {
 | 
						|
        root.set-focus-item(0);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-down() {
 | 
						|
        root.set-focus-item(root.focus-item + 1);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-page-down() {
 | 
						|
        if root.focus-item != root.last-visible-item() {
 | 
						|
            root.set-focus-item(root.last-visible-item())
 | 
						|
        } else {
 | 
						|
            root.set-focus-item(root.item-at-y(root.item-y(root.last-visible-item()) + root.height));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-last() {
 | 
						|
        root.set-focus-item(root.model.length - 1);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function select-focus-item() {
 | 
						|
        root.set-current-item(root.focus-item);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function focus-current-item() {
 | 
						|
        if root.current-item == -1 && !root.has-item-been-selected && root.model.length > 0 {
 | 
						|
            root.set-current-item(0);
 | 
						|
        }
 | 
						|
        root.has-item-been-selected = true;
 | 
						|
 | 
						|
        if (root.current-item-y + root.item-height < 0
 | 
						|
            || root.current-item-y > root.height) {
 | 
						|
                root.focus-item = root.first-visible-item();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected function toggle-focus-item-selection() {
 | 
						|
        if (root.current-item == root.focus-item) {
 | 
						|
            root.current-item = -1;
 | 
						|
        } else {
 | 
						|
            root.select-focus-item();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    protected function set-focus-item(index: int) {
 | 
						|
        root.focus-item = min(root.model.length - 1, max(0, index));
 | 
						|
        root.bring-into-view(root.focus-item);
 | 
						|
    }
 | 
						|
 | 
						|
    for item[index] in root.model : ListItem {
 | 
						|
        height: self.min-height;
 | 
						|
        item: item;
 | 
						|
        index: index;
 | 
						|
        is-selected: index == root.current-item;
 | 
						|
        has-focus: root.has-focus && index == root.focus-item;
 | 
						|
        has-hover: i-touch-area.has-hover;
 | 
						|
        pressed: i-touch-area.pressed;
 | 
						|
        pressed-x: i-touch-area.pressed-x;
 | 
						|
        pressed-y: i-touch-area.pressed-y;
 | 
						|
        accessible-action-default => { i-touch-area.clicked(); }
 | 
						|
 | 
						|
        i-touch-area := TouchArea {
 | 
						|
            clicked => {
 | 
						|
                root.set-current-item(index);
 | 
						|
            }
 | 
						|
 | 
						|
            pointer-event(pe) => {
 | 
						|
                root.item-pointer-event(index, pe, {
 | 
						|
                    x: self.absolute-position.x + self.mouse-x - root.absolute-position.x,
 | 
						|
                    y: self.absolute-position.y + self.mouse-y - root.absolute-position.y,
 | 
						|
                });
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export component StandardListView inherits StandardListViewBase {
 | 
						|
    forward-focus: i-focus-scope;
 | 
						|
 | 
						|
    i-focus-scope := FocusScope {
 | 
						|
        x: 0;
 | 
						|
        width: 0;  // Do not react on clicks
 | 
						|
 | 
						|
        focus-changed-event => {
 | 
						|
            root.focus-current-item();
 | 
						|
            root.has-focus = self.has-focus;
 | 
						|
        }
 | 
						|
 | 
						|
        key-pressed(event) => {
 | 
						|
            if (event.text == Key.UpArrow) {
 | 
						|
                root.focus-up();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.PageUp) {
 | 
						|
                root.focus-page-up();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.Home) {
 | 
						|
                root.focus-first();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.DownArrow) {
 | 
						|
                root.focus-down();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.PageDown) {
 | 
						|
                root.focus-page-down();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.End) {
 | 
						|
                root.focus-last();
 | 
						|
                if (!event.modifiers.control) {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            } else if (event.text == Key.Space) {
 | 
						|
                if (event.modifiers.control) {
 | 
						|
                    root.toggle-focus-item-selection();
 | 
						|
                } else {
 | 
						|
                    root.select-focus-item();
 | 
						|
                }
 | 
						|
                return accept;
 | 
						|
            }
 | 
						|
            reject
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |