sgonchar
Topic Author
Posts: 33
Joined: 15 Mar 2021, 22:11

Scrolling ScrollViewer with RStick when not in focus

30 Jun 2021, 23:34

Hello,

I'm wondering how can we RStick scroll a ScrollViewer/AcceleratedScrollViewer when it's not in focus?
We have a screen with scrollviewer and several buttons, UX wants one of the buttons to be in focus at all times, but RStick to still work to scroll the text.

I'm wondering how you think is best to do something like this?
- Maybe some kind of preview key and forward to component (would PreviewMouseWheel work?)
- Is it possible with xaml only? Like a direct edit scrollviewer "position" from Button? (via some kind of binding / input forwarding?)
I don't think I can find something like this in examples.

Cheers.
 
User avatar
sfernandez
Site Admin
Posts: 2255
Joined: 22 Dec 2011, 19:20

Re: Scrolling ScrollViewer with RStick when not in focus

01 Jul 2021, 10:54

I guess you are sending the RStick changes to the View using the Scroll() method. Those are translated to MouseWheel events that are raised from the focused element.

So I'm thinking that you will need a Behavior you can set on a parent panel that contains both the focused button and the scrollviewer. The behavior will listen to unhandled MouseWheel events on the panel, and will have a ScrollViewer property you can bind to the actual ScrollViewer you want to scroll. When the behavior handles the MouseWheel, it will manually scroll the specified ScrollViewer using ScrollToVerticalOffset().

Would that work for you?
 
sgonchar
Topic Author
Posts: 33
Joined: 15 Mar 2021, 22:11

Re: Scrolling ScrollViewer with RStick when not in focus

09 Jul 2021, 00:45

Hello,
Thank you for the ideas!
I'm wondering if there's a way to do it in xaml? I haven't been able to find how to forward events just via xaml/bindings to existing event handlers.

After that, I've been trying to only involve codebehind for the component. We have a StackPanel containing both the ScrollViewer and another StackPanel of buttons. Outer most StackPanel has:
    <StackPanel
      x:Name="ContentStack"
      VerticalAlignment="Bottom"
      MouseWheel="{Binding ElementName=AccScrollViewer, Path=OnMouseWheel3}">
      <controls:AcceleratedScrollViewer
          x:Name="AccScrollViewer"
          ...
       ...
Currently, I get this at runtime:
ERROR| [Noesis] Views/TosPrompt.xaml(26): Unknown property StackPanel.MouseWheel, or object to assign is incompatibleViews/TosPrompt.xaml(26): Unknown property StackPanel.MouseWheel, or object to assign is incompatible
.. I expected an error on OnMouseWheel3, is it's only a function in AcceleratedScrollViewer.xaml.cpp and is not exposed. I couldn't expose it yet via NSProp or use RegisterProperty, might be just syntax wrong.
I'm not sure why MouseWheel would not be defined though (and only at run time, VS preview is happy as is).
 
User avatar
sfernandez
Site Admin
Posts: 2255
Joined: 22 Dec 2011, 19:20

Re: Scrolling ScrollViewer with RStick when not in focus

09 Jul 2021, 11:28

