add MarginalView to MainView
authorMarcin Romaniuk <m.romaniuk@samsung.com>
Mon, 13 Sep 2021 16:39:57 +0000 (18:39 +0200)
committerPiotr Czaja/Advanced Frameworks (PLT) /SRPOL/Engineer/Samsung Electronics <p.czaja@samsung.com>
Mon, 4 Oct 2021 13:18:48 +0000 (15:18 +0200)
Fitness/ScalableUI/MarginalView.cs [new file with mode: 0644]
Fitness/res/layout/MainView.xaml

diff --git a/Fitness/ScalableUI/MarginalView.cs b/Fitness/ScalableUI/MarginalView.cs
new file mode 100644 (file)
index 0000000..b70188e
--- /dev/null
@@ -0,0 +1,231 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+
+namespace ScalableUI
+{
+    /// <summary>
+    /// MarginalView.
+    /// </summary>
+    public class MarginalView : View
+    {
+        private const float DebugOpacity = 0.4f;
+
+        private readonly View marginalContentView;
+        private readonly View sideLeft;
+        private readonly View sideRight;
+
+        private View debugLeftMargin;
+        private View debugMiddleContent;
+        private View debugRightMargin;
+
+        private int marginalWidth;
+
+        private float maxMargin = 0.25f;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MarginalView"/> class.
+        /// </summary>
+        public MarginalView()
+        {
+            WidthSpecification = LayoutParamPolicies.MatchParent;
+            ClippingMode = ClippingModeType.ClipChildren;
+
+            View container = new View()
+            {
+                Layout = new LinearLayout
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                },
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+            };
+            base.Add(container);
+
+            marginalContentView = new View()
+            {
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+            };
+            sideLeft = new View()
+            {
+                SizeWidth = 10,
+                BackgroundColor = Color.Red,
+            };
+            sideRight = new View()
+            {
+                SizeWidth = 10,
+                BackgroundColor = Color.Blue,
+            };
+            container.Add(sideLeft);
+            container.Add(marginalContentView);
+            container.Add(sideRight);
+
+            Relayout += OnRelayout;
+
+            InitDebug();
+        }
+
+        /// <summary>
+        /// Gets or sets MaxMargin.
+        /// </summary>
+        public float MaxMargin
+        {
+            get => maxMargin;
+            set
+            {
+                maxMargin = value;
+                RelayoutMarginalContentView();
+            }
+        }
+
+        /// <summary>
+        /// Gets MarginalWidth.
+        /// </summary>
+        public int MarginalWidth => marginalContentView.Size2D.Width;
+
+        /// <summary>
+        /// Sets a value indicating whether set.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool ShowMarginalDebugColors
+        {
+            set
+            {
+                if (value)
+                {
+                    debugLeftMargin.Show();
+                    debugMiddleContent.Show();
+                    debugRightMargin.Show();
+                }
+                else
+                {
+                    debugLeftMargin.Hide();
+                    debugMiddleContent.Hide();
+                    debugRightMargin.Hide();
+                }
+            }
+        }
+
+        private static Size MinBreakpointSize { get; } = new Size(1024, 720);
+
+        private static Size MaxBreakpointSize { get; } = new Size(4096, 2160);
+
+        /// <inheritdoc />
+        public override void Add(View child)
+        {
+            marginalContentView.Add(child);
+        }
+
+        /// <inheritdoc />
+        public override void Remove(View child)
+        {
+            marginalContentView.Remove(child);
+        }
+
+        private static List<float> GetMarginalBreakpoints(float min, float max, float margin)
+        {
+            var breakpoints = new List<float>();
+            float lastBreakpoint = min;
+
+            while (lastBreakpoint < max)
+            {
+                breakpoints.Add(lastBreakpoint);
+                lastBreakpoint = lastBreakpoint * (1 + margin);
+            }
+
+            return breakpoints;
+        }
+
+        private static float GetMarginalWidth(float margin)
+        {
+            bool isLandscape = Window.Instance.WindowSize.Width > Window.Instance.WindowSize.Height;
+            float min = isLandscape ? MinBreakpointSize.Width : MinBreakpointSize.Height;
+            float max = isLandscape ? MaxBreakpointSize.Width : MaxBreakpointSize.Height;
+            List<float> breakpoints = GetMarginalBreakpoints(min, max, margin);
+
+            float limit = Window.Instance.WindowSize.Width;
+            float breakpoint = breakpoints.Where(x => x <= limit).Last();
+
+            LogToDebug(breakpoints, limit, breakpoint, margin);
+
+            return breakpoint;
+        }
+
+        private static void LogToDebug(IEnumerable<float> breakpoints, float limit, float breakpoint, float margin)
+        {
+            IEnumerable<string> breakpoints__ = breakpoints.Select(x => Convert.ToInt32(x).ToString());
+            double effectiveMarginPixels = limit - breakpoint;
+            double effectiveMarginPercent = Math.Round(effectiveMarginPixels / limit, 2);
+
+            // Logger.Debug($"Window size {Window.Instance.WindowSize.Width} x {Window.Instance.WindowSize.Height} px");
+            // Logger.Debug($"Horizontal breakpoints [{string.Join(",", breakpoints__)}] px, Selected {breakpoint} px is below {Window.Instance.WindowSize.Width} px");
+            // Logger.Debug($"MaxMargin = {margin}, effective margin = {effectiveMarginPercent} ({effectiveMarginPixels} px)");
+        }
+
+        private void OnRelayout(object sender, EventArgs e)
+        {
+            RelayoutMarginalContentView();
+        }
+
+        private void RelayoutMarginalContentView()
+        {
+            marginalWidth = (int)GetMarginalWidth(MaxMargin);
+            int margin = (Window.Instance.WindowSize.Width - marginalWidth) / 2;
+
+            sideLeft.SizeWidth = margin;
+            sideRight.SizeWidth = margin;
+
+            RelayoutDebug(margin);
+        }
+
+        private void InitDebug()
+        {
+            View GetDebugContainer(Color color, string text)
+            {
+                View debugText = new TextLabel(text)
+                {
+                    PointSize = 3,
+                    TextColor = Color.Black,
+                    PositionUsesPivotPoint = true,
+                    PivotPoint = Position.PivotPointCenter,
+                    ParentOrigin = Position.ParentOriginCenter,
+                    HorizontalAlignment = HorizontalAlignment.Center,
+                    MultiLine = true,
+                };
+                View debugView = new View
+                {
+                    BackgroundColor = color,
+                    Opacity = DebugOpacity,
+                    ClippingMode = ClippingModeType.ClipChildren,
+                };
+                debugView.Add(debugText);
+                debugView.Hide();
+                return debugView;
+            }
+
+            debugLeftMargin = GetDebugContainer(Color.Red, "LEFT\nMARGIN");
+            debugMiddleContent = GetDebugContainer(Color.Green, "MARGINAL\nCONTENT");
+            debugRightMargin = GetDebugContainer(Color.Blue, "RIGHT\nMARGIN");
+
+            base.Add(debugLeftMargin);
+            base.Add(debugMiddleContent);
+            base.Add(debugRightMargin);
+        }
+
+        private void RelayoutDebug(int margin)
+        {
+            debugLeftMargin.Position2D = new Position2D(0, 0);
+            debugLeftMargin.Size2D = new Size2D(margin, Size2D.Height);
+
+            debugMiddleContent.Position2D = new Position2D(margin, 0);
+            debugMiddleContent.Size2D = new Size2D(marginalWidth, Size2D.Height);
+
+            debugRightMargin.Position2D = new Position2D(margin + marginalWidth, 0);
+            debugRightMargin.Size2D = new Size2D(margin, Size2D.Height);
+        }
+    }
+}
index 7cf8cb2a90dac98286d34b7c6802cb57d2d7f8c2..6c0fa2c0d5f04900a63218ab82cdeec88820350e 100644 (file)
@@ -6,6 +6,7 @@
            xmlns:views="clr-namespace:Fitness.Views"
            xmlns:ctrl="clr-namespace:Fitness.Controls"
            xmlns:nui="clr-namespace:Tizen.NUI.Components;assembly=Tizen.NUI.Components"
