Compare commits
5 Commits
git-clone
...
settings-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bcb4bd400 | ||
|
|
98f26b2b43 | ||
|
|
9b3bb0b4aa | ||
|
|
5b85cdcf3c | ||
|
|
557199052e |
@@ -3034,7 +3034,20 @@ struct ScrollHandleState {
|
|||||||
child_bounds: Vec<Bounds<Pixels>>,
|
child_bounds: Vec<Bounds<Pixels>>,
|
||||||
scroll_to_bottom: bool,
|
scroll_to_bottom: bool,
|
||||||
overflow: Point<Overflow>,
|
overflow: Point<Overflow>,
|
||||||
active_item: Option<usize>,
|
active_item: Option<ScrollActiveItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
struct ScrollActiveItem {
|
||||||
|
index: usize,
|
||||||
|
strategy: ScrollStrategy,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
enum ScrollStrategy {
|
||||||
|
#[default]
|
||||||
|
FirstVisible,
|
||||||
|
Top,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to the scrollable aspects of an element.
|
/// A handle to the scrollable aspects of an element.
|
||||||
@@ -3084,6 +3097,25 @@ impl ScrollHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the bottom child that's scrolled into view.
|
||||||
|
pub fn bottom_item(&self) -> usize {
|
||||||
|
let state = self.0.borrow();
|
||||||
|
let bottom = state.bounds.bottom() - state.offset.borrow().y;
|
||||||
|
|
||||||
|
match state.child_bounds.binary_search_by(|bounds| {
|
||||||
|
if bottom < bounds.top() {
|
||||||
|
Ordering::Greater
|
||||||
|
} else if bottom > bounds.bottom() {
|
||||||
|
Ordering::Less
|
||||||
|
} else {
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Ok(ix) => ix,
|
||||||
|
Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the bounds into which this child is painted
|
/// Return the bounds into which this child is painted
|
||||||
pub fn bounds(&self) -> Bounds<Pixels> {
|
pub fn bounds(&self) -> Bounds<Pixels> {
|
||||||
self.0.borrow().bounds
|
self.0.borrow().bounds
|
||||||
@@ -3097,26 +3129,48 @@ impl ScrollHandle {
|
|||||||
/// Update [ScrollHandleState]'s active item for scrolling to in prepaint
|
/// Update [ScrollHandleState]'s active item for scrolling to in prepaint
|
||||||
pub fn scroll_to_item(&self, ix: usize) {
|
pub fn scroll_to_item(&self, ix: usize) {
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
state.active_item = Some(ix);
|
state.active_item = Some(ScrollActiveItem {
|
||||||
|
index: ix,
|
||||||
|
strategy: ScrollStrategy::default(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scrolls the minimal amount to ensure that the child is
|
/// Update [ScrollHandleState]'s active item for scrolling to in prepaint
|
||||||
/// fully visible
|
/// This scrolls the minimal amount to ensure that the child is the first visible element
|
||||||
|
pub fn scroll_to_top_of_item(&self, ix: usize) {
|
||||||
|
let mut state = self.0.borrow_mut();
|
||||||
|
state.active_item = Some(ScrollActiveItem {
|
||||||
|
index: ix,
|
||||||
|
strategy: ScrollStrategy::Top,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scrolls the minimal amount to either ensure that the child is
|
||||||
|
/// fully visible or the top element of the view depends on the
|
||||||
|
/// scroll strategy
|
||||||
fn scroll_to_active_item(&self) {
|
fn scroll_to_active_item(&self) {
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
||||||
let Some(active_item_index) = state.active_item else {
|
let Some(active_item) = state.active_item else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let active_item = match state.child_bounds.get(active_item_index) {
|
|
||||||
|
let active_item = match state.child_bounds.get(active_item.index) {
|
||||||
Some(bounds) => {
|
Some(bounds) => {
|
||||||
let mut scroll_offset = state.offset.borrow_mut();
|
let mut scroll_offset = state.offset.borrow_mut();
|
||||||
|
|
||||||
if state.overflow.y == Overflow::Scroll {
|
match active_item.strategy {
|
||||||
if bounds.top() + scroll_offset.y < state.bounds.top() {
|
ScrollStrategy::FirstVisible => {
|
||||||
|
if state.overflow.y == Overflow::Scroll {
|
||||||
|
if bounds.top() + scroll_offset.y < state.bounds.top() {
|
||||||
|
scroll_offset.y = state.bounds.top() - bounds.top();
|
||||||
|
} else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
|
||||||
|
scroll_offset.y = state.bounds.bottom() - bounds.bottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScrollStrategy::Top => {
|
||||||
scroll_offset.y = state.bounds.top() - bounds.top();
|
scroll_offset.y = state.bounds.top() - bounds.top();
|
||||||
} else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
|
|
||||||
scroll_offset.y = state.bounds.bottom() - bounds.bottom();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3129,7 +3183,7 @@ impl ScrollHandle {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
None => Some(active_item_index),
|
None => Some(active_item),
|
||||||
};
|
};
|
||||||
state.active_item = active_item;
|
state.active_item = active_item;
|
||||||
}
|
}
|
||||||
@@ -3163,6 +3217,21 @@ impl ScrollHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the logical scroll bottom, based on a child index and a pixel offset.
|
||||||
|
pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
|
||||||
|
let ix = self.bottom_item();
|
||||||
|
let state = self.0.borrow();
|
||||||
|
|
||||||
|
if let Some(child_bounds) = state.child_bounds.get(ix) {
|
||||||
|
(
|
||||||
|
ix,
|
||||||
|
child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(ix, px(0.))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the count of children for scrollable item.
|
/// Get the count of children for scrollable item.
|
||||||
pub fn children_count(&self) -> usize {
|
pub fn children_count(&self) -> usize {
|
||||||
self.0.borrow().child_bounds.len()
|
self.0.borrow().child_bounds.len()
|
||||||
|
|||||||
@@ -943,6 +943,29 @@ impl SettingsWindow {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_navbar_entry_from_scroll_position(&mut self) {
|
||||||
|
let top = self.scroll_handle.top_item();
|
||||||
|
let bottom = self.scroll_handle.bottom_item();
|
||||||
|
|
||||||
|
let scroll_index = (top + bottom) / 2;
|
||||||
|
let scroll_index = scroll_index.clamp(top, bottom);
|
||||||
|
let mut page_index = self.navbar_entry;
|
||||||
|
|
||||||
|
while !self.navbar_entries[page_index].is_root {
|
||||||
|
page_index -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.navbar_entries[page_index].expanded {
|
||||||
|
let section_index = self
|
||||||
|
.page_items()
|
||||||
|
.take(scroll_index + 1)
|
||||||
|
.filter(|item| matches!(item, SettingsPageItem::SectionHeader(_)))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
self.navbar_entry = section_index + page_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fetch_files(&mut self, cx: &mut Context<SettingsWindow>) {
|
fn fetch_files(&mut self, cx: &mut Context<SettingsWindow>) {
|
||||||
let prev_files = self.files.clone();
|
let prev_files = self.files.clone();
|
||||||
let settings_store = cx.global::<SettingsStore>();
|
let settings_store = cx.global::<SettingsStore>();
|
||||||
@@ -1021,9 +1044,7 @@ impl SettingsWindow {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<SettingsWindow>,
|
cx: &mut Context<SettingsWindow>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let visible_entries: Vec<_> = self.visible_navbar_entries().collect();
|
let visible_count = self.visible_navbar_entries().count();
|
||||||
let visible_count = visible_entries.len();
|
|
||||||
|
|
||||||
let nav_background = cx.theme().colors().panel_background;
|
let nav_background = cx.theme().colors().panel_background;
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
@@ -1069,6 +1090,36 @@ impl SettingsWindow {
|
|||||||
.on_click(cx.listener(
|
.on_click(cx.listener(
|
||||||
move |this, evt: &gpui::ClickEvent, window, cx| {
|
move |this, evt: &gpui::ClickEvent, window, cx| {
|
||||||
this.navbar_entry = ix;
|
this.navbar_entry = ix;
|
||||||
|
|
||||||
|
if !this.navbar_entries[ix].is_root {
|
||||||
|
let mut selected_page_ix = ix;
|
||||||
|
|
||||||
|
while !this.navbar_entries[selected_page_ix]
|
||||||
|
.is_root
|
||||||
|
{
|
||||||
|
selected_page_ix -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let section_header = ix - selected_page_ix;
|
||||||
|
|
||||||
|
if let Some(section_index) = this
|
||||||
|
.page_items()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|item| {
|
||||||
|
matches!(
|
||||||
|
item.1,
|
||||||
|
SettingsPageItem::SectionHeader(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.take(section_header)
|
||||||
|
.last()
|
||||||
|
.map(|pair| pair.0)
|
||||||
|
{
|
||||||
|
this.scroll_handle
|
||||||
|
.scroll_to_top_of_item(section_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if evt.is_keyboard() {
|
if evt.is_keyboard() {
|
||||||
// todo(settings_ui): Focus the actual item and scroll to it
|
// todo(settings_ui): Focus the actual item and scroll to it
|
||||||
this.focus_first_content_item(window, cx);
|
this.focus_first_content_item(window, cx);
|
||||||
@@ -1337,6 +1388,7 @@ impl SettingsWindow {
|
|||||||
impl Render for SettingsWindow {
|
impl Render for SettingsWindow {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let ui_font = theme::setup_ui_font(window, cx);
|
let ui_font = theme::setup_ui_font(window, cx);
|
||||||
|
self.calculate_navbar_entry_from_scroll_position();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id("settings-window")
|
.id("settings-window")
|
||||||
|
|||||||
Reference in New Issue
Block a user