I'm wondering if there's a way to do it in xaml? I haven't been able to find how to forward events just via xaml/bindings to existing event handlers.
To do it in xaml you'll need to write a new Behavior that provides the logic that registers to the appropriate events, something like this:
class ScrollTargetBehavior : public BehaviorT<FrameworkElement>
{
public:
  ScrollViewer* GetTarget() const { return GetValue<Ptr<ScrollViewer>>(TargetProperty); }
  void SetTarget(ScrollViewer* value) { SetValue<Ptr<ScrollViewer>>(TargetProperty, value); }
public:
  static const DependencyProperty* TargetProperty;
protected:
  Ptr<Freezable> CreateInstanceCore() { return *new ScrollTargetBehavior(); }
  void OnAttached() override
  {
    ParentClass::OnAttached();
    FrameworkElement* associatedObject = GetAssociatedObject();
    associatedObject->MouseWheel() += MakeDelegate(this, &ScrollTargetBehavior::OnMouseWheel);
  }
  void OnDetaching() override
  {
    FrameworkElement* associatedObject = GetAssociatedObject();
    associatedObject->MouseWheel() -= MakeDelegate(this, &ScrollTargetBehavior::OnMouseWheel);
    ParentClass::OnDetaching();  
  }
private:
  void OnMouseWheel(BaseComponent* sender, const MouseWheelEventArgs& e)
  {
    ScrollViewer* target = GetTarget();
    if (target != nullptr)
      target->ScrollToVerticalOffset(target->GetVerticalOffset() - e.wheelRotation / 120.0f);
  }
  NS_IMPLEMENT_INLINE_REFLECTION(ScrollTargetBehavior, BehaviorT<FrameworkElement>, "Testing.ScrollTargetBehavior")
  {
    DependencyData* data = NsMeta<DependencyData>(TypeOf<SelfClass>());
    data->RegisterProperty<Ptr<ScrollViewer>>(TargetProperty, "Target", PropertyMetadata::Create(Ptr<ScrollViewer>()));
  }
};
You can use this behavior in your xaml as follows:
<StackPanel x:Name="ContentStack" VerticalAlignment="Bottom">
  <i:Interaction.Behaviors>
    <local:ScrollTargetBehavior Target="{Binding ElementName=AccScrollViewer}"/>
  </i:Interaction.Behaviors>  
  <controls:AcceleratedScrollViewer x:Name="AccScrollViewer" .../>
  ...
</StackPanel>
I'm not sure why MouseWheel would not be defined though
MouseWheel is an event so you cannot set bindings there, you can only specify a code-behind method. Although I think you don't need code-behind and a custom ScrollViewer control to achieve this. As I see it, using the behavior I described before you can use the default ScrollViewer and scroll it from the behavior.

Could this work for you?
 
sgonchar
Topic Author
Posts: 33
Joined: 15 Mar 2021, 22:11

Re: Scrolling ScrollViewer with RStick when not in focus

09 Jul 2021, 22:08

Hello!
Phenomenal help, thank you, works great!
Cheers.
 
User avatar
sfernandez
Site Admin
Posts: 2255
Joined: 22 Dec 2011, 19:20

Re: Scrolling ScrollViewer with RStick when not in focus

09 Jul 2021, 23:16

Great, marking this as solved.
 
sgonchar
Topic Author
Posts: 33
Joined: 15 Mar 2021, 22:11

Re: Scrolling ScrollViewer with RStick when not in focus

10 Jul 2021, 00:04

Hello
I may have spoken too soon. Works amazing on PC, but doesn't trigger ScrollViewer::ScrollChanged / doesn't scroll on the switch .. still debugging.
Cheers.

edit: figured it out, local changes on top of scrollviewer, this question is resolved, thank you again!
 
User avatar
sfernandez
Site Admin
Posts: 2255
Joined: 22 Dec 2011, 19:20

Re: Scrolling ScrollViewer with RStick when not in focus

12 Jul 2021, 10:19

You're welcome.
 
sgonchar
Topic Author
Posts: 33
Joined: 15 Mar 2021, 22:11

Re: Scrolling ScrollViewer with RStick when not in focus

14 Jul 2021, 18:07

Hello,
We are running into some optimization voodoo.

EDIT 4 figured it out, effective fps too high so all rstick changes are tiny and get 0-ed. You can read about this epic adventure bellow if you're in a mood for a story.
FYI "fps" is ~ 5k it seems. I'm not sure yet what limits the fps to something reasonable when moving the mouse .. if it's incidental due to rendering cost, or there's an fps limit somewhere that we aren't usually checking.
End edit 4


