Repository: zng-ui/zng (language: rust)

Issue:
Auto scroll to focused scrolls when large widget is already partially visible
#### Following these steps:

1 - Run respawn example.
2 - Click "Crash (panic)".
3 - Scroll to end of panic text without focusing text (click scrollbar and drag).
4 - Click panic text to focus.

#### I saw this happen:

Text scrolls, sometimes to top, sometimes to center.

#### I expected this to happen:

No scrolling because the text is already partially visible.


Discussion / hints:
Better test:

```rust
fn issue_161() -> impl UiNode {
    let mut t = Txt::from("");
    for i in 0..1000 {
        use std::fmt::Write as _;
        writeln!(&mut t, "Line {i}").unwrap();
    }
    t.end_mut();
    Scroll!(SelectableText!(t))
}
```
Discovered a related issue, if no scroll child subscribes to `FOCUS_CHANGED_EVENT` the event is not received. Will fix with the main issue.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3eace8ad8..655ec17d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,6 +50,8 @@ fn version_0_5() -> impl UiNode {
         - Removed `FocusChangedCause::is_prev_request`.
 * Add `FocusChangedCause::request_target` helper method.
 * Add `WidgetPath::parent_id` helper method.
+* Fix auto scroll-to-focused not working when the focused child does not subscribe to focus change events.
+* Fix auto scroll-to-focused scrolling when large widget is already visible.
 
 # 0.4.0
 
diff --git a/crates/zng-wgt-scroll/src/node.rs b/crates/zng-wgt-scroll/src/node.rs
index ded178148..ff3d0d936 100644
--- a/crates/zng-wgt-scroll/src/node.rs
+++ b/crates/zng-wgt-scroll/src/node.rs
@@ -640,6 +640,7 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
     match_node(child, move |child, op| match op {
         UiNodeOp::Init => {
             _handle = SCROLL_TO_CMD.scoped(WIDGET.id()).subscribe(true);
+            WIDGET.sub_event(&FOCUS_CHANGED_EVENT);
         }
         UiNodeOp::Deinit => {
             _handle = CommandHandle::dummy();
@@ -654,8 +655,8 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
                             // scroll_to_focused enabled
 
                             if SCROLL.can_scroll_vertical() || SCROLL.can_scroll_horizontal() {
-                                // can scroll AND focus did not change by a click on the Scroll! scope restoring focus
-                                // back to a child.
+                                // auto scroll if can scroll AND focus did not change by a click on the
+                                // Scroll! scope restoring focus back to a child AND the target is not already visible.
 
                                 let tree = WINDOW.info();
                                 if let Some(mut target) = tree.get(path.widget_id()) {
@@ -695,8 +696,24 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
                                             }
                                         }
 
-                                        scroll_to = Some((Rect::from(target.inner_bounds()), mode, None, false));
-                                        WIDGET.layout();
+                                        // check if widget is not large and already visible.
+                                        let mut scroll = true;
+                                        let scroll_bounds = tree.get(self_id).unwrap().inner_bounds();
+                                        let target_bounds = target.inner_bounds();
+                                        if let Some(r) = scroll_bounds.intersection(&target_bounds) {
+                                            let is_large_visible_v = r.height() > Px(20)
+                                                && target_bounds.height() > scroll_bounds.height()
+                                                && SCROLL.can_scroll_vertical();
+                                            let is_large_visible_h = r.width() > Px(20)
+                                                && target_bounds.width() > scroll_bounds.width()
+                                                && SCROLL.can_scroll_horizontal();
+
+                                            scroll = !is_large_visible_v && !is_large_visible_h;
+                                        }
+                                        if scroll {
+                                            scroll_to = Some((Rect::from(target_bounds), mode, None, false));
+                                            WIDGET.layout();
+                                        }
                                     }
                                 }
                             }
diff --git a/crates/zng-wgt-scroll/src/scroll_properties.rs b/crates/zng-wgt-scroll/src/scroll_properties.rs
index bf139ac3b..7ee66a7b6 100644
--- a/crates/zng-wgt-scroll/src/scroll_properties.rs
+++ b/crates/zng-wgt-scroll/src/scroll_properties.rs
@@ -77,7 +77,7 @@ context_var! {
     /// [`LayoutMetrics::viewport`]: zng_wgt::prelude::LayoutMetrics::viewport
     pub static DEFINE_VIEWPORT_UNIT_VAR: bool = true;
 
-    /// Scroll to mode used by scroll widgets when scrolling to make the focused child visible.
+    /// Scroll to mode used when scrolling to make the focused child visible.
     ///
     /// Default is minimal 0dip on all sides.
     pub static SCROLL_TO_FOCUSED_MODE_VAR: Option<ScrollToMode> = ScrollToMode::Minimal {


Test patch: