Seek by clicking on video progress bar (1 Viewer)

morpheus_xx

Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    To be honest, my EventHandlerExtension can be replaced by CommandStencilExtension.
    Yes, I think the CommandStencil would fit for our needs.

    Current XAML implementation can be seen here: https://github.com/MediaPortal/Medi...ult/screens/FullscreenContentDVD.xaml#L38-L44

    In code the UIC does override OnMouseMove of UIElement: https://github.com/MediaPortal/Medi...ements/Controls/UserInputCapture.cs#L126-L128

    Does this match to your approach?
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    My approach is an exact clone of WPF (syntax wise) except to use a CommandStencil instead of the method name from the code behind class.
    There are no extra CommandStencil properties needed in anyclass that get executed from On... methods.
    I simply create an delegate from the CommandStencil and add it to the event.

    I create an GUITest page with MouseDown event.
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    Ok, it's working now:

    my XAML lokks like this:
    XML:
    <?xml version="1.0" encoding="utf-8"?>
    <Screen
        xmlns="www.team-mediaportal.com/2008/mpf/directx"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
      <Screen.Resources>
        <Model x:Key="Model" Id="F4FC1599-F412-40d0-82BF-46FC352E93BE"/>
        <!-- GUI-Test-Model -->
      </Screen.Resources>
    
      <Grid x:Name="Grid"
            PreviewMouseDown="{CommandStencil Source={StaticResource Model}, Path=PreviewMouseDownHandler}"
            MouseDown="{CommandStencil Source={StaticResource Model}, Path=MouseDownHandler}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"/>
          <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
          <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
       
        <Border Grid.Column="0" Grid.Row="0" Margin="20" Background="Yellow"
                PreviewMouseDown="{CommandStencil Source={StaticResource Model}, Path=PreviewMouseDownHandler}"
                MouseDown="{CommandStencil Source={StaticResource Model}, Path=MouseDownHandler}"
                x:Name="Yellow">
          <StackPanel Margin="10">
            <Border Margin="10" Background="Red" x:Name="RenInYellow"
                    PreviewMouseDown="{CommandStencil Source={StaticResource Model}, Path=PreviewMouseDownHandler}"
                    MouseDown="{CommandStencil Source={StaticResource Model}, Path=MouseDownHandler}">
              <Label HorizontalAlignment="Center" VerticalAlignment="Center"
                     Content="Red in yellow"/>
            </Border>
            <Border Margin="10" Background="Green" x:Name="GreenInYellow"
                    PreviewMouseDown="{CommandStencil Source={StaticResource Model}, Path=PreviewMouseDownHandler}"
                    MouseDown="{CommandStencil Source={StaticResource Model}, Path=MouseDownHandler}">
              <Label HorizontalAlignment="Center" VerticalAlignment="Center"
                     Content="Green in yellow"/>
            </Border>
          </StackPanel>
        </Border>
      </Grid>
    
    </Screen>

    The model looks like this:
    Code:
    public void PreviewMouseDownHandler(object sender, MouseButtonEventArgs e)
        {
          Debug.Print("PreviewMouseDownHandler sender={0}; Source={1}; OriginalSource={2}", sender, e.Source, e.OriginalSource);
        }
    
        public void MouseDownHandler(object sender, MouseButtonEventArgs e)
        {
          Debug.Print("MouseDownHandler sender={0}; Source={1}; OriginalSource={2}", sender, e.Source, e.OriginalSource);
        }

    When I click on the yellow area I get this output:
    Code:
    PreviewMouseDownHandler sender=Grid, Name: 'Grid', ElementState: Running; Source=Grid, Name: 'Grid', ElementState: Running; OriginalSource=StackPanel, ElementState: Running
    PreviewMouseDownHandler sender=Border, Name: 'Yellow', ElementState: Running; Source=Border, Name: 'Yellow', ElementState: Running; OriginalSource=StackPanel, ElementState: Running
    MouseDownHandler sender=Border, Name: 'Yellow', ElementState: Running; Source=Border, Name: 'Yellow', ElementState: Running; OriginalSource=StackPanel, ElementState: Running
    MouseDownHandler sender=Grid, Name: 'Grid', ElementState: Running; Source=Grid, Name: 'Grid', ElementState: Running; OriginalSource=StackPanel, ElementState: Running
    My "Hit testing" seems to return the StackPanel, even that it is transparent, and the Yellow Border should be the original source.
    Currently I missuse the OnMouseMove method for hittesting :whistle:

    When I click on the green area I get this:
    Code:
    PreviewMouseDownHandler sender=Grid, Name: 'Grid', ElementState: Running; Source=Grid, Name: 'Grid', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running
    PreviewMouseDownHandler sender=Border, Name: 'Yellow', ElementState: Running; Source=Border, Name: 'Yellow', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running
    PreviewMouseDownHandler sender=Border, Name: 'GreenInYellow', ElementState: Running; Source=Border, Name: 'GreenInYellow', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running
    MouseDownHandler sender=Border, Name: 'GreenInYellow', ElementState: Running; Source=Border, Name: 'GreenInYellow', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running
    MouseDownHandler sender=Border, Name: 'Yellow', ElementState: Running; Source=Border, Name: 'Yellow', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running
    MouseDownHandler sender=Grid, Name: 'Grid', ElementState: Running; Source=Grid, Name: 'Grid', ElementState: Running; OriginalSource=Border, Name: 'GreenInYellow', ElementState: Running

    The events in UIElement are defined like this (without the class handlers):
    Code:
       public static readonly RoutedEvent PreviewMouseDownEvent = EventManager.RegisterRoutedEvent(
          "PreviewMouseDown", RoutingStrategy.Tunnel, typeof(MouseButtonEventHandler), typeof(UIElement));
    
        // Provide CLR accessors for the event
        public event MouseButtonEventHandler PreviewMouseDown
        {
          add { AddHandler(PreviewMouseDownEvent, value); }
          remove { RemoveHandler(PreviewMouseDownEvent, value); }
        }
      public static readonly RoutedEvent MouseDownEvent = EventManager.RegisterRoutedEvent(
          "MouseDown", RoutingStrategy.Bubble, typeof(MouseButtonEventHandler), typeof(UIElement));
    
        // Provide CLR accessors for the event
        public event MouseButtonEventHandler MouseDown
        {
          add { AddHandler(MouseDownEvent, value); }
          remove { RemoveHandler(MouseDownEvent, value); }
        }

    For anyone who knows WPF, this should be straight forward.
    What's missing you might ask:
    - Proper event invocation
    - Hit testing
    - Adding all the events (and invoke them)
    That's it I guess.
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    One thing we need to consider here: the life time of resources is quite complicated in MPF. We have a "DeepCopy" logic to make sure that resources can be freed when they are no longer needed.

    There are following types of "resource lifetimes" (I hope this list is complete, as Albert wrote the logic):
    • Style resources: remain in memory as long as application runs
    • Screens: remain active once Workflow is in its state. After workflow transitions into a new workflow state, resources of former screen can be GCed
    When you deal with static event handler registration this could potentially introduce references that avoid proper GC. This needs to be checked thoroughly
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    The event handlers are not registered statically. It's the event definition that is registered statically. Same as in WPF.
    The event handlers are added directly to the UIElement (or subclasses).
    Only class handlers are registered globally, but only once per class, not per instance.
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    I think the basics are finished now.
    I have added (Preview)MouseDown and (Preview)MouseUp events to UIElement.
    The events get routed correctly through the InputManager dispatcher (I made a generic implementation for all routed events).
    Also hit testing is now performed correctly.
    In XAML event handler can be attached by the CommandStencil markup extension.
    I have currently two open topics, I have to look into:
    - Qualified event names in XAML (like Button.Click="... in an StackPanel)
    - Event triggers are most likely not useable with routed events so far.

    More events should be implemented as well, but specially with the key events I have to check that nothing gets broken.
    I'll make a code review and upload my changes, fro MP dev review then.
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    I have recognized some "confusion" with types for Point, Size, ...
    In some classes the WinFoms System.Darwing.PointF, ... are used (like InputManager)
    In other classes the (UIElement, ...) the SharpDX.Vector2 is used by defining "using PointF = SharpDX.Vector2;"
    This brings a bit confusion and unnecessary conversions with it.
    What is the intended type to use?
    From my point of view both are not my 1st choice:
    When using the WinFoms type, any plugin who needs to interact must reference WinForms (not a big deal, but not really necessary).
    When using SharpDX types, then every Plugin needs to reference SharpDX, which might be a bit more of a problem, if the renderer should be exchangeable.
    I would recommend to create some MP2 own types for these (like WPF does). By this a point can be called Point, ... Only in the rare classes where WinForms interop is needed, the name Point is a bit "ugly" b/c WinFoms has also a Point with in coordinates.
     

    osre

    Retired Team Member
  • Premium Supporter
  • December 14, 2014
    775
    387
    Home Country
    Germany Germany
    I started to implement the Qualified event names. The XAML parser runs into the attached properties section (as expected). I thought I could use part of the logic there (even the comments mention events already, so I guess it's intended).
    For my 1st try I added "UIElement.MouseDown=..." to my XAML screen.
    When it comes to resolving the type name UIElement the code runs into "MpfNamespaceHandler" which uses Mpf.ObjectClassRegistrations.
    This dictionary contains only non abstract types, which makes sense since it's used for which types can be instantiated in XAML.
    I think a nice extension would be to have abstract classes too, and have an optional parameter if abstract types are wanted as well.
    Also the dictionary could be filled automatically, using reflection.
    Do you think it's ok to implement changes like this to MpfNamespaceHandler?
    Currently I'm stuck here, because events from abstract classes must be able to use.
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    I have recognized some "confusion" with types for Point, Size, ...
    In some classes the WinFoms System.Darwing.PointF, ... are used (like InputManager)
    In other classes the (UIElement, ...) the SharpDX.Vector2 is used by defining "using PointF = SharpDX.Vector2;"
    For now I'd like to use the SharpDX.Vector2. The reason is, that I'm currently reworking the renderer to D2D. A global change would be better placed into (or after) this task.

    I think a nice extension would be to have abstract classes too, and have an optional parameter if abstract types are wanted as well.
    Yes, good idea. Go for it.
     

    Users who are viewing this thread

    Top Bottom