+           xmlns:sui="clr-namespace:ScalableUI"
            xmlns:converters="clr-namespace:Fitness.Views.Converters"
            xmlns:styles="clr-namespace:Fitness.Views.Styles"
            xmlns:behaviors="clr-namespace:Fitness.Views.Behaviors"
@@ -16,7 +17,7 @@
            ParentOrigin="Center"
            PivotPoint="Center"
            PositionUsesPivotPoint="true">
-    
+
     <View.BindingContext>
         <vm:MainViewModel x:Name="context"/>
     </View.BindingContext>
         <LinearLayout LinearOrientation="Vertical"/>
     </View.Layout>
 
-    <nui:Button BindingContext="{Binding Source={x:Reference context}}"
-                Size="{views:SizeInUnits Width=12, Height=12}"
-                Margin="{views:ExtentsInUnits Start=6, Bottom=8}"
-                behaviors:StyleSetter.Style="{x:Static styles:Buttons.Exit}"
-                Command="{Binding Exit}"/>
+    <!--top level view contains sui:MarginalView and nui:CollectionView-->
 
-    <View Margin="{views:ExtentsInUnits Start=16, End=16}"
-          WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
-          HeightSpecification="{Static LayoutParamPolicies.MatchParent}">
+    <sui:MarginalView ShowMarginalDebugColors="False"
+                      HeightSpecification="{Static LayoutParamPolicies.MatchParent}">
 