The above works great until we try to use view->Update to not render frames that "have no changes" :
// pseudo code 
display->Render() += [...] ( ... )
{
        if( Noesis::view->Update(totalTimeElapsed) )
        {
                context->BeginRender();
                
                view->GetRenderer()->UpdateRenderTree();
                view->GetRenderer()->RenderOffscreen();
                
                context->SetDefaultRenderTarget(display->GetClientWidth(), display->GetClientHeight(), true);
		
		view->GetRenderer()->Render();
		
                context->EndRender();
                context->Swap();
        }
}
This now has some odd behaviors:
- Scrolling with R stick does not work on it's own. (releasedbg)
-- Scrolling with mouse wheel works great all the time.
- Moving the mouse around only within render area makes scrolling work again.
-- Once scrolling started, I can stop moving the mouse and scrolling will continue just fine.
- Adding conditional break points that don't trigger makes scrolling work again (releasedbg).
- Adding a break point will make scroll tick once per break (releasedbg).
- Going from ReleaseDbg to Debug makes scrolling work again.
-- Though even debug has maybe 0.5s lag before scroll starts.
- In releasedbg, adding a stdout or NS_LOG anywhere on frame will (say within ScrollTargetBehavior::OnMouseWheel(), or before view->Update() check will cause scrolling to work again
-- NS_LOG makes things work possibly due to our logger using a mutex? Not sure why stdout would ..
-- I'm certain that it's the logging when view->Update() is returning false that makes scrolling work again.
- Adding a gater in in OnMouseWheel to call target->ScrollToVerticalOffset() at most once per ~1ms also sometimes make scroll work.
- Calling context->Swap() without BeginRender()/EndRender()/UpdateRenderTree()/RenderOffscreen()/Render() also makes scrolling work (though jittery).

Can you think of why something like this would happen?
- Is there a way to mark view "dirty" within ScrollTargetBehavior so view->Update() returns true?

- Is there an invalidate of some kind I need to call after target->ScrollToVerticalOffset() ?

- As an aside, it looks like calling ScrollViewer::ScrollToVerticalOffset can call ScrollViewer::ScrollChanged() ?

- What does ScrollTargetBehavior::OnMouseWheel do differently when triggered with R stick from being triggered by mouse wheel?
-- it looks like there's a frequency of updates difference? Is it flooding something in ScrollViewer ?
-- Mouse::RaiseWheel seems to be triggered regardless, but Rstick goes through View::Scroll and mouse wheel through Mouse::Wheel.
--- Further up Rstick comes direct from Win32Display::EnterMessageLoop() through mScroll()
--- Further up Mouse wheel comes comes from EnterMessageLoop too but through through Win32Display::DispatchEvent()
---- but I don't know why one would scroll and one wouldn't.

- What about going from ReleaseDbg to Debug makes scrolling work again?
-- it seems it not entirely timing related as Debug still lags a few frames before starting to scroll, seems like some kind of event overflow / min difference checker?

- What does context->Swap() do that can make scrolling work again?
-- Is RStick input ignored without Swap? It seems to do some kind of event flush. Though not sure why debug build would work differently then.
-- Edit: Turns out RStick isn't getting ignored! When things aren't scrolling, It's coming through as wheelRotation = 0, but in when things scroll it's +/- some number. Frequency of updates doesn't change it seems.
-- Edit 2 I think I can confirm that View::Scroll gets called with some good value, but Mouse::RaiseWheel gets a 0 rotation. In debug initial couple thousand updates are also 0s but after that scrolling starts getting non zero values after that. If I keep moving the mouse around on screen, then all there are almost no 0 values (debug or relesedbg)
--- There's also a difference in frequency, 0 values happen 1000x more then non 0.
--- Edit 3 crazy idea time: what if our effective fps is like bazillion when we aren't rendering and that messes with input pulling .. so incremental values seem really small ?!
 
User avatar
sfernandez
Site Admin
Posts: 2255
Joined: 22 Dec 2011, 19:20

Re: Scrolling ScrollViewer with RStick when not in focus

15 Jul 2021, 19:20

Glad to hear you figured it out.
I was going to say that if you are not rendering every frame you will be getting lots of updates, and it didn't seem a good idea to send thousands of scroll events per second.

Who is online

Users browsing this forum: Bing [Bot] and 1 guest