Scrolling ScrollViewer with RStick when not in focus
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.
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.
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Scrolling ScrollViewer with RStick when not in focus
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?
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?
Re: Scrolling ScrollViewer with RStick when not in focus
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:
Currently, I get this at runtime:
.. 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).
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:
Code: Select all
<StackPanel
x:Name="ContentStack"
VerticalAlignment="Bottom"
MouseWheel="{Binding ElementName=AccScrollViewer, Path=OnMouseWheel3}">
<controls:AcceleratedScrollViewer
x:Name="AccScrollViewer"
...
...
Code: Select all
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'm not sure why MouseWheel would not be defined though (and only at run time, VS preview is happy as is).
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Scrolling ScrollViewer with RStick when not in focus
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: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.
Code: Select all
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>()));
}
};
Code: Select all
<StackPanel x:Name="ContentStack" VerticalAlignment="Bottom">
<i:Interaction.Behaviors>
<local:ScrollTargetBehavior Target="{Binding ElementName=AccScrollViewer}"/>
</i:Interaction.Behaviors>
<controls:AcceleratedScrollViewer x:Name="AccScrollViewer" .../>
...
</StackPanel>
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.I'm not sure why MouseWheel would not be defined though
Could this work for you?
Re: Scrolling ScrollViewer with RStick when not in focus
Hello!
Phenomenal help, thank you, works great!
Cheers.
Phenomenal help, thank you, works great!
Cheers.
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Scrolling ScrollViewer with RStick when not in focus
Great, marking this as solved.
Re: Scrolling ScrollViewer with RStick when not in focus
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!
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!
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Scrolling ScrollViewer with RStick when not in focus
You're welcome.
Re: Scrolling ScrollViewer with RStick when not in focus
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" :
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 ?!
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" :
Code: Select all
// 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();
}
}
- 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 ?!
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Scrolling ScrollViewer with RStick when not in focus
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.
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: Ahrefs [Bot], Bing [Bot] and 70 guests