-        <View.Layout>
-            <LinearLayout LinearOrientation="Horizontal"/>
-        </View.Layout>
-        
-        <ImageView BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                   ResourceUrl="{Binding ThumbnailUrl}"
-                   WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
-                   HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
-                   x:Name="imagePreview"
-                   ctrl:Connected.Id="preview">
-            
-            <ImageView.Layout>
-                <LinearLayout LinearOrientation="Vertical"
-                              LinearAlignment="Center"
-                              CellPadding="{views:SizeInUnits Width=0, Height=10}"/>
-            </ImageView.Layout>
-            
-            <ctrl:NinePatchButton BindingContext="{Binding Source={x:Reference context}}"
-                                  PositionUsesPivotPoint="true"
-                                  ParentOrigin="0.5, 0.5"
-                                  PivotPoint="0.5, 0.5"
-                                  Text="Let's try!"
-                                  Command="{Binding StartWorkout}"
-                                  Size="{views:SizeInUnits Width=112, Height=24}"
-                                  behaviors:StyleSetter.Style="{x:Static styles:Buttons.Inverse}"/>
-            
-            <ctrl:NinePatchButton BindingContext="{Binding Source={x:Reference context}}"
-                                  PositionUsesPivotPoint="true"
-                                  ParentOrigin="0.5, 0.5"
-                                  PivotPoint="0.5, 0.5"
-                                  Text="Watch preview"
-                                  Command="{Binding WatchPreview}"
-                                  Size="{views:SizeInUnits Width=112, Height=24}"
-                                  behaviors:StyleSetter.Style="{x:Static styles:Buttons.Regular}"/>
-            
-        </ImageView>
-
-        <View Size="{views:SizeInUnits Width=114}"
+        <!--MarginalView must contain single view-->
+        <View WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
               HeightSpecification="{Static LayoutParamPolicies.MatchParent}">
-            
             <View.Layout>
-                <AbsoluteLayout/>
+                <LinearLayout LinearOrientation="Vertical"
+                              LinearAlignment="Top"/>
             </View.Layout>
 
-            <View>
-                
+            <nui:Button BindingContext="{Binding Source={x:Reference context}}"
+                        Size="{views:SizeInUnits Width=12, Height=12}"
+                        Margin="{views:ExtentsInUnits Start=6, Bottom=8}"
+                        behaviors:StyleSetter.Style="{x:Static styles:Buttons.Exit}"
+                         Command="{Binding Exit}"/>
+
+            <View  WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
+                   HeightSpecification="{Static LayoutParamPolicies.MatchParent}">
+
                 <View.Layout>
-                    <LinearLayout LinearOrientation="Vertical"
-                                  LinearAlignment="Begin"/>
+                    <LinearLayout LinearOrientation="Horizontal"/>
                 </View.Layout>
-                
-                <View Margin="{views:ExtentsInUnits Start=6, Top=3}">
-                    
-                    <View.Layout>
-                        <LinearLayout LinearOrientation="Horizontal"
-                                      LinearAlignment="Begin"/>
-                    </View.Layout>
-                    
-                    <TextLabel BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                               Text="{Binding Path=Title}"
-                               PixelSize="{views:PixelSizeInUnits UnitSize=10}"
-                               VerticalAlignment="Center"
-                               TextColor="#000C2B">
-                        <TextLabel.FontStyle>
-                            <PropertyMap>
-                                <KeyValue Key="weight" Value="regular" />
-                            </PropertyMap>
-                        </TextLabel.FontStyle>
-                    </TextLabel>
 
