From f0adbcc9e60ae871d667afb610c7b4718b73f292 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Sat, 27 Mar 2021 18:45:57 +0100 Subject: [PATCH] simplified tabView as TemplatedGroup --- Crow/Default.style | 13 - Crow/Templates/TabView.template | 17 + Crow/src/Widgets/TabItem.cs | 329 ------------------ Crow/src/Widgets/TabView.cs | 322 +---------------- .../Experimental/SimplifiedTabView.crow | 11 + .../Interfaces/Experimental/tabviewTest.crow | 47 +-- .../TemplatedContainer/testTabView.crow | 51 ++- 7 files changed, 84 insertions(+), 706 deletions(-) create mode 100644 Crow/Templates/TabView.template delete mode 100644 Crow/src/Widgets/TabItem.cs create mode 100644 Samples/common/ui/Interfaces/Experimental/SimplifiedTabView.crow diff --git a/Crow/Default.style b/Crow/Default.style index f2f94823..0e442eb6 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -137,19 +137,6 @@ Splitter { StickyMouse="10"; Background = "Grey"; } - -TabView { - CacheEnabled = "false"; - AllowDrop = "true"; -} -TabItem { - Caption = "TabItem"; - Focusable = "true"; - CacheEnabled = "true"; - //MouseEnter = "{Background = Cobalt;Foreground=White;}"; - //MouseLeave = "{Foreground=LightGrey;Background=Jet;}"; - AllowDrag = "true"; -} Window { Caption = "Window"; Focusable = "true"; diff --git a/Crow/Templates/TabView.template b/Crow/Templates/TabView.template new file mode 100644 index 00000000..af4eae07 --- /dev/null +++ b/Crow/Templates/TabView.template @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/Crow/src/Widgets/TabItem.cs b/Crow/src/Widgets/TabItem.cs deleted file mode 100644 index cc6c489b..00000000 --- a/Crow/src/Widgets/TabItem.cs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.ComponentModel; -using Crow.Cairo; -using System.Linq; - -namespace Crow -{ - public class TabItem : TemplatedContainer - { - #region CTOR - protected TabItem() {} - public TabItem (Interface iface, string style = null) : base (iface, style) { } - #endregion - - public event EventHandler QueryClose; - - internal TabView tview = null; - - #region Private fields - Widget titleWidget; - int tabOffset; - bool isSelected; - //Measure tabThickness; - Fill selectedBackground = Colors.Transparent; - #endregion - - #region TemplatedControl overrides - public override Widget Content { - get { - return _contentContainer == null ? null : _contentContainer.Child; - } - set { - if (Content != null) { - Content.LogicalParent = null; - _contentContainer.SetChild (null); - } - _contentContainer.SetChild(value); - if (value != null) - value.LogicalParent = this; - } - } - protected override void loadTemplate(Widget template = null) - { - base.loadTemplate (template); - - titleWidget = this.child.FindByName ("TabTitle"); - } - internal Widget TabTitle { get { return titleWidget; }} - #endregion - - /// - /// order of redrawing, items can't be reordered in TemplatedGroup due to data linked, so we need another index - /// instead of children list order - /// - public int viewIndex = 0; - public virtual int ViewIndex { - get { return viewIndex; } - set { - if (viewIndex == value) - return; - viewIndex = value; - NotifyValueChangedAuto (viewIndex); - } - } - - [DefaultValue(0)] - public int TabOffset { - get { return tabOffset; } - set { - if (tabOffset == value) - return; - tabOffset = value; - NotifyValueChangedAuto (tabOffset); - - RegisterForLayouting (LayoutingType.X); - RegisterForGraphicUpdate (); - } - } - public Measure TabHeight { - get { return tview == null ? Measure.Fit : tview.TabHeight; } - } - public bool IsActiveTab => tview?.ActiveTab == this; - public Measure TabWidth { - get { return tview == null ? Measure.Fit : tview.TabWidth; } - } - [DefaultValue(false)] - public virtual bool IsSelected { - get { return isSelected; } - set { - if (isSelected == value) - return; - - //Console.WriteLine ($"TabItem({this.dataSource}).IsSelected: {isSelected} -> {value}"); - /*if (tview != null) - tview.SelectedTab = tview.Children.IndexOf(this);*/ - //Console.WriteLine ($"TabView.IsSelected({this.Caption}): {isSelected} -> {value}"); - //Console.WriteLine (new System.Diagnostics.StackTrace()); - - isSelected = value; - - if (IsSelected) - tview.ActiveTab = this; - - NotifyValueChangedAuto (isSelected); - RegisterForRedraw (); - } - } - - /// - /// background fill of the control, maybe solid color, gradient, image, or svg - /// - [DesignCategory ("Appearance")][DefaultValue("DimGrey")] - public virtual Fill SelectedBackground { - get { return selectedBackground; } - set { - if (selectedBackground == value) - return; - if (value == null) - return; - selectedBackground = value; - NotifyValueChangedAuto (selectedBackground); - RegisterForRedraw (); - } - } - protected override void onDraw (Context gr) - { - gr.Save (); - - parentRWLock.EnterReadLock (); - - TabView tv = Parent as TabView; - - //TODO:this appens in designView - if (tv == null) { - parentRWLock.ExitReadLock (); - return; - } - - Rectangle r = TabTitle.Slot; - r.Width = TabWidth; - - gr.MoveTo (0.5, r.Bottom-0.5); - gr.LineTo (r.Left - tv.LeftSlope, r.Bottom-0.5); - gr.CurveTo ( - r.Left - tv.LeftSlope / 2, r.Bottom-0.5, - r.Left - tv.LeftSlope / 2, 0.5, - r.Left, 0.5); - gr.LineTo (r.Right, 0.5); - gr.CurveTo ( - r.Right + tv.RightSlope / 2, 0.5, - r.Right + tv.RightSlope / 2, r.Bottom-0.5, - r.Right + tv.RightSlope, r.Bottom-0.5); - gr.LineTo (Slot.Width-0.5, r.Bottom-0.5); - - parentRWLock.ExitReadLock (); - - gr.LineTo (Slot.Width-0.5, Slot.Height-0.5); - gr.LineTo (0.5, Slot.Height-0.5); - gr.ClosePath (); - gr.LineWidth = 1; - Foreground.SetAsSource (IFace, gr); - gr.StrokePreserve (); - gr.ClipPreserve (); - - if (IsActiveTab) - SelectedBackground.SetAsSource (IFace, gr, ClientRectangle); - else - Background.SetAsSource (IFace, gr, ClientRectangle); - - gr.Fill (); - - base.onDraw (gr); - - gr.Restore (); - } - - Point dragStartPoint; - int dragThreshold = 16; - int dis = 128; - internal TabView savedParent = null; - - - void makeFloating (TabView tv) { - lock (IFace.UpdateMutex) { - ImageSurface di = new ImageSurface (Format.Argb32, dis, dis); - IFace.DragImageHeight = dis; - IFace.DragImageWidth = dis; - using (Context ctx = new Context (di)) { - double div = Math.Max (LastPaintedSlot.Width, LastPaintedSlot.Height); - double s = (double)dis / div; - ctx.Scale (s, s); - if (bmp == null) - this.onDraw (ctx); - else { - if (LastPaintedSlot.Width>LastPaintedSlot.Height) - ctx.SetSource (bmp, 0, (LastPaintedSlot.Width-LastPaintedSlot.Height)/2); - else - ctx.SetSource (bmp, (LastPaintedSlot.Height-LastPaintedSlot.Width)/2, 0); - - ctx.Paint (); - } - } - IFace.DragImage = di; - } - tv.RemoveChild (this); - savedParent = tv; - } - - public override ILayoutable Parent { - get { - return base.Parent; - } - set { - base.Parent = value; - if (value != null) { - dragStartPoint = IFace.MousePosition; - savedParent = value as TabView; - } - } - } - protected override void onStartDrag (object sender, DragDropEventArgs e) - { - base.onStartDrag (sender, e); - - dragStartPoint = IFace.MousePosition; - } - public override void onEndDrag (object sender, DragDropEventArgs e) - { - base.onEndDrag (sender, e); - - if (Parent != null) - return; - - savedParent.AddChild (this); - - IFace.ClearDragImage (); - } - public override void onDrop (object sender, DragDropEventArgs e) - { - base.onDrop (sender, e); - if (Parent != null) - return; - TabView tv = e.DropTarget as TabView; - if (tv == null) - return; - - IFace.ClearDragImage (); - - tv.AddChild (this); - } - #region Mouse Handling - public override bool PointIsIn (ref Point m) - { - if (!base.PointIsIn (ref m)) - return false; - if (tview == null)//double check this, just added to prevent exception - return false; - if (m.Y < tview.TabHeight) - return TabTitle.Slot.ContainsOrIsEqual (m); - else - return this.isSelected; - } - public override void onMouseUp (object sender, MouseButtonEventArgs e) - { - base.onMouseUp (sender, e); - tview?.UpdateLayout (LayoutingType.ArrangeChildren); - } - public override void onMouseMove (object sender, MouseMoveEventArgs e) - { - base.onMouseMove (sender, e); - - if (Parent == null) - return; - - if (!IsDragged) - return; - - TabView tv = Parent as TabView; - if (Math.Abs (e.Position.Y - dragStartPoint.Y) > dragThreshold || - Math.Abs (e.Position.X - dragStartPoint.X) > dragThreshold) { - makeFloating (tv); - return; - } - - Rectangle cb = ClientRectangle; - - int tmp = TabOffset + e.XDelta; - if (tmp < tview.LeftSlope) { - TabOffset = tview.LeftSlope; - } else if (tmp > cb.Width - tv.RightSlope - tv.TabWidth) { - TabOffset = cb.Width - tv.RightSlope - tv.TabWidth; - }else{ - dragStartPoint.X = e.Position.X; - TabItem[] tabItms = tv.Children.Cast().OrderBy (t=>t.ViewIndex).ToArray(); - if (ViewIndex > 0 && e.XDelta < 0) { - TabItem previous = tabItms [ViewIndex - 1]; - if (tmp < previous.TabOffset + tview.TabWidth / 2) { - previous.ViewIndex = ViewIndex; - ViewIndex--; - tv.UpdateLayout (LayoutingType.ArrangeChildren); - } - - }else if (ViewIndex < tabItms.Length - 1 && e.XDelta > 0) { - TabItem next = tabItms [ViewIndex + 1]; - if (tmp > next.TabOffset - tview.TabWidth / 2){ - next.ViewIndex = ViewIndex; - ViewIndex++; - tv.UpdateLayout (LayoutingType.ArrangeChildren); - } - } - TabOffset = tmp; - } - } - public void butCloseTabClick (object sender, MouseButtonEventArgs e){ - QueryClose.Raise (this, null); - //if tab is used as a templated item root in a templatedGroup, local datasource - //is not null, in this case, removing the data entries will delete automatically the item - if (localDataSourceIsNull) - (Parent as TabView)?.DeleteChild (this); - } - #endregion - - } -} - diff --git a/Crow/src/Widgets/TabView.cs b/Crow/src/Widgets/TabView.cs index 3067243d..440322fd 100644 --- a/Crow/src/Widgets/TabView.cs +++ b/Crow/src/Widgets/TabView.cs @@ -9,330 +9,28 @@ using System.Linq; namespace Crow { - public class TabView : Group + public class TabView : TemplatedGroup { #region CTOR public TabView () { } public TabView (Interface iface, string style = null) : base (iface, style) { } #endregion - #region Private fields - int adjustedTab = -1; - int leftSlope; - int rightSlope; - Measure tabHeight, tabWidth; - Orientation _orientation; - TabItem activeTab; - bool activateNewTab; - #endregion + Orientation orientation; - #region public properties [DefaultValue (Orientation.Horizontal)] - public virtual Orientation Orientation { - get { return _orientation; } - set { - if (_orientation == value) - return; - _orientation = value; - NotifyValueChangedAuto (_orientation); - if (_orientation == Orientation.Horizontal) - NotifyValueChanged ("TabOrientation", Orientation.Vertical); - else - NotifyValueChanged ("TabOrientation", Orientation.Horizontal); - this.RegisterForLayouting (LayoutingType.ArrangeChildren); - } - } - [DefaultValue (16)] - public int LeftSlope { - get { return leftSlope; } - set { - if (leftSlope == value) - return; - leftSlope = value; - NotifyValueChangedAuto (leftSlope); - //tabSizeHasChanged = true; - //RegisterForLayouting (LayoutingType.ArrangeChildren); - } - } - //bool tabSizeHasChanged = false; - [DefaultValue (16)] - public int RightSlope { - get { return rightSlope; } - set { - if (rightSlope == value) - return; - rightSlope = value; - NotifyValueChangedAuto (rightSlope); - //tabSizeHasChanged = true; - //RegisterForLayouting (LayoutingType.ArrangeChildren); - } - } - [DefaultValue ("18")] - public Measure TabHeight { - get { return tabHeight; } - set { - if (tabHeight == value) - return; - tabHeight = value; - NotifyValueChangedAuto (tabHeight); - // childrenRWLock.EnterReadLock (); - // foreach (GraphicObject ti in Children) { - // ti.NotifyValueChanged ("TabHeight", tabHeight); - // } - // childrenRWLock.ExitReadLock (); - RegisterForLayouting (LayoutingType.ArrangeChildren); - } - } - [DefaultValue ("120")] - public Measure TabWidth { - get { return adjustedTab > 0 ? (Measure)adjustedTab : tabWidth; } + public Orientation Orientation { + get => orientation; set { - if (tabWidth == value) - return; - tabWidth = value; - NotifyValueChangedAuto (TabWidth); - // - // childrenRWLock.EnterReadLock (); - // foreach (GraphicObject ti in Children) { - // ti.NotifyValueChanged ("TabWidth", tabWidth); - // } - // childrenRWLock.ExitReadLock (); - RegisterForLayouting (LayoutingType.ArrangeChildren); - } - } - /// - /// If true new tabs will be set as the active tab of this tabview. - /// - [DefaultValue (true)] - public bool ActivateNewTab { - get => activateNewTab; - set { - if (activateNewTab == value) - return; - activateNewTab = value; - NotifyValueChangedAuto (activateNewTab); - } - } - public virtual TabItem ActiveTab { - get => activeTab; - set { - if (activeTab == value) + if (orientation == value) return; - - //Console.WriteLine ($"TabView.ActiveTab: {activeTab?.DataSource} -> {value?.DataSource}"); - - if (value != null) { - if (activeTab != null) { - activeTab.IsSelected = false; - ActiveTab.NotifyValueChanged ("IsActiveTab", false); - } - activeTab = value; - ActiveTab.IsSelected = true; - ActiveTab.NotifyValueChanged ("IsActiveTab", true); - } else - activeTab = value; - - NotifyValueChangedAuto (activeTab); - RegisterForRedraw (); - } - } - #endregion - - public override void AddChild (Widget child) { - TabItem ti = child as TabItem; - if (ti == null) - throw new Exception ("TabView control accept only TabItem as child."); - - ti.MouseDown += Ti_MouseDown; - ti.TabTitle.LayoutChanged += Ti_TabTitle_LayoutChanged; - ti.tview = this; - - base.AddChild (child); - - ti.ViewIndex = Children.Count - 1; - - if (ActivateNewTab || ti.ViewIndex == 0) - ti.IsSelected = true; - - this.RegisterForLayouting (LayoutingType.ArrangeChildren); - } - public override void RemoveChild (Widget child) { - TabItem ti = child as TabItem; - if (ti == null) - throw new Exception ("TabView control accept only TabItem as child."); - - ti.MouseDown -= Ti_MouseDown; - ti.TabTitle.LayoutChanged -= Ti_TabTitle_LayoutChanged; - ti.tview = null; - - childrenRWLock.EnterReadLock (); - - TabItem[] tabItms = Children.Cast ().OrderBy (t => t.ViewIndex).ToArray (); - if (ActiveTab == ti) { - if (tabItms.Length > 1) { - if (ti.ViewIndex == tabItms.Length - 1) - ActiveTab = tabItms[ti.ViewIndex - 1]; - else - ActiveTab = tabItms[ti.ViewIndex + 1]; - } else - ActiveTab = null; + orientation = value; + NotifyValueChangedAuto (orientation); + NotifyValueChanged ("OppositeOrientation", OppositeOrientation); } - for (int i = ti.viewIndex + 1; i < tabItms.Length; i++) - tabItms[i].ViewIndex--; - - /*int selTabViewIdx = -1; - - if (SelectedTab < tabItms.Length && SelectedTab >= 0) - selTabViewIdx = (Children [SelectedTab] as TabItem).ViewIndex; - - for (int i = selTabViewIdx+1; i < tabItms.Length; i++) - tabItms [i].ViewIndex--; - - if (selTabViewIdx > tabItms.Length - 2) - selTabViewIdx = tabItms.Length - 2; - - if (selTabViewIdx < 0) - SelectedTab = -1; - else - SelectedTab = Children.IndexOf (tabItms [selTabViewIdx]);*/ - - childrenRWLock.ExitReadLock (); - - base.RemoveChild (child); - } - - public override bool ArrangeChildren { get { return true; } } - public override bool UpdateLayout (LayoutingType layoutType) { - RegisteredLayoutings &= (~layoutType); - - if (layoutType == LayoutingType.ArrangeChildren && Children.Count > 0) { - Rectangle cb = ClientRectangle; - - int tabSpace = tabWidth + leftSlope; - int tc = Children.Count (c => c.Visible == true); - - if (tc > 0) - tabSpace = Math.Min (tabSpace, (cb.Width - rightSlope) / tc); - - if (tabSpace < tabWidth + leftSlope) - adjustedTab = tabSpace - leftSlope; - else - adjustedTab = -1; - - //System.Diagnostics.Debug.WriteLine ("tabspace: {0} tw:{1}", tabSpace, tabWidth); - - childrenRWLock.EnterReadLock (); - TabItem[] tabItms = Children.Cast ().OrderBy (t => t.ViewIndex).ToArray (); - childrenRWLock.ExitReadLock (); - int curOffset = leftSlope; - - for (int i = 0; i < tabItms.Length; i++) { - if (!tabItms[i].Visible) - continue; - tabItms[i].NotifyValueChanged ("TabHeight", tabHeight); - tabItms[i].NotifyValueChanged ("TabWidth", TabWidth); - if (!tabItms[i].IsDragged) { - tabItms[i].TabOffset = curOffset; - //System.Diagnostics.Debug.WriteLine ("offset: {0}=>{1}", tabItms [i].Name, tabItms [i].TabOffset); - } - if (Orientation == Orientation.Horizontal) { - curOffset += tabSpace; - } else - curOffset += tabSpace; - } - - //if no layouting remains in queue for item, registre for redraw - if (RegisteredLayoutings == LayoutingType.None && IsDirty) - IFace.EnqueueForRepaint (this); - - return true; - } - - return base.UpdateLayout (layoutType); - } - public override void OnLayoutChanges (LayoutingType layoutType) { - if (_orientation == Orientation.Horizontal) { - if (layoutType == LayoutingType.Width) - RegisterForLayouting (LayoutingType.ArrangeChildren); - } else if (layoutType == LayoutingType.Height) - RegisterForLayouting (LayoutingType.ArrangeChildren); - - base.OnLayoutChanges (layoutType); - } - - internal TabItem[] VisibleTabsByViewIdx => - Children.Where (tt => tt.Visible).Cast (). - OrderBy (t => t.ViewIndex).ToArray (); - - protected override void onDraw (Context gr) { - Rectangle rBack = new Rectangle (Slot.Size); - - Background.SetAsSource (IFace, gr, rBack); - CairoHelpers.CairoRectangle (gr, rBack, CornerRadius); - gr.Fill (); - - gr.Save (); - - if (ClipToClientRect) { - //clip to client zone - CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius); - gr.Clip (); - } - - childrenRWLock.EnterReadLock (); - - TabItem[] tabItms = VisibleTabsByViewIdx; - - TabItem sti = ActiveTab; - int selTabViewIdx = sti == null ? tabItms.Length - 1 : sti.ViewIndex; - - int i = 0; - while (i < selTabViewIdx) { - tabItms[i].Paint (gr); - i++; - } - i = tabItms.Length - 1; - while (i > selTabViewIdx) { - tabItms[i].Paint (gr); - i--; - } - - if (selTabViewIdx >= 0 && selTabViewIdx < tabItms.Length) - tabItms[selTabViewIdx].Paint (gr); - - childrenRWLock.ExitReadLock (); - - gr.Restore (); - } - - protected override void onDragEnter (object sender, DragDropEventArgs e) { - base.onDragEnter (sender, e); - - TabItem ti = e.DragSource as TabItem; - if (ti == null) - return; - if (ti.Parent != null || ti.savedParent == this) - return; - - this.AddChild (ti); - - Point p = ScreenPointToLocal (IFace.MousePosition) - Margin; - - p.X = Math.Max (leftSlope, p.X); - p.X = Math.Min (ClientRectangle.Width - rightSlope - TabWidth, p.X); - ti.TabOffset = p.X; - - IFace.ClearDragImage (); - - } - - void Ti_TabTitle_LayoutChanged (object sender, LayoutingEventArgs e) { - if (e.LayoutType == LayoutingType.X) - RegisterForLayouting (LayoutingType.ArrangeChildren); - } - void Ti_MouseDown (object sender, MouseButtonEventArgs e) { - ActiveTab = sender as TabItem; } + public Orientation OppositeOrientation + => orientation == Orientation.Vertical ? Orientation.Horizontal : Orientation.Vertical; } } diff --git a/Samples/common/ui/Interfaces/Experimental/SimplifiedTabView.crow b/Samples/common/ui/Interfaces/Experimental/SimplifiedTabView.crow new file mode 100644 index 00000000..7082be3f --- /dev/null +++ b/Samples/common/ui/Interfaces/Experimental/SimplifiedTabView.crow @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/common/ui/Interfaces/Experimental/tabviewTest.crow b/Samples/common/ui/Interfaces/Experimental/tabviewTest.crow index c578b0ca..f874a2c6 100644 --- a/Samples/common/ui/Interfaces/Experimental/tabviewTest.crow +++ b/Samples/common/ui/Interfaces/Experimental/tabviewTest.crow @@ -1,22 +1,25 @@ - - - - - - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/Samples/common/ui/Interfaces/TemplatedContainer/testTabView.crow b/Samples/common/ui/Interfaces/TemplatedContainer/testTabView.crow index 7aa4cdd5..f54a7182 100644 --- a/Samples/common/ui/Interfaces/TemplatedContainer/testTabView.crow +++ b/Samples/common/ui/Interfaces/TemplatedContainer/testTabView.crow @@ -1,37 +1,28 @@  - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + + + + -