add tiling frames
This commit is contained in:
parent
30a11c7b0b
commit
8c2a41a133
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls.DragAndDrop;
|
||||||
|
|
||||||
|
public abstract partial class DragAndDropControl : Control
|
||||||
|
{
|
||||||
|
public virtual void OnDragStart(object model, UIElement draggedElement) { }
|
||||||
|
|
||||||
|
public virtual void OnDragEnd(object model, UIElement targetElement) { }
|
||||||
|
|
||||||
|
public virtual void OnDrag(object sender, MouseEventArgs args) { }
|
||||||
|
|
||||||
|
#region Dependency Properties
|
||||||
|
|
||||||
|
public bool AllowDrag {
|
||||||
|
get => (bool)GetValue(AllowDragProperty);
|
||||||
|
set => SetValue(AllowDragProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty AllowDragProperty = DependencyProperty
|
||||||
|
.Register(
|
||||||
|
nameof(AllowDrag),
|
||||||
|
typeof(bool),
|
||||||
|
typeof(DragAndDropControl),
|
||||||
|
new PropertyMetadata(true));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Attached Properties
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsDraggableProperty = DependencyProperty
|
||||||
|
.RegisterAttached(
|
||||||
|
"IsDraggable",
|
||||||
|
typeof(bool),
|
||||||
|
typeof(DragAndDropControl),
|
||||||
|
new PropertyMetadata(false, OnIsDraggableChanged));
|
||||||
|
|
||||||
|
public static bool GetIsDraggable(DependencyObject obj) => (bool)obj.GetValue(IsDraggableProperty);
|
||||||
|
public static void SetIsDraggable(DependencyObject obj, bool value) => obj.SetValue(IsDraggableProperty, value);
|
||||||
|
|
||||||
|
private static void OnIsDraggableChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is UIElement element && (bool)e.NewValue) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleDragStart(object sender, MouseButtonEventArgs args, DragAndDropControl dndControl)
|
||||||
|
{
|
||||||
|
if (sender is UIElement element) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleDrop(object sender, MouseButtonEventArgs args, DragAndDropControl dndControl)
|
||||||
|
{
|
||||||
|
// moi schaun
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? FindVisualParent<T>(DependencyObject child) where T : DependencyObject
|
||||||
|
{
|
||||||
|
// Todo: find a better solution to this, if there even is one.
|
||||||
|
// There are, in fact, many controls even in the WPF standard that do similar lookups,
|
||||||
|
// so I assume it can't be _that_ bad, but every clean solution kind of seems to need some ugly spot in it. :(
|
||||||
|
|
||||||
|
DependencyObject parent = VisualTreeHelper.GetParent(child);
|
||||||
|
while (parent != null) {
|
||||||
|
if (parent is T expected)
|
||||||
|
return expected;
|
||||||
|
parent = VisualTreeHelper.GetParent(parent);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty HandlerProperty = DependencyProperty
|
||||||
|
.RegisterAttached(
|
||||||
|
"Handler",
|
||||||
|
typeof(DependencyObject),
|
||||||
|
typeof(DragAndDropControl),
|
||||||
|
new PropertyMetadata(null, OnHandlerChanged));
|
||||||
|
|
||||||
|
public static DependencyObject GetHandler(DependencyObject obj) => (DependencyObject)obj.GetValue(HandlerProperty);
|
||||||
|
public static void SetHandler(DependencyObject obj, bool value) => obj.SetValue(HandlerProperty, value);
|
||||||
|
|
||||||
|
private static void OnHandlerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is UIElement element && e.NewValue is DragAndDropControl dndControl) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"Can not set {e.NewValue} as handler. Handler needs to be of type DragAndDropControl.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using CopaData.FileInspector.GUI.TilingPanels.Controls.DragAndDrop;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea;
|
||||||
|
|
||||||
|
public class DropArea : DragAndDropControl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea;
|
||||||
|
|
||||||
|
public delegate void DropAreaDecisionEvent(DropArea sender, HostControl host, DropDecision decision);
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Controls.DropArea;
|
||||||
|
|
||||||
|
public enum DropDecision
|
||||||
|
{
|
||||||
|
Cancel = 0,
|
||||||
|
Center,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Bottom
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Qrakhen.TilingFrames.Controls.DragAndDrop;
|
||||||
|
using Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control that represents the state of a <see cref="Models.TilingHost"/>,
|
||||||
|
/// with all the necessary interaction logic tied to it.
|
||||||
|
/// </summary>
|
||||||
|
public class HostControl : DragAndDropControl
|
||||||
|
{
|
||||||
|
public TilingHost? Host => DataContext as TilingHost;
|
||||||
|
|
||||||
|
static HostControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(HostControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(HostControl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostControl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DependencyProperties
|
||||||
|
|
||||||
|
/*public Host Host
|
||||||
|
{
|
||||||
|
get => (Host)GetValue(HostProperty);
|
||||||
|
set => SetValue(HostProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty HostProperty = DependencyProperty
|
||||||
|
.Register(nameof(Host),
|
||||||
|
typeof(Host),
|
||||||
|
typeof(HostControl),
|
||||||
|
new PropertyMetadata(null));*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control that represents the state of a <see cref="HostFrame"/>,
|
||||||
|
/// with all the necessary interaction logic tied to it.
|
||||||
|
/// </summary>
|
||||||
|
public class HostFrameControl : ContentControl
|
||||||
|
{
|
||||||
|
static HostFrameControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(HostFrameControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(HostFrameControl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostFrameControl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DependencyProperties
|
||||||
|
|
||||||
|
public HostFrame Frame {
|
||||||
|
get => (HostFrame)GetValue(FrameProperty);
|
||||||
|
set => SetValue(FrameProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty FrameProperty = DependencyProperty
|
||||||
|
.Register(nameof(Frame),
|
||||||
|
typeof(HostFrame),
|
||||||
|
typeof(HostFrameControl),
|
||||||
|
new PropertyMetadata(null));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
using Qrakhen.TilingFrames.Models;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls;
|
||||||
|
|
||||||
|
public class PanelControl : Control
|
||||||
|
{
|
||||||
|
static PanelControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(PanelControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(PanelControl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PanelControl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DependencyProperties
|
||||||
|
|
||||||
|
public Orientation SplitOrientation => Panel.SplitOrientation;
|
||||||
|
|
||||||
|
public TilingNode Alpha => Panel.Alpha;
|
||||||
|
public TilingNode? Beta => Panel.Beta;
|
||||||
|
|
||||||
|
public Models.TilingPanel Panel {
|
||||||
|
get => (Models.TilingPanel)GetValue(PanelProperty);
|
||||||
|
set => SetValue(PanelProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty PanelProperty = DependencyProperty
|
||||||
|
.Register(nameof(Panel),
|
||||||
|
typeof(Models.TilingPanel),
|
||||||
|
typeof(PanelControl),
|
||||||
|
new PropertyMetadata(null));
|
||||||
|
|
||||||
|
public double SplitterWidth {
|
||||||
|
get => (double)GetValue(SplitterWidthProperty);
|
||||||
|
set => SetValue(SplitterWidthProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty SplitterWidthProperty = DependencyProperty
|
||||||
|
.Register(
|
||||||
|
nameof(SplitterWidth),
|
||||||
|
typeof(double),
|
||||||
|
typeof(PanelControl),
|
||||||
|
new PropertyMetadata(8.0));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to force-update columns into rows when orientation is vertical
|
||||||
|
/// </summary>
|
||||||
|
public static class TilingGridAttachment
|
||||||
|
{
|
||||||
|
#region AlphaRow
|
||||||
|
public static readonly DependencyProperty AlphaRowProperty =
|
||||||
|
DependencyProperty.RegisterAttached(
|
||||||
|
"AlphaRow",
|
||||||
|
typeof(int),
|
||||||
|
typeof(TilingGridAttachment),
|
||||||
|
new PropertyMetadata(0, OnAlphaRowChanged));
|
||||||
|
|
||||||
|
public static int GetAlphaRow(DependencyObject obj)
|
||||||
|
=> (int)obj.GetValue(AlphaRowProperty);
|
||||||
|
public static void SetAlphaRow(DependencyObject obj, int value)
|
||||||
|
=> obj.SetValue(AlphaRowProperty, value);
|
||||||
|
|
||||||
|
private static void OnAlphaRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is ContentPresenter presenter) {
|
||||||
|
Grid.SetRow(presenter, (int)e.NewValue);
|
||||||
|
Grid.SetColumn(presenter, 0); // Always Column 0 for vertical split
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region BetaRow
|
||||||
|
public static readonly DependencyProperty BetaRowProperty =
|
||||||
|
DependencyProperty.RegisterAttached(
|
||||||
|
"BetaRow",
|
||||||
|
typeof(int),
|
||||||
|
typeof(TilingGridAttachment),
|
||||||
|
new PropertyMetadata(0, OnBetaRowChanged));
|
||||||
|
|
||||||
|
public static int GetBetaRow(DependencyObject obj)
|
||||||
|
=> (int)obj.GetValue(BetaRowProperty);
|
||||||
|
public static void SetBetaRow(DependencyObject obj, int value)
|
||||||
|
=> obj.SetValue(BetaRowProperty, value);
|
||||||
|
|
||||||
|
private static void OnBetaRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is ContentPresenter presenter) {
|
||||||
|
Grid.SetRow(presenter, (int)e.NewValue);
|
||||||
|
Grid.SetColumn(presenter, 0); // Always Column 0 for vertical split
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using Qrakhen.TilingFrames.Models;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For Pop-Outs etc.
|
||||||
|
/// </summary>
|
||||||
|
public class TilingWindowControl : Window
|
||||||
|
{
|
||||||
|
static TilingWindowControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingWindowControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingWindowControl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TilingPanel Root {
|
||||||
|
get => (TilingPanel)GetValue(RootProperty);
|
||||||
|
set => SetValue(RootProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty RootProperty = DependencyProperty
|
||||||
|
.Register(
|
||||||
|
nameof(Root),
|
||||||
|
typeof(TilingPanel),
|
||||||
|
typeof(TilingWindowControl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TilingRootControl : TilingPanelControl
|
||||||
|
{
|
||||||
|
static TilingRootControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingRootControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingRootControl)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TilingPanelControl : Control
|
||||||
|
{
|
||||||
|
static TilingPanelControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingPanelControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingPanelControl)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TilingHostControl : Control
|
||||||
|
{
|
||||||
|
static TilingHostControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingHostControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingHostControl)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TilingFrameControl : ContentControl
|
||||||
|
{
|
||||||
|
static TilingFrameControl()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingFrameControl),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingFrameControl)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Controls;
|
||||||
|
|
||||||
|
public class TilingRoot : Control
|
||||||
|
{
|
||||||
|
static TilingRoot()
|
||||||
|
{
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(
|
||||||
|
typeof(TilingRoot),
|
||||||
|
new FrameworkPropertyMetadata(typeof(TilingRoot)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Dependency Properties
|
||||||
|
|
||||||
|
public double SplitterWidth {
|
||||||
|
get => (double)GetValue(SplitterWidthProperty);
|
||||||
|
set => SetValue(SplitterWidthProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DependencyProperty SplitterWidthProperty = DependencyProperty
|
||||||
|
.Register(
|
||||||
|
nameof(SplitterWidth),
|
||||||
|
typeof(double),
|
||||||
|
typeof(TilingRoot),
|
||||||
|
new PropertyMetadata(8.0));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Converters
|
||||||
|
{
|
||||||
|
public class AlphaRatioConverter : MarkupExtension, IValueConverter
|
||||||
|
{
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider) => this;
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is double ratio) {
|
||||||
|
if (ratio <= double.Epsilon)
|
||||||
|
return new GridLength(0, GridUnitType.Star);
|
||||||
|
if (ratio <= .5)
|
||||||
|
return new GridLength(1, GridUnitType.Star);
|
||||||
|
else if (ratio >= 1)
|
||||||
|
return new GridLength(1, GridUnitType.Star);
|
||||||
|
return new GridLength((1 - ratio) / ratio, GridUnitType.Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException($"Expected value to be ratio (double), god {value} instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Converters
|
||||||
|
{
|
||||||
|
public class BetaRatioConverter : MarkupExtension, IValueConverter
|
||||||
|
{
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider) => this;
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is double ratio) {
|
||||||
|
if (ratio >= 1)
|
||||||
|
return new GridLength(0, GridUnitType.Star);
|
||||||
|
if (ratio >= .5)
|
||||||
|
return new GridLength(1, GridUnitType.Star);
|
||||||
|
else if (ratio <= double.Epsilon)
|
||||||
|
return new GridLength(1, GridUnitType.Star);
|
||||||
|
return new GridLength((1 - ratio) / ratio, GridUnitType.Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException($"Expected value to be ratio (double), god {value} instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
public class EmptyFrame : HostFrame
|
||||||
|
{
|
||||||
|
public EmptyFrame()
|
||||||
|
{
|
||||||
|
HeaderText = "Empty";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestOverrideFrame : HostFrame
|
||||||
|
{
|
||||||
|
public TestOverrideFrame()
|
||||||
|
{
|
||||||
|
HeaderText = "Testerinho";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
public class EmptyHost : TilingHost
|
||||||
|
{
|
||||||
|
public EmptyHost() : base(new EmptyFrame(), new TestOverrideFrame())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags for declaring the buttons on a <see cref="HostFrame"/>
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum FrameButtons
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Close = 1 << 0,
|
||||||
|
Minimize = 1 << 1,
|
||||||
|
Pin = 1 << 2,
|
||||||
|
Split = 1 << 3,
|
||||||
|
PopOut = 1 << 4,
|
||||||
|
Options = 1 << 5,
|
||||||
|
Help = 1 << 6,
|
||||||
|
|
||||||
|
Default = Close | Pin | PopOut
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base data class for content that is displayed for a <see cref="TilingHost"/>'s tabs or stand-alone pop-out windows.<br/>
|
||||||
|
/// Anything that you can physically see from the root <see cref="TilingPanel"/> is a <see cref="HostFrame"/>.<br/>
|
||||||
|
/// Serving a header text, pin state and button configuration properties.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class HostFrame : ObservableObject
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="HeaderText"/>
|
||||||
|
private string _headerText = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// The title of the tab that will be displayed inside the tab button.
|
||||||
|
/// </summary>
|
||||||
|
public string HeaderText {
|
||||||
|
get => _headerText;
|
||||||
|
set => SetField(ref _headerText, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="FrameButtons"/>
|
||||||
|
private FrameButtons _frameButtons = FrameButtons.Default;
|
||||||
|
/// <summary>
|
||||||
|
/// The buttons to be displayed next to the header.
|
||||||
|
/// </summary>
|
||||||
|
public FrameButtons FrameButtons {
|
||||||
|
get => _frameButtons;
|
||||||
|
set => SetField(ref _frameButtons, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsPinned"/>
|
||||||
|
private bool _isPinned = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The title of the tab that will be displayed inside the tab button.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPinned {
|
||||||
|
get => _isPinned;
|
||||||
|
set => SetField(ref _isPinned, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual object? GetOptions() => null;
|
||||||
|
public virtual void SetOptions(object? options) { }
|
||||||
|
|
||||||
|
public virtual object? GetHelp() => null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
public abstract class ObservableObject : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected bool SetField<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(field, newValue))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
field = newValue;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to declare where a new <see cref="TilingHost"/> shall be placed when tiling.
|
||||||
|
/// </summary>
|
||||||
|
public enum TilingDirection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Right side of the target host, the new child becomes beta.
|
||||||
|
/// </summary>
|
||||||
|
Right = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// The bottom of the target host, the new child becomes beta.
|
||||||
|
/// </summary>
|
||||||
|
Bottom = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Left side of the target host, the new child becomes alpha and the target host moves to beta.
|
||||||
|
/// </summary>
|
||||||
|
Left = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// The top of the target host, the new child becomes alpha and the target host moves to beta.
|
||||||
|
/// </summary>
|
||||||
|
Top = 3
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
using CopaData.FileInspector.GUI.Models;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Host node that by paradigm is located only at the very end of the tree's branches.<br/>
|
||||||
|
/// Hosts may contain at least one central control, or multiple tabs of controls.<br/>
|
||||||
|
/// Note that tab contents must _not_ be any tiling controls, as that would break hierarchy and branching logic.<br/>
|
||||||
|
/// Everything above a <see cref="TilingHost"/> will be a <see cref="TilingPanel"/> by definition.
|
||||||
|
/// </summary>
|
||||||
|
public class TilingHost : TilingNode
|
||||||
|
{
|
||||||
|
public ObservableCollection<HostFrame> Frames { get; } = [];
|
||||||
|
|
||||||
|
private HostFrame? _activeFrame;
|
||||||
|
public HostFrame? ActiveFrame {
|
||||||
|
get => _activeFrame;
|
||||||
|
set => SetField(ref _activeFrame, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowTabs => Frames.Count > 1;
|
||||||
|
|
||||||
|
public bool IsEmpty => Frames.Count == 0;
|
||||||
|
|
||||||
|
public TilingHost(params HostFrame[] frames)
|
||||||
|
{
|
||||||
|
if (frames != null && frames.Length > 0) {
|
||||||
|
Frames = new ObservableCollection<HostFrame>(frames);
|
||||||
|
ActiveFrame = Frames[^1];
|
||||||
|
}
|
||||||
|
Frames.CollectionChanged += OnTabsChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTabsChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.NewItems != null && e.NewItems.Count > 0) {
|
||||||
|
// Make the last item active
|
||||||
|
ActiveFrame = e.NewItems[^1] as HostFrame;
|
||||||
|
} else if (e.OldItems != null && e.OldItems.Count > 0) {
|
||||||
|
if (ActiveFrame != null && !Frames.Contains(ActiveFrame)) {
|
||||||
|
// Previous active tab got removed, revert back to the most recent tab or set ActiveTab to null if tabs are empty.
|
||||||
|
if (Frames.Count > 0) {
|
||||||
|
ActiveFrame = Frames[^1];
|
||||||
|
} else {
|
||||||
|
ActiveFrame = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts <paramref name="newHost"/> into <paramref name="targetHost"/> as a new tab
|
||||||
|
/// after being dropped in the center region by the user.<br/>
|
||||||
|
/// If the previous host only had one frame inside it, it will be detached from its parent.
|
||||||
|
/// Otherwise, only the frame will be removed and inserted into <paramref name="targetHost"/>'s frames.
|
||||||
|
/// </summary>
|
||||||
|
public static void InsertHost(TilingPanel rootPanel, TilingHost targetHost, TilingHost newHost)
|
||||||
|
{
|
||||||
|
HostFrame? frame = newHost.ActiveFrame;
|
||||||
|
if (frame == null) {
|
||||||
|
throw new InvalidOperationException($"No active frame to be inserted could be found within {newHost}'s frames.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newHost.Frames.Count == 1) {
|
||||||
|
TilingPanel.Detach(rootPanel, newHost);
|
||||||
|
} else {
|
||||||
|
newHost.Frames.Remove(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetHost.Frames.Add(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Declares the intent of dropping a host into another.
|
||||||
|
/// </summary>
|
||||||
|
public enum TilingMethod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// In the case of the host being placed as a split next to the target host.
|
||||||
|
/// </summary>
|
||||||
|
Split = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// In case of the new host becoming a tab of the target host, rather than splitting.
|
||||||
|
/// </summary>
|
||||||
|
Drop = 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base node for the binary tree structure to work.<br/>
|
||||||
|
/// A tiling node is either a <see cref="TilingPanel"/>, which has a reference to two <see cref="TilingNode"/>s.<br/>
|
||||||
|
/// Those nodes may then be additional <see cref="TilingPanel"/>s, or, when at the end of the branch, a <see cref="TilingHost"/>.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class TilingNode : ObservableObject;
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Qrakhen.TilingFrames.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class represents a branch in the tree structure, having two children,<br/>
|
||||||
|
/// which can be either another <see cref="TilingPanel"/> - in the case of the tree branching onwards -<br/>
|
||||||
|
/// or a <see cref="TilingHost"/>, if we're at the end of a branch (a so-called leaf node).<br/>
|
||||||
|
/// A panel contains information about how its children <see cref="Alpha"/> and <see cref="Beta"/>
|
||||||
|
/// are separated by exposing an <see cref="Orientation"/> and split ratio.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The entire attaching and detaching logic is handled from static methods in this class for brevity.<br/>
|
||||||
|
/// Those methods are <see cref="Attach(TilingPanel, TilingHost, TilingHost, TilingDirection)"/> and <see cref="Detach(TilingPanel, TilingNode)"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public class TilingPanel : TilingNode
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="SplitOrientation" />
|
||||||
|
private Orientation _splitOrientation = Orientation.Horizontal;
|
||||||
|
/// <summary>
|
||||||
|
/// The orientation of the split, so either vertical or horizontal.
|
||||||
|
/// </summary>
|
||||||
|
public Orientation SplitOrientation {
|
||||||
|
get => _splitOrientation;
|
||||||
|
set => SetField(ref _splitOrientation, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SplitRatio" />
|
||||||
|
private double _splitRatio = 0.5;
|
||||||
|
/// <summary>
|
||||||
|
/// The width or height ratio of the split, with 0.5 being the exact middle.
|
||||||
|
/// </summary>
|
||||||
|
public double SplitRatio {
|
||||||
|
get => _splitRatio;
|
||||||
|
set => SetField(ref _splitRatio, Math.Min(1, Math.Max(0, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Alpha" />
|
||||||
|
private TilingNode _alpha;
|
||||||
|
/// <summary>
|
||||||
|
/// 'Alpha is always set' is an enforced paradigm, so it will always point to an existing node.
|
||||||
|
/// </summary>
|
||||||
|
public TilingNode Alpha {
|
||||||
|
get => _alpha;
|
||||||
|
private set => SetField(ref _alpha, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Beta" />
|
||||||
|
private TilingNode? _beta;
|
||||||
|
/// <summary>
|
||||||
|
/// The beta node may be null, for example if a panel is not yet split.
|
||||||
|
/// </summary>
|
||||||
|
public TilingNode? Beta {
|
||||||
|
get => _beta;
|
||||||
|
private set => SetField(ref _beta, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this panel is split in two, so whether beta is not null.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSplit => Beta != null;
|
||||||
|
|
||||||
|
public TilingPanel(TilingNode alpha, TilingNode? beta = null)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
_alpha = alpha;
|
||||||
|
_beta = beta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Traverses down the entire tree from <paramref name="rootPanel"/> until
|
||||||
|
/// <paramref name="node"/> is encountered, returning its direct parent panel.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This may not be the quickest approach, but we're looking at UI tiling panels with perhaps 20 children at most.
|
||||||
|
/// </remarks>
|
||||||
|
public static TilingPanel? GetParentPanel(TilingPanel rootPanel, TilingNode node)
|
||||||
|
{
|
||||||
|
if (rootPanel.Alpha == node || rootPanel.Beta == node) {
|
||||||
|
return rootPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
TilingPanel? parent = null;
|
||||||
|
if (rootPanel.Alpha is TilingPanel alphaPanel) {
|
||||||
|
parent = GetParentPanel(alphaPanel, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent == null && rootPanel.Beta is TilingPanel betaPanel) {
|
||||||
|
parent = GetParentPanel(betaPanel, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Attaches <paramref name="newHost"/>'s active frame to <paramref name="targetHost"/>'s parent as a new <see cref="TilingHost"/>.<br/>
|
||||||
|
/// The parent will be looked up by traversing the tree from <paramref name="rootPanel"/>.<br/>
|
||||||
|
/// The following ordered rules apply when attaching:
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// 1. If <paramref name="targetHost"/>'s parent has an open beta slot, <paramref name="newHost"/> will become the beta node.<br/>
|
||||||
|
/// 2. If one of the <paramref name="targetHost"/> parent's child nodes is an <see cref="EmptyHost"/>, it will be replaced with <paramref name="newHost"/><br/>
|
||||||
|
/// 3. If none of the above apply, the <paramref name="targetHost"/> parent's beta node will be split into a new tiling panel.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All of these actions will automatically split the parent panel.<br/>
|
||||||
|
/// If <paramref name="newHost"/> had a parent assigned previously and only a single frame, it will be detached from that branch first.<br/>
|
||||||
|
/// If more frames are present in <paramref name="newHost"/>, only the active frame will be removed from it,<br/>
|
||||||
|
/// and a new <see cref="TilingHost"/> is created, containing only that frame.<br/>
|
||||||
|
/// Note that all passed elements have to be children to the same <see cref="TilingNode"/> tree.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="rootPanel">The root to start branch traversal from.</param>
|
||||||
|
/// <param name="targetHost">The host to be split.</param>
|
||||||
|
/// <param name="newHost">
|
||||||
|
/// The host to be attached to <paramref name="targetHost"/>'s parent.
|
||||||
|
/// If set to null, an <see cref="EmptyHost"/> will be created in its place.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="direction">The direction at which to drop <paramref name="newHost"/> relative to <paramref name="targetHost"/>.</param>
|
||||||
|
/// <returns>The <see cref="TilingPanel"/> that now contains <paramref name="newHost"/> and <paramref name="targetHost"/>.</returns>
|
||||||
|
public static TilingPanel Attach(TilingPanel rootPanel,
|
||||||
|
TilingHost targetHost,
|
||||||
|
TilingHost? newHost,
|
||||||
|
TilingDirection direction)
|
||||||
|
{
|
||||||
|
// This method looks way more complicated than it actually is,
|
||||||
|
// all I wanted to achieve is one centralized place where branching and child assignments happen,
|
||||||
|
// as tree structures are notoriously hard to navigate and handle when putting references everywhere.
|
||||||
|
// It really boils down to a simple three-step process:
|
||||||
|
// 1. Get or create correct host to attach,
|
||||||
|
// 2. Find out where to place the new host (Alpha or Beta, depending on tiling direction),
|
||||||
|
// 3. Create branch to be attached, or attach to free slot of parent.
|
||||||
|
// Todo: This note is in place for the reviewer to have a bit more context. Remove when done.
|
||||||
|
|
||||||
|
TilingPanel? parent = GetParentPanel(rootPanel, targetHost);
|
||||||
|
if (parent == null) {
|
||||||
|
throw new NullReferenceException($"Detached target: Could not find a parent for {targetHost} in any of {rootPanel}'s child nodes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newHost == null) {
|
||||||
|
// Substitute for the split.
|
||||||
|
newHost = new EmptyHost();
|
||||||
|
} else {
|
||||||
|
if (newHost.Frames.Count < 2) {
|
||||||
|
// Detach newHost from a potential parent.
|
||||||
|
Detach(rootPanel, newHost);
|
||||||
|
} else {
|
||||||
|
// Only take the active frame from newHost and instantiate a host to attach.
|
||||||
|
HostFrame frame = newHost.ActiveFrame!; // Frames.Count >= 2 already ensures ActiveFrame not to be null.
|
||||||
|
newHost = new TilingHost([frame]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I tried formulating an explanation for this step but I simply can't.
|
||||||
|
// "Left or Top go alpha, Bottom or Right go beta."
|
||||||
|
bool newHostIsAlpha = direction is TilingDirection.Left or TilingDirection.Top;
|
||||||
|
|
||||||
|
// Check whether there's a free beta slot in the parent to use
|
||||||
|
if (parent.Beta == null) {
|
||||||
|
if (newHostIsAlpha) {
|
||||||
|
// Open beta slot but newHost wants to be in alpha slot, simply swap and attach.
|
||||||
|
parent.Beta = parent.Alpha;
|
||||||
|
parent.Alpha = newHost;
|
||||||
|
} else {
|
||||||
|
// Open beta slot means we just put newHost there, no branching needed.
|
||||||
|
parent.Beta = newHost;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new branch to hold both target- and newHost.
|
||||||
|
TilingPanel branch;
|
||||||
|
if (direction is TilingDirection.Left or TilingDirection.Top) {
|
||||||
|
branch = new TilingPanel(newHost, targetHost);
|
||||||
|
} else {
|
||||||
|
branch = new TilingPanel(targetHost, newHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign new branch to correct chíld node.
|
||||||
|
if (parent.Alpha == targetHost) {
|
||||||
|
parent.Alpha = branch;
|
||||||
|
} else if (parent.Beta == targetHost) {
|
||||||
|
parent.Beta = branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detaches <paramref name="child"/> from its parent node, which will be looked up by traversing the <paramref name="rootPanel"/>.<br/>
|
||||||
|
/// If the child happened to be the alpha value, the beta value will be moved into the alpha slot to ensure alpha always having a value.<br/>
|
||||||
|
/// If the child's parent node ends up having no children left, which would violate the 'alpha is always set' rule,<br/>
|
||||||
|
/// that node itself will also be detached in order to prevent trailing zombie nodes.
|
||||||
|
/// </summary>
|
||||||
|
public static void Detach(TilingPanel rootPanel, TilingNode child)
|
||||||
|
{
|
||||||
|
TilingPanel? parent = GetParentPanel(rootPanel, child);
|
||||||
|
if (parent == null) {
|
||||||
|
return; // Already an orphan.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.Alpha == child) {
|
||||||
|
if (parent.Beta != null) {
|
||||||
|
// Move beta over to alpha if beta has a value.
|
||||||
|
parent.Alpha = parent.Beta;
|
||||||
|
parent.Beta = null;
|
||||||
|
} else {
|
||||||
|
// Both branches have detached, which means we'll detach the parent itself
|
||||||
|
// in order to prevent empty, trailing panels with no children.
|
||||||
|
Detach(rootPanel, child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Child was the beta node, so we simply null it.
|
||||||
|
parent.Beta = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
# TilingPanels
|
||||||
|
|
||||||
|
Cool Library to have tiling panels.
|
||||||
|
Is that not cool?
|
||||||
|
I think it very much is cool.
|
||||||
|
Very nice, yes. Alpha & Beta. No Gamma or Delta. Just Alpha-Beta.
|
||||||
|
|
||||||
|
Data structure resembles a binary tree with uniform branch- and end-nodes (TilingPanels & TilingHosts).
|
||||||
|
|
||||||
|
### TilingNode
|
||||||
|
|
||||||
|
### TilingPanel
|
||||||
|
|
||||||
|
### TilingHost
|
||||||
|
|
||||||
|
### HostFrame
|
||||||
|
|
||||||
|
### Node Structure
|
||||||
|
```cs
|
||||||
|
RootPanel
|
||||||
|
/ \
|
||||||
|
A B
|
||||||
|
/ \
|
||||||
|
Panel Host
|
||||||
|
/ \ |-Frame (Single Frame)
|
||||||
|
/ \
|
||||||
|
Panel Host
|
||||||
|
/ \ |-Frame (Tab)
|
||||||
|
/ . |-Frame (Tab)
|
||||||
|
Host
|
||||||
|
|-Frame (Tab)
|
||||||
|
|-Frame (Tab)
|
||||||
|
|-Frame (Tab)
|
||||||
|
```
|
||||||
|
Key Behaviours:
|
||||||
|
- All Panels have information about how and where their content is split in two.
|
||||||
|
- Every panel may have up to two children (Alpha/Beta), where Alpha is never null.
|
||||||
|
- Beta may not be set (In a case of a single frame with no splits, for example).
|
||||||
|
- If a child located at Alpha is detached, Beta will move to alpha, to ensure the 'Alpha is always set' paradigm.
|
||||||
|
- If a child is detached and both alpha and beta result in being null, the entire Panel is detached from its parent.
|
||||||
|
- Hosts are always at the end of the branches, everything above a Node is a Panel.
|
||||||
|
- The things you're dragging around to re-order, separate and split panels are Frames.
|
||||||
|
- Hosts contain Frames, which will be displayed as tabs if stacked atop each other, or as a single frame if only one is present.
|
||||||
|
- All mutations of the tree structure expect the root node as their first argument.
|
||||||
|
That is done so there's no two-way referencing and to keep stuff simple.
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<Color x:Key="C_Back_Primary">#242527</Color>
|
||||||
|
<Color x:Key="C_Back_Primary_Tint">#28292c</Color>
|
||||||
|
<Color x:Key="C_Back_Secondary">#161718</Color>
|
||||||
|
<Color x:Key="C_Back_Secondary_Tint">#1c1d1f</Color>
|
||||||
|
<Color x:Key="C_Back_Tertiary">#323336</Color>
|
||||||
|
<Color x:Key="C_Back_Tertiary_Tint">#37393c</Color>
|
||||||
|
<Color x:Key="C_Border_Primary">#727478</Color>
|
||||||
|
<Color x:Key="C_Border_Primary_Tint">#84868b</Color>
|
||||||
|
<Color x:Key="C_Text_Primary">#fcfeff</Color>
|
||||||
|
<Color x:Key="C_Text_Grey">#97999c</Color>
|
||||||
|
<Color x:Key="C_Text_Accent">#32ce96</Color>
|
||||||
|
<Color x:Key="C_Text_Accent_Tint">#48faaf</Color>
|
||||||
|
<Color x:Key="C_Text_Accent_Dark">#249672</Color>
|
||||||
|
<Color x:Key="C_Text_Accent_Dark">#249672</Color>
|
||||||
|
<Color x:Key="C_Text_Error">#ef4232</Color>
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="B_Back_Primary" Color="{StaticResource C_Back_Primary}" />
|
||||||
|
<SolidColorBrush x:Key="B_Back_Primary_Tint" Color="{StaticResource C_Back_Primary_Tint}" />
|
||||||
|
<SolidColorBrush x:Key="B_Back_Secondary" Color="{StaticResource C_Back_Secondary}" />
|
||||||
|
<SolidColorBrush x:Key="B_Back_Secondary_Tint" Color="{StaticResource C_Back_Secondary_Tint}" />
|
||||||
|
<SolidColorBrush x:Key="B_Back_Tertiary" Color="{StaticResource C_Back_Tertiary}" />
|
||||||
|
<SolidColorBrush x:Key="B_Back_Tertiary_Tint" Color="{StaticResource C_Back_Tertiary_Tint}" />
|
||||||
|
<SolidColorBrush x:Key="B_Border_Primary" Color="{StaticResource C_Border_Primary}" />
|
||||||
|
<SolidColorBrush x:Key="B_Border_Primary_Tint" Color="{StaticResource C_Border_Primary_Tint}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Primary" Color="{StaticResource C_Text_Primary}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Grey" Color="{StaticResource C_Text_Grey}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Accent" Color="{StaticResource C_Text_Accent}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Accent_Tint" Color="{StaticResource C_Text_Accent_Tint}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Accent_Dark" Color="{StaticResource C_Text_Accent_Dark}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Accent_Dark" Color="{StaticResource C_Text_Accent_Dark}" />
|
||||||
|
<SolidColorBrush x:Key="B_Text_Error" Color="{StaticResource C_Text_Error}" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Qrakhen.TilingFrames.Converters"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames.Controls.DropArea"
|
||||||
|
xmlns:dnd="clr-namespace:Qrakhen.TilingFrames.Controls.DragAndDrop"
|
||||||
|
xmlns:models="clr-namespace:Qrakhen.TilingFrames.Models">
|
||||||
|
|
||||||
|
<converters:AlphaRatioConverter x:Key="RatioToStarConverter" />
|
||||||
|
|
||||||
|
<Style x:Key="DropAreaButton" TargetType="{x:Type Button}">
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="AllowDrop" Value="True" />
|
||||||
|
<Setter Property="Background" Value="#10affefe" />
|
||||||
|
<Setter Property="Margin" Value="3" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Trigger.Setters>
|
||||||
|
<Setter Property="Background" Value="#40affefe" />
|
||||||
|
</Trigger.Setters>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type local:DropArea}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:DropArea}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="48" />
|
||||||
|
<ColumnDefinition Width="72" />
|
||||||
|
<ColumnDefinition Width="48" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="48" />
|
||||||
|
<RowDefinition Height="72" />
|
||||||
|
<RowDefinition Height="48" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Button x:Name="CenterDrop"
|
||||||
|
Drop="CenterDrop_Drop"
|
||||||
|
dnd:DragAndDropControl.IsDraggable="True"
|
||||||
|
dnd:DragAndDropControl.HAndler="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}"
|
||||||
|
Style="{StaticResource DropAreaButton}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1" />
|
||||||
|
|
||||||
|
<Button x:Name="LeftDrop"
|
||||||
|
Drop="LeftDrop_Drop"
|
||||||
|
Style="{StaticResource DropAreaButton}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0" />
|
||||||
|
|
||||||
|
<Button x:Name="TopDrop"
|
||||||
|
Drop="TopDrop_Drop"
|
||||||
|
Style="{StaticResource DropAreaButton}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1" />
|
||||||
|
|
||||||
|
<Button x:Name="RightDrop"
|
||||||
|
Drop="RightDrop_Drop"
|
||||||
|
Style="{StaticResource DropAreaButton}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2" />
|
||||||
|
|
||||||
|
<Button x:Name="BottomDrop"
|
||||||
|
Drop="BottomDrop_Drop"
|
||||||
|
Style="{StaticResource DropAreaButton}"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1" />
|
||||||
|
|
||||||
|
<Border BorderBrush="#affefe"
|
||||||
|
BorderThickness="1.28"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="3" />
|
||||||
|
|
||||||
|
<Border BorderBrush="#affefe"
|
||||||
|
BorderThickness="1.28"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Grid.Row="1" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Qrakhen.TilingFrames.Converters"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames.Controls"
|
||||||
|
xmlns:models="clr-namespace:Qrakhen.TilingFrames.Models">
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type local:HostControl}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:HostControl}">
|
||||||
|
<Border Padding="0"
|
||||||
|
CornerRadius="4"
|
||||||
|
BorderBrush="{DynamicResource Brush_Border_Primary}"
|
||||||
|
BorderThickness="1">
|
||||||
|
<TabControl ItemsSource="{Binding Frames}"
|
||||||
|
SelectedItem="{Binding ActiveFrame, Mode=TwoWay}">
|
||||||
|
<TabControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid Background="Transparent">
|
||||||
|
<TextBlock Text="{Binding HeaderText}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ItemTemplate>
|
||||||
|
|
||||||
|
<TabControl.ContentTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ContentPresenter Content="{Binding}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ContentTemplate>
|
||||||
|
|
||||||
|
<TabControl.Style>
|
||||||
|
<Style TargetType="TabControl">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="TabControl">
|
||||||
|
<Border CornerRadius="4">
|
||||||
|
<Grid KeyboardNavigation.TabNavigation="Local"
|
||||||
|
Background="{DynamicResource Brush_Background_Primary}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Border CornerRadius="4"
|
||||||
|
Background="{DynamicResource Brush_Background_Secondary}">
|
||||||
|
<TabPanel Grid.Row="0"
|
||||||
|
IsItemsHost="True"
|
||||||
|
KeyboardNavigation.TabIndex="1"
|
||||||
|
Margin="0,0,0,-1" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border x:Name="ActiveContent"
|
||||||
|
Grid.Row="1"
|
||||||
|
BorderThickness="0"
|
||||||
|
AllowDrop="True"
|
||||||
|
Padding="4"
|
||||||
|
BorderBrush="{DynamicResource Brush_Border_Primary}"
|
||||||
|
Background="Transparent">
|
||||||
|
<ContentPresenter ContentSource="SelectedContent"
|
||||||
|
KeyboardNavigation.TabNavigation="Cycle"
|
||||||
|
KeyboardNavigation.DirectionalNavigation="Contained" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</TabControl.Style>
|
||||||
|
</TabControl>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<DataTemplate DataType="{x:Type models:TilingHost}">
|
||||||
|
<local:HostControl />
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<Style TargetType="TabItem">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="TabItem">
|
||||||
|
<Border x:Name="TemplateBorder"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderBrush="Transparent"
|
||||||
|
Cursor="Hand"
|
||||||
|
Margin="0"
|
||||||
|
Padding="8,4"
|
||||||
|
MinWidth="48"
|
||||||
|
CornerRadius="4"
|
||||||
|
BorderThickness="1">
|
||||||
|
|
||||||
|
<ContentPresenter x:Name="ContentSite"
|
||||||
|
ContentSource="Header"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
RecognizesAccessKey="True" />
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter TargetName="ContentSite" Property="TextElement.Foreground" Value="{DynamicResource Brush_Text_Primary}" />
|
||||||
|
<Setter TargetName="TemplateBorder" Property="BorderBrush" Value="{DynamicResource Brush_Accent_Primary}" />
|
||||||
|
<Setter TargetName="TemplateBorder" Property="Background" Value="Transparent" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="TemplateBorder" Property="Background" Value="{DynamicResource Brush_Background_Primary_Tint}" />
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Brush_Text_Grey}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Qrakhen.TilingFrames.Converters"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames.Controls"
|
||||||
|
xmlns:models="clr-namespace:Qrakhen.TilingFrames.Models">
|
||||||
|
|
||||||
|
<converters:AlphaRatioConverter x:Key="RatioToStarConverter" />
|
||||||
|
|
||||||
|
<!-- NO TEMPLATE OVERRIDE (FALLBACK) -->
|
||||||
|
<DataTemplate DataType="{x:Type models:HostFrame}">
|
||||||
|
<Border BorderBrush="{DynamicResource Brush_Text_Error}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14"
|
||||||
|
Foreground="{DynamicResource Brush_Text_Error}">
|
||||||
|
No Template override for the provided HostFrame model found.<LineBreak/>
|
||||||
|
You need to override the data template of your host frame like so:
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock xml:space="preserve"
|
||||||
|
FontSize="13"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
Foreground="{DynamicResource Brush_Text_Primary}">
|
||||||
|
<DataTemplate DataType="{x:Type YourFrameModel}">
|
||||||
|
<TextBlock Text="This is my model view! :)" />
|
||||||
|
</DataTemplate>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- EMPTY TEMPLATE -->
|
||||||
|
<DataTemplate DataType="{x:Type models:EmptyFrame}">
|
||||||
|
<Border Padding="8"
|
||||||
|
MaxHeight="64"
|
||||||
|
MaxWidth="256"
|
||||||
|
Margin="24"
|
||||||
|
CornerRadius="4"
|
||||||
|
Background="#10fefefe"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch">
|
||||||
|
<TextBlock FontSize="14"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource Brush_Text_Primary}">
|
||||||
|
Drag or Drop something here :)
|
||||||
|
</TextBlock>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Qrakhen.TilingFrames.Converters"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames.Controls"
|
||||||
|
xmlns:models="clr-namespace:Qrakhen.TilingFrames.Models">
|
||||||
|
|
||||||
|
<converters:AlphaRatioConverter x:Key="AlphaRatioConverter" />
|
||||||
|
<converters:BetaRatioConverter x:Key="BetaRatioConverter" />
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type local:PanelControl}">
|
||||||
|
<Style.Setters>
|
||||||
|
<Setter Property="SplitterWidth" Value="{Binding SplitterWidth,
|
||||||
|
RelativeSource={RelativeSource
|
||||||
|
AncestorType={x:Type local:TilingRoot},
|
||||||
|
Mode=FindAncestor}}" />
|
||||||
|
</Style.Setters>
|
||||||
|
<Style.Triggers>
|
||||||
|
|
||||||
|
<!-- VERTICAL LAYOUT -->
|
||||||
|
<DataTrigger Binding="{Binding SplitOrientation}" Value="{x:Static Orientation.Vertical}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:PanelControl}">
|
||||||
|
<Border CornerRadius="4">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="{Binding SplitRatio,
|
||||||
|
Mode=OneWay,
|
||||||
|
Converter={StaticResource AlphaRatioConverter}}" />
|
||||||
|
<RowDefinition Height="{TemplateBinding SplitterWidth}" />
|
||||||
|
<RowDefinition Height="{Binding SplitRatio,
|
||||||
|
Mode=OneWay,
|
||||||
|
Converter={StaticResource BetaRatioConverter}}" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ContentPresenter Grid.Row="0"
|
||||||
|
Content="{Binding Alpha}" />
|
||||||
|
|
||||||
|
<GridSplitter Grid.Row="1"
|
||||||
|
Height="{TemplateBinding SplitterWidth}"
|
||||||
|
Background="Transparent"
|
||||||
|
Margin="0"
|
||||||
|
ResizeBehavior="PreviousAndNext"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
|
||||||
|
<ContentPresenter Grid.Row="2"
|
||||||
|
Content="{Binding Beta}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</DataTrigger>
|
||||||
|
|
||||||
|
<!-- HORIZONTAL LAYOUT -->
|
||||||
|
<DataTrigger Binding="{Binding SplitOrientation}" Value="{x:Static Orientation.Horizontal}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:PanelControl}">
|
||||||
|
<Border CornerRadius="4">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="{Binding SplitRatio,
|
||||||
|
Mode=OneWay,
|
||||||
|
Converter={StaticResource AlphaRatioConverter}}" />
|
||||||
|
<ColumnDefinition Width="{TemplateBinding SplitterWidth}" />
|
||||||
|
<ColumnDefinition Width="{Binding SplitRatio,
|
||||||
|
Mode=OneWay,
|
||||||
|
Converter={StaticResource BetaRatioConverter}}" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<ContentPresenter Grid.Column="0"
|
||||||
|
Content="{Binding Alpha}" />
|
||||||
|
|
||||||
|
<GridSplitter Grid.Column="1"
|
||||||
|
Width="{TemplateBinding SplitterWidth}"
|
||||||
|
Background="Transparent"
|
||||||
|
Margin="0"
|
||||||
|
ResizeBehavior="PreviousAndNext"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
|
||||||
|
<ContentPresenter Grid.Column="2"
|
||||||
|
Content="{Binding Beta}" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</DataTrigger>
|
||||||
|
|
||||||
|
<!-- OVERRIDE FOR NO SPLIT -->
|
||||||
|
<DataTrigger Binding="{Binding Beta}" Value="{x:Null}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:PanelControl}">
|
||||||
|
<Border CornerRadius="4">
|
||||||
|
<Grid>
|
||||||
|
<ContentPresenter Content="{Binding Alpha}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<DataTemplate DataType="{x:Type models:TilingPanel}">
|
||||||
|
<local:PanelControl />
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Qrakhen.TilingFrames.Converters"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames.Controls"
|
||||||
|
xmlns:models="clr-namespace:Qrakhen.TilingFrames.Models">
|
||||||
|
|
||||||
|
<converters:AlphaRatioConverter x:Key="RatioToStarConverter" />
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type local:TilingRoot}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:TilingRoot}">
|
||||||
|
<Border Padding="{TemplateBinding Padding}"
|
||||||
|
Margin="{TemplateBinding Margin}">
|
||||||
|
<local:PanelControl DataContext="{Binding}" />
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<DataTemplate DataType="{x:Type local:TilingRoot}">
|
||||||
|
<local:TilingRoot />
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:Qrakhen.TilingFrames">
|
||||||
|
<Style TargetType="{x:Type local:CustomControl1}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}">
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="./Controls/TilingRoot.xaml" />
|
||||||
|
<ResourceDictionary Source="./Controls/Panel.xaml" />
|
||||||
|
<ResourceDictionary Source="./Controls/Host.xaml" />
|
||||||
|
<ResourceDictionary Source="./Controls/HostFrame.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
Loading…
Reference in New Issue