-                    <ImageView BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                               Size="{views:SizeInUnits Width=9, Height=12}"
-                               Margin="{views:ExtentsInUnits Start=7, End=7}"
-                               ResourceUrl="{Binding Difficulty, Converter={Static converters:DifficultyLevelToIconConverter.Converter}}"/>
-                    
-                </View>
-                
-                <View Margin="{views:ExtentsInUnits Start=6, Top=4}">
-                    
+                <ImageView BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
+                           ResourceUrl="{Binding ThumbnailUrl}"
+                           WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
+                           HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
+                           x:Name="imagePreview"
+                           ctrl:Connected.Id="preview"
+
+                           FittingMode="ShrinkToFit">
+
+                    <ImageView.Layout>
+                        <LinearLayout LinearOrientation="Vertical"
+                                      LinearAlignment="Center"
+                                      CellPadding="{views:SizeInUnits Width=0, Height=10}"/>
+                    </ImageView.Layout>
+
+                    <ctrl:NinePatchButton BindingContext="{Binding Source={x:Reference context}}"
+                                          PositionUsesPivotPoint="true"
+                                          ParentOrigin="0.5, 0.5"
+                                          PivotPoint="0.5, 0.5"
+                                          Text="Let's try!"
+                                          Command="{Binding StartWorkout}"
+                                          Size="{views:SizeInUnits Width=112, Height=24}"
+                                          behaviors:StyleSetter.Style="{x:Static styles:Buttons.Inverse}"/>
+
+                    <ctrl:NinePatchButton BindingContext="{Binding Source={x:Reference context}}"
+                                          PositionUsesPivotPoint="true"
+                                          ParentOrigin="0.5, 0.5"
+                                          PivotPoint="0.5, 0.5"
+                                          Text="Watch preview"
+                                          Command="{Binding WatchPreview}"
+                                          Size="{views:SizeInUnits Width=112, Height=24}"
+                                          behaviors:StyleSetter.Style="{x:Static styles:Buttons.Regular}"/>
+
+                </ImageView>
+
+                <View Size="{views:SizeInUnits Width=114}"
+                      HeightSpecification="{Static LayoutParamPolicies.MatchParent}">
+
                     <View.Layout>
-                        <LinearLayout LinearOrientation="Horizontal"
-                                      LinearAlignment="Begin"/>
+                        <AbsoluteLayout/>
                     </View.Layout>
-                    
-                    <ImageView Size="{views:SizeInUnits Width=7, Height=7}"
-                               ResourceUrl="*Resource*/layout/images/icon_time.png"/>
-                    
+
+                    <View>
+
+                        <View.Layout>
+                            <LinearLayout LinearOrientation="Vertical"
+                                          LinearAlignment="Begin"/>
+                        </View.Layout>
+
+                        <View Margin="{views:ExtentsInUnits Start=6, Top=3}">
+
+                            <View.Layout>
+                                <LinearLayout LinearOrientation="Horizontal"
+                                              LinearAlignment="Begin"/>
+                            </View.Layout>
+
+                            <TextLabel BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
+                                       Text="{Binding Path=Title}"
+                                       PixelSize="{views:PixelSizeInUnits UnitSize=10}"
+                                       VerticalAlignment="Center"
+                                       TextColor="#000C2B">
+                                <TextLabel.FontStyle>
+                                    <PropertyMap>
+                                        <KeyValue Key="weight" Value="regular" />
+                                    </PropertyMap>
+                                </TextLabel.FontStyle>
+                            </TextLabel>
+
+                            <ImageView BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
+                                       Size="{views:SizeInUnits Width=9, Height=12}"
+                                       Margin="{views:ExtentsInUnits Start=7, End=7}"
+                                       ResourceUrl="{Binding Difficulty, Converter={Static converters:DifficultyLevelToIconConverter.Converter}}" />
+
+                        </View>
+
+                        <View Margin="{views:ExtentsInUnits Start=6, Top=4}">
+
+                            <View.Layout>
+                                <LinearLayout LinearOrientation="Horizontal"
+                                              LinearAlignment="Begin"/>
+                            </View.Layout>
+
+                            <ImageView Size="{views:SizeInUnits Width=7, Height=7}"
+                                       ResourceUrl="*Resource*/layout/images/icon_time.png" />
+
+                            <TextLabel BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
+                                       Text="{Binding Duration, StringFormat=\{0:h\\:mm\\:ss\}}"
+                                       PixelSize="{views:PixelSizeInUnits UnitSize=7}"
+                                       Margin="{views:ExtentsInUnits Start=2}"
+                                       VerticalAlignment="Center"
+                                       TextColor="#000C2B">
+                                <TextLabel.FontStyle>
+                                    <PropertyMap>
+                                        <KeyValue Key="weight" Value="regular"/>
+                                    </PropertyMap>
+                                </TextLabel.FontStyle>
+                            </TextLabel>
+
+                        </View>
+
+                    </View>
+
+                    <ImageView PositionUsesPivotPoint="true"
+                               PivotPoint="1.0, 0.0"
+                               ParentOrigin="1.0, 0.0"
+                               Size="{views:SizeInUnits Width=10, Height=10}"
+                               BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
+                               ResourceUrl="{Binding Favourite, Converter={Static converters:FavouriteToIconConverter.Converter}}"/>
+
                     <TextLabel BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                               Text="{Binding Duration, StringFormat=\{0:h\\:mm\\:ss\}}"
+                               Padding="{views:ExtentsInUnits Start=4}"
+                               WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
+                               Text="{Binding Description}"
                                PixelSize="{views:PixelSizeInUnits UnitSize=7}"
-                               Margin="{views:ExtentsInUnits Start=2}"
-                               VerticalAlignment="Center"
-                               TextColor="#000C2B">
+                               LineWrapMode="Mixed"
+                               Weight="1.0"
+                               Ellipsis="false"
+                               MultiLine="true"
+                               TextColor="#000C2B"
+                               PositionUsesPivotPoint="true"
+                               PivotPoint="BottomLeft"
+                               ParentOrigin="BottomLeft">
                         <TextLabel.FontStyle>
                             <PropertyMap>
-                                <KeyValue Key="weight" Value="regular" />
+                                <KeyValue Key="weight" Value="regular"/>
                             </PropertyMap>
                         </TextLabel.FontStyle>
                     </TextLabel>
 
                 </View>
-
             </View>
 
-            <ImageView PositionUsesPivotPoint="true"
-                       PivotPoint="1.0, 0.0"
-                       ParentOrigin="1.0, 0.0"
-                       Size="{views:SizeInUnits Width=10, Height=10}"
-                       BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                       ResourceUrl="{Binding Favourite, Converter={Static converters:FavouriteToIconConverter.Converter}}"/>
-
-            <TextLabel BindingContext="{Binding Source={x:Reference context}, Path=SelectedWorkout}"
-                       Padding="{views:ExtentsInUnits Start=4}"
-                       WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
-                       Text="{Binding Description}"
-                       PixelSize="{views:PixelSizeInUnits UnitSize=7}"
-                       LineWrapMode="Mixed"
-                       Weight="1.0"
-                       Ellipsis="false"
-                       MultiLine="true"
-                       TextColor="#000C2B"
-                       PositionUsesPivotPoint="true"
-                       PivotPoint="BottomLeft"
-                       ParentOrigin="BottomLeft">
-                <TextLabel.FontStyle>
-                    <PropertyMap>
-                        <KeyValue Key="weight" Value="regular" />
-                    </PropertyMap>
-                </TextLabel.FontStyle>
-            </TextLabel>
-            
-        </View>        
-    </View>
+        </View>
+    </sui:MarginalView>
 
     <nui:CollectionView Size="{views:SizeInUnits Height=44}"
                         Margin="{views:ExtentsInUnits Top=10}"
                 <views:FitnessItemView />
             </DataTemplate>
         </nui:CollectionView.ItemTemplate>
-        
+
         <nui:CollectionView.ItemsLayouter>
             <nui:GridLayouter />
         </nui:CollectionView.ItemsLayouter>
-    
+
     </nui:CollectionView>
 
 </ctrl:Page>