FancyZones and Shortcut Guide initial commit

Co-authored-by: Alexis Campailla <alexis@janeasystems.com>
Co-authored-by: Bret Anderson <bretan@microsoft.com>
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Jeff Bogdan <jeffbog@microsoft.com>
Co-authored-by: March Rogers <marchr@microsoft.com>
Co-authored-by: Mike Harsh <mharsh@microsoft.com>
Co-authored-by: Nachum Bundak <Nachum.Bundak@microsoft.com>
Co-authored-by: Oliver Jones <ojones@microsoft.com>
Co-authored-by: Patrick Little <plittle@microsoft.com>
This commit is contained in:
Bartosz Sosnowski
2019-09-04 18:26:26 +02:00
committed by Bartosz Sosnowski
parent 10c5396099
commit 8431b80e48
341 changed files with 54766 additions and 62 deletions

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2042
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZonesEditor", "FancyZonesEditor\FancyZonesEditor.csproj", "{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A90742C-8AA0-4A56-A01F-70C8754B4684}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
</configuration>

View File

@@ -0,0 +1,17 @@
<Application x:Class="FancyZonesEditor.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FancyZonesEditor"
Startup="OnStartup">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,74 @@
using FancyZonesEditor;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public Settings ZoneSettings { get { return _settings; } }
private Settings _settings;
private ushort _idInitial = 0;
public App()
{
//init settings
_settings = new Settings();
}
private void OnStartup(object sender, StartupEventArgs e)
{
if (e.Args.Length > 1)
{
UInt16.TryParse(e.Args[1], out _idInitial);
}
LayoutModel foundModel = null;
if (_idInitial != 0)
{
foreach (LayoutModel model in _settings.DefaultModels)
{
if (model.Id == _idInitial)
{
// found match
foundModel = model;
break;
}
}
if (foundModel == null)
{
foreach (LayoutModel model in _settings.CustomModels)
{
if (model.Id == _idInitial)
{
// found match
foundModel = model;
break;
}
}
}
}
if (foundModel == null)
{
foundModel = _settings.DefaultModels[0];
}
foundModel.IsSelected = true;
// TODO: multimon
// Pass in the correct args to show on the desired monitor
EditorOverlay overlay = new EditorOverlay();
overlay.Show();
overlay.DataContext = foundModel;
}
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="FancyZonesEditor.CanvasEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Canvas x:Name="Preview"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for CanvasEditor.xaml
/// </summary>
public partial class CanvasEditor : UserControl
{
public CanvasEditor()
{
InitializeComponent();
Loaded += CanvasEditor_Loaded;
}
private void CanvasEditor_Loaded(object sender, RoutedEventArgs e)
{
CanvasLayoutModel model = (CanvasLayoutModel)DataContext;
if (model != null)
{
Model = model;
UpdateZoneRects();
model.PropertyChanged += OnModelChanged;
}
}
private void OnModelChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Zones")
{
UpdateZoneRects();
}
}
private void UpdateZoneRects()
{
UIElementCollection previewChildren = Preview.Children;
int previewChildrenCount = previewChildren.Count;
while (previewChildrenCount < Model.Zones.Count)
{
CanvasZone zone = new CanvasZone();
zone.Model = Model;
Preview.Children.Add(zone);
previewChildrenCount++;
}
for (int i = 0; i < previewChildrenCount; i++)
{
Int32Rect rect = Model.Zones[i];
CanvasZone zone = previewChildren[i] as CanvasZone;
zone.ZoneIndex = i;
Canvas.SetLeft(zone, rect.X);
Canvas.SetTop(zone, rect.Y);
zone.MinHeight = rect.Height;
zone.MinWidth = rect.Width;
}
}
public CanvasLayoutModel Model;
}
}

View File

@@ -0,0 +1,189 @@
<local:EditorWindow x:Class="FancyZonesEditor.CanvasEditorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title=""
Width="528"
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Closed="OnClosed">
<Window.Resources>
<Style x:Key="titleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LineHeight" Value="24" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="16,16,0,12" />
</Style>
<Style x:Key="zoneCount" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="FontSize" Value="24"/>
<Setter Property="LineHeight" Value="24" />
<Setter Property="Margin" Value="20,0,20,0" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="tabText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="#C4C4C4"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="24,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<Style x:Key="settingText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="5,10,0,0" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="templateTitleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="250"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style x:Key="secondaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#BCBCBD"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="primaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#0078D7"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#024D89"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="spinnerButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="0,0,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="0,0,0,0" />
</Style>
<Style x:Key="templateBackground" TargetType="Rectangle">
<Setter Property="Fill" Value="Black"/>
<Setter Property="Opacity" Value="0.05"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
</Style>
<Style x:Key="templateBackgroundSelected" TargetType="Rectangle">
<Setter Property="Fill" Value="#F2F2F2"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
<Setter Property="Stroke" Value="#0078D7"/>
<Setter Property="StrokeThickness" Value="2"/>
</Style>
<Style x:Key="newZoneButton" TargetType="Button">
<Setter Property="Background" Value="#f2f2f2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="120"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="textLabel" TargetType="TextBlock">
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="16,12,0,0"/>
</Style>
<Style x:Key="textBox" TargetType="TextBox">
<Setter Property="BorderBrush" Value="#cccccc"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,5,0,0"/>
<Setter Property="Padding" Value="5,5,5,5"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Name="windowEditorDialogTitle" Text="Custom layout creator" Style="{StaticResource titleText}" />
<Button x:Name="newZoneButton" Width="496" Height="136" Style="{StaticResource newZoneButton}" Click="OnAddZone">
<StackPanel>
<TextBlock x:Name="newSoneName" Text="Add new zone" Style="{StaticResource templateTitleText}" />
<TextBlock HorizontalAlignment="Center" Text="+" Margin="0,-40,0,0"/>
</StackPanel>
</Button>
<TextBlock Text="Name" Style="{StaticResource textLabel}" />
<TextBox Text="{Binding Name}" Width="496" Style="{StaticResource textBox}" />
<!--
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="16,4,0,0"/>
<TextBlock Text="Show snap grid" Style="{StaticResource settingText}" />
<TextBlock Text="Grid spacing" Style="{StaticResource settingText}" Margin="40,10,10,0" />
<TextBox x:Name="gridValue" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}"/>
</StackPanel>
-->
<StackPanel Orientation="Horizontal" Margin="0,12,0,16">
<Button Content="Cancel" Style="{StaticResource secondaryButton}" Click="OnCancel"/>
<Button Content="Save and apply" Style="{StaticResource primaryButton}" Click="OnSaveApplyTemplate" />
</StackPanel>
</StackPanel>
</local:EditorWindow>

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using MahApps.Metro.Controls;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for windowEditor.xaml
/// </summary>
public partial class CanvasEditorWindow : EditorWindow
{
public CanvasEditorWindow()
{
InitializeComponent();
Model = EditorOverlay.Current.DataContext as CanvasLayoutModel;
}
private void OnAddZone(object sender, RoutedEventArgs e)
{
Model.AddZone(new Int32Rect(_offset, _offset, (int) (Model.ReferenceWidth * 0.6), (int) (Model.ReferenceHeight * 0.6)));
_offset += 100;
}
private int _offset = 100;
private CanvasLayoutModel Model;
}
}

View File

@@ -0,0 +1,44 @@
<UserControl x:Class="FancyZonesEditor.CanvasZone"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Background="LightGray"
Opacity="0.75"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="Frame">
<Grid.RowDefinitions>
<RowDefinition Height="8"/>
<RowDefinition Height="16"/>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
<RowDefinition Height="16"/>
<RowDefinition Height="8"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="8"/>
</Grid.ColumnDefinitions>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" Background="Black" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="NWResize_DragDelta"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" Background="Black" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="NEResize_DragDelta"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" Background="Black" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="SWResize_DragDelta"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" Background="Black" Grid.Row="4" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="SEResize_DragDelta"/>
<Thumb x:Name="NResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="0" Grid.Column="2" DragDelta="NResize_DragDelta"/>
<Thumb x:Name="SResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="5" Grid.Column="2" DragDelta="SResize_DragDelta"/>
<Thumb x:Name="WResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" DragDelta="WResize_DragDelta"/>
<Thumb x:Name="EResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="4" Grid.RowSpan="2" DragDelta="EResize_DragDelta"/>
<DockPanel Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3">
<Button DockPanel.Dock="Right" Padding="8,0" Click="OnClose">
<Image Source="images/ChromeClose.png" Height="24" Width="24" />
</Button>
<Thumb x:Name="Caption" Cursor="SizeAll" Background="DarkGray" DragDelta="Caption_DragDelta"/>
</DockPanel>
<Rectangle Fill="LightGray" Grid.Row="3" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
<Canvas x:Name="Body" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Once you've "Committ"ed the starter grid, then the Zones within the grid come to life for you to be able to further subdivide them
/// using splitters
/// </summary>
public partial class CanvasZone : UserControl
{
public CanvasZone()
{
InitializeComponent();
Canvas.SetZIndex(this, c_zIndex++);
}
public CanvasLayoutModel Model;
public int ZoneIndex;
private Point Move(double xDelta, double yDelta)
{
Int32Rect rect = Model.Zones[ZoneIndex];
if (xDelta < 0)
{
xDelta = Math.Max(xDelta, -rect.X);
}
else if (xDelta > 0)
{
xDelta = Math.Min(xDelta, c_workArea.Width - rect.Width - rect.X);
}
if (yDelta < 0)
{
yDelta = Math.Max(yDelta, -rect.Y);
}
else if (yDelta > 0)
{
yDelta = Math.Min(yDelta, c_workArea.Height - rect.Height - rect.Y);
}
rect.X += (int) xDelta;
rect.Y += (int) yDelta;
Canvas.SetLeft(this, rect.X);
Canvas.SetTop(this, rect.Y);
Model.Zones[ZoneIndex] = rect;
return new Point(xDelta, yDelta);
}
private void Size(double xDelta, double yDelta)
{
Int32Rect rect = Model.Zones[ZoneIndex];
if (xDelta != 0)
{
int newWidth = rect.Width + (int) xDelta;
if (newWidth < 48)
{
newWidth = 48;
}
else if (newWidth > (c_workArea.Width - rect.X))
{
newWidth = (int) c_workArea.Width - rect.X;
}
MinWidth = rect.Width = newWidth;
}
if (yDelta != 0)
{
int newHeight = rect.Height + (int)yDelta;
if (newHeight < 48)
{
newHeight = 48;
}
else if (newHeight > (c_workArea.Height - rect.Y))
{
newHeight = (int)c_workArea.Height - rect.Y;
}
MinHeight = rect.Height = newHeight;
}
Model.Zones[ZoneIndex] = rect;
}
private static int c_zIndex = 0;
// TODO: multimon
// This needs to be the work area of the monitor we get launched on
private static Rect c_workArea = System.Windows.SystemParameters.WorkArea;
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
Canvas.SetZIndex(this, c_zIndex++);
base.OnPreviewMouseDown(e);
}
private void NWResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Point actualChange = Move(e.HorizontalChange, e.VerticalChange);
Size(-actualChange.X, -actualChange.Y);
}
private void NEResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Point actualChange = Move(0, e.VerticalChange);
Size(e.HorizontalChange, -actualChange.Y);
}
private void SWResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Point actualChange = Move(e.HorizontalChange, 0);
Size(-actualChange.X, e.VerticalChange);
}
private void SEResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Size(e.HorizontalChange, e.VerticalChange);
}
private void NResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Point actualChange = Move(0, e.VerticalChange);
Size(0, -actualChange.Y);
}
private void SResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Size(0, e.VerticalChange);
}
private void WResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Point actualChange = Move(e.HorizontalChange, 0);
Size(-actualChange.X, 0);
}
private void EResize_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Size(e.HorizontalChange, 0);
}
private void Caption_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Move(e.HorizontalChange, e.VerticalChange);
}
private void OnClose(object sender, RoutedEventArgs e)
{
((Panel)Parent).Children.Remove(this);
Model.RemoveZoneAt(ZoneIndex);
}
}
}

View File

@@ -0,0 +1,15 @@
<Window x:Class="FancyZonesEditor.EditorOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800"
ShowInTaskbar="False"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Loaded="onLoad"/>

View File

@@ -0,0 +1,138 @@
using FancyZonesEditor.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.RightsManagement;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class EditorOverlay : Window
{
public Int32Rect[] GetZoneRects()
{
// TODO: the ideal here is that the ArrangeRects logic is entirely inside the model, so we don't have to walk the UIElement children to get the rect info
Panel previewPanel = null;
if (_editor != null)
{
GridEditor gridEditor = _editor as GridEditor;
if (gridEditor != null)
{
previewPanel = gridEditor.PreviewPanel;
}
else
{
//CanvasEditor
previewPanel = ((CanvasEditor)_editor).Preview;
}
}
else
{
previewPanel = _layoutPreview.PreviewPanel;
}
var count = previewPanel.Children.Count;
Int32Rect[] zones = new Int32Rect[count];
int i = 0;
foreach (FrameworkElement child in previewPanel.Children)
{
Point topLeft = child.TransformToAncestor(previewPanel).Transform(new Point());
var right = topLeft.X + child.ActualWidth;
var bottom = topLeft.Y + child.ActualHeight;
zones[i].X = (int)topLeft.X;
zones[i].Y = (int)topLeft.Y;
zones[i].Width = (int)child.ActualWidth;
zones[i].Height = (int)child.ActualHeight;
i++;
}
return zones;
}
public static EditorOverlay Current;
public EditorOverlay()
{
InitializeComponent();
Current = this;
// TODO: multimon
// Need to set Left and Top to the correct monitor based on the
// foreground window passed in the command line arguments
Rect workArea = System.Windows.SystemParameters.WorkArea;
Left = workArea.Left;
Top = workArea.Top;
Width = workArea.Width;
Height = workArea.Height;
}
void onLoad(object sender, RoutedEventArgs e)
{
ShowLayoutPicker();
}
public void ShowLayoutPicker()
{
DataContext = null;
_editor = null;
_layoutPreview = new LayoutPreview();
_layoutPreview.IsActualSize = true;
_layoutPreview.Opacity = 0.5;
Content = _layoutPreview;
MainWindow window = new MainWindow();
window.Owner = this;
window.Show();
}
// These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard
// They reflect that current state into properties on the Settings object, which the Zone view will listen to in editing mode
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
_settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
_settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
base.OnPreviewKeyDown(e);
}
protected override void OnPreviewKeyUp(KeyEventArgs e)
{
_settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
_settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
base.OnPreviewKeyUp(e);
}
public void Edit()
{
_layoutPreview = null;
if (DataContext is GridLayoutModel)
{
_editor = new GridEditor();
}
else if (DataContext is CanvasLayoutModel)
{
_editor = new CanvasEditor();
}
Content = _editor;
}
private Settings _settings = ((App)Application.Current).ZoneSettings;
private LayoutPreview _layoutPreview;
private UserControl _editor;
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
using MahApps.Metro.Controls;
namespace FancyZonesEditor
{
public class EditorWindow : MetroWindow
{
protected void OnSaveApplyTemplate(object sender, RoutedEventArgs e)
{
EditorOverlay mainEditor = EditorOverlay.Current;
LayoutModel model = mainEditor.DataContext as LayoutModel;
if (model != null)
{
model.Persist(mainEditor.GetZoneRects());
}
_choosing = true;
this.Close();
EditorOverlay.Current.Close();
}
protected void OnClosed(object sender, EventArgs e)
{
if (!_choosing)
{
EditorOverlay.Current.ShowLayoutPicker();
}
}
protected void OnCancel(object sender, RoutedEventArgs e)
{
_choosing = true;
this.Close();
EditorOverlay.Current.ShowLayoutPicker();
}
private bool _choosing = false;
}
}

View File

@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>FancyZonesEditor</RootNamespace>
<AssemblyName>FancyZonesEditor</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\..\x64\Debug\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="CanvasEditor.xaml.cs">
<DependentUpon>CanvasEditor.xaml</DependentUpon>
</Compile>
<Compile Include="EditorWindow.cs" />
<Compile Include="GridEditor.xaml.cs">
<DependentUpon>GridEditor.xaml</DependentUpon>
</Compile>
<Compile Include="GridResizer.xaml.cs">
<DependentUpon>GridResizer.xaml</DependentUpon>
</Compile>
<Compile Include="LayoutPreview.xaml.cs">
<DependentUpon>LayoutPreview.xaml</DependentUpon>
</Compile>
<Compile Include="Models\CanvasLayoutModel.cs" />
<Compile Include="Models\GridLayoutModel.cs" />
<Compile Include="Models\LayoutModel.cs" />
<Compile Include="Models\Settings.cs" />
<Compile Include="EditorOverlay.xaml.cs">
<DependentUpon>EditorOverlay.xaml</DependentUpon>
</Compile>
<Compile Include="RowColInfo.cs" />
<Compile Include="GridEditorWindow.xaml.cs">
<DependentUpon>GridEditorWindow.xaml</DependentUpon>
</Compile>
<Compile Include="WindowLayout.xaml.cs">
<DependentUpon>WindowLayout.xaml</DependentUpon>
</Compile>
<Compile Include="CanvasEditorWindow.xaml.cs">
<DependentUpon>CanvasEditorWindow.xaml</DependentUpon>
</Compile>
<Compile Include="CanvasZone.xaml.cs">
<DependentUpon>CanvasZone.xaml</DependentUpon>
</Compile>
<Compile Include="GridZone.xaml.cs">
<DependentUpon>GridZone.xaml</DependentUpon>
</Compile>
<Page Include="CanvasEditor.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="GridEditor.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="GridResizer.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="LayoutPreview.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="EditorOverlay.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="GridEditorWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="WindowLayout.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="CanvasEditorWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="CanvasZone.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="GridZone.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MahApps.Metro">
<Version>2.0.0-alpha0455</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Resource Include="images\ChromeClose.png" />
<Resource Include="images\Delete.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\Merge.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,35 @@
<UserControl x:Class="FancyZonesEditor.GridEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Width" Value="150"/>
</Style>
</UserControl.Resources>
<Grid>
<Canvas x:Name="Preview"/>
<Canvas x:Name="AdornerLayer"/>
<Canvas x:Name="MergePanel" Visibility="Collapsed" MouseUp="MergePanelMouseUp">
<StackPanel x:Name="MergeButtons" Background="Gray" Orientation="Horizontal">
<Button Click="MergeClick" Margin="0" Height="36" Width="134">
<StackPanel Orientation="Horizontal">
<Image Source="images/Merge.png" Margin="0,0,12,0" Height="16" HorizontalAlignment="Left" />
<TextBlock Text="Merge zones"/>
</StackPanel>
</Button>
</StackPanel>
</Canvas>
</Grid>
</UserControl>

View File

@@ -0,0 +1,911 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// GridEditor is how you tweak an initial GridLayoutModel before saving
/// </summary>
public partial class GridEditor : UserControl
{
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(GridLayoutModel), typeof(GridEditor), new PropertyMetadata(null, OnGridDimensionsChanged));
public GridEditor()
{
InitializeComponent();
Loaded += GridEditor_Loaded;
((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
{
GridLayoutModel model = (GridLayoutModel)DataContext;
if (model != null)
{
int rows = model.Rows;
int cols = model.Columns;
_rowInfo = new RowColInfo[rows];
for (int row = 0; row < rows; row++)
{
_rowInfo[row] = new RowColInfo(model.RowPercents[row]);
}
_colInfo = new RowColInfo[cols];
for (int col = 0; col < cols; col++)
{
_colInfo[col] = new RowColInfo(model.ColumnPercents[col]);
}
int maxIndex = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
maxIndex = Math.Max(maxIndex, model.CellChildMap[row, col]);
}
}
for (int i = 0; i <= maxIndex; i++)
{
AddZone();
}
}
Model = model;
if (Model == null)
{
Model = new GridLayoutModel();
DataContext = Model;
}
Model.PropertyChanged += OnGridDimensionsChanged;
AddDragHandles();
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Size actualSize = new Size(ActualWidth, ActualHeight);
if (actualSize.Width > 0)
{
ArrangeGridRects(actualSize);
}
}
public GridLayoutModel Model
{
get { return (GridLayoutModel)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
}
public Panel PreviewPanel { get { return Preview; } }
private void OnFullSplit(object o, SplitEventArgs e)
{
UIElementCollection previewChildren = Preview.Children;
UIElement splitee = (UIElement)o;
GridLayoutModel model = Model;
int spliteeIndex = previewChildren.IndexOf(splitee);
int rows = model.Rows;
int cols = model.Columns;
_startRow = -1;
_startCol = -1;
for (int row = rows - 1; row >= 0; row--)
{
for (int col = cols - 1; col >= 0; col--)
{
if (model.CellChildMap[row, col] == spliteeIndex)
{
RemoveDragHandles();
_startRow = _endRow = row;
_startCol = _endCol = col;
ExtendRangeToHaveEvenCellEdges();
for (row = _startRow; row <= _endRow; row++)
{
for (col = _startCol; col <= _endCol; col++)
{
if ((row != _startRow) || (col != _startCol))
{
model.CellChildMap[row, col] = AddZone();
}
}
}
OnGridDimensionsChanged();
return;
}
}
}
}
private void ExtendRangeToHaveEvenCellEdges()
{
// extend each edge of the [(_startCol, _startRow) - (_endCol, _endRow)] range based on merged cells until you have 4 straight edges with no "straddling cells"
GridLayoutModel model = Model;
while (_startRow > 0)
{
bool dirty = false;
for (int col = _startCol; col <= _endCol; col++)
{
if (model.CellChildMap[_startRow - 1, col] == model.CellChildMap[_startRow, col])
{
_startRow--;
dirty = true;
break;
}
}
if (!dirty)
{
break;
}
}
while (_endRow < model.Rows - 1)
{
bool dirty = false;
for (int col = _startCol; col <= _endCol; col++)
{
if (model.CellChildMap[_endRow + 1, col] == model.CellChildMap[_endRow, col])
{
_endRow++;
dirty = true;
break;
}
}
if (!dirty)
{
break;
}
}
while (_startCol > 0)
{
bool dirty = false;
for (int row = _startRow; row <= _endRow; row++)
{
if (model.CellChildMap[row, _startCol - 1] == model.CellChildMap[row, _startCol])
{
_startCol--;
dirty = true;
break;
}
}
if (!dirty)
{
break;
}
}
while (_endCol < model.Columns - 1)
{
bool dirty = false;
for (int row = _startRow; row <= _endRow; row++)
{
if (model.CellChildMap[row, _endCol + 1] == model.CellChildMap[row, _endCol])
{
_endCol++;
dirty = true;
break;
}
}
if (!dirty)
{
break;
}
}
}
private void OnSplit(object o, SplitEventArgs e)
{
UIElementCollection previewChildren = Preview.Children;
GridZone splitee = (GridZone)o;
int spliteeIndex = previewChildren.IndexOf(splitee);
GridLayoutModel model = Model;
int rows = model.Rows;
int cols = model.Columns;
int foundRow = -1;
int foundCol = -1;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (model.CellChildMap[row, col] == spliteeIndex)
{
foundRow = row;
foundCol = col;
break;
}
}
if (foundRow != -1)
{
break;
}
}
int newChildIndex = AddZone();
double offset = e.Offset;
if (e.Orientation == Orientation.Vertical)
{
if (splitee.VerticalSnapPoints != null)
{
offset += Canvas.GetLeft(splitee);
int count = splitee.VerticalSnapPoints.Length;
bool foundExistingSplit = false;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkRow = foundRow;
while ((walkRow < rows) && (model.CellChildMap[walkRow, foundCol + i] == spliteeIndex))
{
model.CellChildMap[walkRow++, foundCol + i] = newChildIndex;
}
}
if (_colInfo[foundCol + i].End == offset)
{
foundExistingSplit = true;
// use existing division
}
}
if (foundExistingSplit)
{
OnGridDimensionsChanged();
return;
}
while (_colInfo[foundCol].End < offset)
{
foundCol++;
}
offset -= _colInfo[foundCol].Start;
}
AddDragHandle(Orientation.Vertical, cols - 1);
cols++;
int[,] newCellChildMap = new int[rows, cols];
int[] newColPercents = new int[cols];
RowColInfo[] newColInfo = new RowColInfo[cols];
int sourceCol = 0;
for (int col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
if ((col > foundCol) && (model.CellChildMap[row, sourceCol] == spliteeIndex))
{
newCellChildMap[row, col] = newChildIndex;
}
else
{
newCellChildMap[row, col] = model.CellChildMap[row, sourceCol];
}
}
if (col != foundCol)
{
sourceCol++;
}
}
model.CellChildMap = newCellChildMap;
sourceCol = 0;
for (int col = 0; col < cols; col++)
{
if (col == foundCol)
{
RowColInfo[] split = _colInfo[col].Split(offset);
newColPercents[col] = split[0].Percent;
newColInfo[col++] = split[0];
newColPercents[col] = split[1].Percent;
newColInfo[col] = split[1];
sourceCol++;
}
else
{
newColPercents[col] = model.ColumnPercents[sourceCol];
newColInfo[col] = _colInfo[sourceCol++];
}
}
_colInfo = newColInfo;
model.ColumnPercents = newColPercents;
model.Columns++;
}
else // Horizontal
{
if (splitee.HorizontalSnapPoints != null)
{
offset += Canvas.GetTop(splitee);
int count = splitee.HorizontalSnapPoints.Length;
bool foundExistingSplit = false;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkCol = foundCol;
while ((walkCol < cols) && (model.CellChildMap[foundRow + i, walkCol] == spliteeIndex))
{
model.CellChildMap[foundRow + i, walkCol] = newChildIndex;
}
}
if (_rowInfo[foundRow + i].End == offset)
{
foundExistingSplit = true;
// use existing division
}
}
if (foundExistingSplit)
{
OnGridDimensionsChanged();
return;
}
while (_rowInfo[foundRow].End < offset)
{
foundRow++;
}
offset -= _rowInfo[foundRow].Start;
}
AddDragHandle(Orientation.Horizontal, rows - 1);
rows++;
int[,] newCellChildMap = new int[rows, cols];
int[] newRowPercents = new int[rows];
RowColInfo[] newRowInfo = new RowColInfo[rows];
int sourceRow = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if ((row > foundRow) && (model.CellChildMap[sourceRow, col] == spliteeIndex))
{
newCellChildMap[row, col] = newChildIndex;
}
else
{
newCellChildMap[row, col] = model.CellChildMap[sourceRow, col];
}
}
if (row != foundRow)
{
sourceRow++;
}
}
model.CellChildMap = newCellChildMap;
sourceRow = 0;
for (int row = 0; row < rows; row++)
{
if (row == foundRow)
{
RowColInfo[] split = _rowInfo[row].Split(offset);
newRowPercents[row] = split[0].Percent;
newRowInfo[row++] = split[0];
newRowPercents[row] = split[1].Percent;
newRowInfo[row] = split[1];
sourceRow++;
}
else
{
newRowPercents[row] = model.RowPercents[sourceRow];
newRowInfo[row] = _rowInfo[sourceRow++];
}
}
_rowInfo = newRowInfo;
model.RowPercents = newRowPercents;
model.Rows++;
}
}
private void RemoveDragHandles()
{
AdornerLayer.Children.Clear();
}
private void AddDragHandles()
{
if (AdornerLayer.Children.Count == 0)
{
int interiorRows = Model.Rows - 1;
int interiorCols = Model.Columns - 1;
for (int row = 0; row < interiorRows; row++)
{
AddDragHandle(Orientation.Horizontal, row);
}
for (int col = 0; col < interiorCols; col++)
{
AddDragHandle(Orientation.Vertical, col);
}
}
}
private void AddDragHandle(Orientation orientation, int index)
{
GridResizer resizer = new GridResizer();
resizer.Orientation = orientation;
resizer.Index = index;
resizer.Model = Model;
resizer.DragDelta += Resizer_DragDelta;
if (orientation == Orientation.Vertical)
{
index += (Model.Rows - 1);
}
AdornerLayer.Children.Insert(index, resizer);
}
private void DeleteZone(int index)
{
IList<int> freeZones = Model.FreeZones;
if (freeZones.Contains(index))
{
return;
}
freeZones.Add(index);
GridZone zone = (GridZone)Preview.Children[index];
zone.Visibility = Visibility.Hidden;
zone.MinHeight = 0;
zone.MinWidth = 0;
}
private int AddZone()
{
GridZone zone;
if (Model != null)
{
IList<int> freeZones = Model.FreeZones;
// first check free list
if (freeZones.Count > 0)
{
int freeIndex = freeZones[0];
freeZones.RemoveAt(0);
zone = (GridZone)Preview.Children[freeIndex];
zone.Visibility = Visibility.Visible;
return freeIndex;
}
}
zone = new GridZone();
zone.Split += OnSplit;
zone.MergeDrag += OnMergeDrag;
zone.MergeComplete += OnMergeComplete;
zone.FullSplit += OnFullSplit;
Preview.Children.Add(zone);
return Preview.Children.Count - 1;
}
private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if ((e.PropertyName == "Rows") || (e.PropertyName == "Columns"))
{
OnGridDimensionsChanged();
}
}
private static void OnGridDimensionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GridEditor)d).OnGridDimensionsChanged();
}
private void OnGridDimensionsChanged()
{
GridLayoutModel model = Model;
Size actualSize = new Size(ActualWidth, ActualHeight);
if (actualSize.Width > 0)
{
ArrangeGridRects(actualSize);
}
}
private void ArrangeGridRects(Size arrangeSize)
{
GridLayoutModel model = Model;
if (model == null)
{
return;
}
Settings settings = ((App)(Application.Current)).ZoneSettings;
int spacing = settings.Spacing;
int gutter = settings.Spacing;
int cols = model.Columns;
int rows = model.Rows;
double totalWidth = arrangeSize.Width - (gutter * 2) - (spacing * (cols - 1));
double totalHeight = arrangeSize.Height - (gutter * 2) - (spacing * (rows - 1));
double top = gutter;
for (int row = 0; row < rows; row++)
{
double cellHeight = _rowInfo[row].SetExtent(top, totalHeight);
top += cellHeight + spacing;
}
double left = gutter;
for (int col = 0; col < cols; col++)
{
double cellWidth = _colInfo[col].SetExtent(left, totalWidth);
left += cellWidth + spacing;
}
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
int i = model.CellChildMap[row, col];
if (((row == 0) || (model.CellChildMap[row - 1, col] != i)) &&
((col == 0) || (model.CellChildMap[row, col - 1] != i)))
{
// this is not a continuation of a span
GridZone zone = (GridZone)Preview.Children[i];
left = _colInfo[col].Start;
top = _rowInfo[row].Start;
Canvas.SetLeft(zone, left);
Canvas.SetTop(zone, top);
int maxRow = row;
while (((maxRow + 1) < rows) && (model.CellChildMap[maxRow + 1, col] == i))
{
maxRow++;
}
zone.HorizontalSnapPoints = null;
if (maxRow > row)
{
zone.HorizontalSnapPoints = new double[maxRow - row];
int pointsIndex = 0;
for (int walk = row; walk < maxRow; walk++)
{
zone.HorizontalSnapPoints[pointsIndex++] = _rowInfo[walk].End + spacing / 2 - top;
}
}
int maxCol = col;
while (((maxCol + 1) < cols) && (model.CellChildMap[row, maxCol + 1] == i))
{
maxCol++;
}
zone.VerticalSnapPoints = null;
if (maxCol > col)
{
zone.VerticalSnapPoints = new double[maxCol - col];
int pointsIndex = 0;
for (int walk = col; walk < maxCol; walk++)
{
zone.VerticalSnapPoints[pointsIndex++] = _colInfo[walk].End + spacing / 2 - left;
}
}
zone.MinWidth = _colInfo[maxCol].End - left;
zone.MinHeight = _rowInfo[maxRow].End - top;
}
}
}
AddDragHandles();
int childIndex = 0;
UIElementCollection adornerChildren = AdornerLayer.Children;
for (int row = 0; row < rows - 1; row++)
{
GridResizer resizer = (GridResizer)adornerChildren[childIndex++];
int startCol = -1;
int endCol = cols - 1;
for (int col = 0; col < cols; col++)
{
if ((startCol == -1) && (model.CellChildMap[row, col] != model.CellChildMap[row + 1, col]))
{
startCol = col;
}
else if ((startCol != -1) && (model.CellChildMap[row, col] == model.CellChildMap[row + 1, col]))
{
endCol = col - 1;
break;
}
}
if (startCol != -1)
{
Canvas.SetTop(resizer, _rowInfo[row].End + (spacing / 2) - 24); // hard coding this as (resizer.ActualHeight / 2) will still evaluate to 0 here ... a layout hasn't yet happened
Canvas.SetLeft(resizer, (_colInfo[endCol].End + _colInfo[startCol].Start) / 2);
}
else
{
resizer.Visibility = Visibility.Collapsed;
}
}
for (int col = 0; col < cols - 1; col++)
{
GridResizer resizer = (GridResizer)adornerChildren[childIndex++];
int startRow = -1;
int endRow = rows - 1;;
for (int row = 0; row < rows; row++)
{
if ((startRow == -1) && (model.CellChildMap[row, col] != model.CellChildMap[row, col + 1]))
{
startRow = row;
}
else if ((startRow != -1) && (model.CellChildMap[row, col] == model.CellChildMap[row, col + 1]))
{
endRow = row - 1;
break;
}
}
if (startRow != -1)
{
Canvas.SetLeft(resizer, _colInfo[col].End + (spacing / 2) - 24); // hard coding this as (resizer.ActualWidth / 2) will still evaluate to 0 here ... a layout hasn't yet happened
Canvas.SetTop(resizer, (_rowInfo[endRow].End + _rowInfo[startRow].Start) / 2);
}
else
{
resizer.Visibility = Visibility.Collapsed;
}
}
}
private void Resizer_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
GridResizer resizer = (GridResizer)sender;
int[] percents;
RowColInfo[] info;
int index = resizer.Index;
double delta;
if (resizer.Orientation == Orientation.Vertical)
{
percents = Model.ColumnPercents;
info = _colInfo;
delta = e.HorizontalChange;
}
else
{
percents = Model.RowPercents;
info = _rowInfo;
delta = e.VerticalChange;
}
double currentExtent = info[index].Extent;
double newExtent = currentExtent + delta;
int currentPercent = info[index].Percent;
int totalPercent = currentPercent + info[index + 1].Percent;
int newPercent = (int)(currentPercent * newExtent / currentExtent);
if ((newPercent > 0) && (newPercent < totalPercent))
{
percents[index] = info[index].Percent = newPercent;
percents[index + 1] = info[index + 1].Percent = totalPercent - newPercent;
Size actualSize = new Size(ActualWidth, ActualHeight);
ArrangeGridRects(actualSize);
}
}
private Point _startDragPos = new Point(-1, -1);
private void OnMergeComplete(object o, MouseButtonEventArgs e)
{
Point mousePoint = e.GetPosition(Preview);
_startDragPos = new Point(-1, -1);
int mergedIndex = Model.CellChildMap[_startRow, _startCol];
for (int row = _startRow; row <= _endRow; row++)
{
for (int col = _startCol; col <= _endCol; col++)
{
if (Model.CellChildMap[row, col] != mergedIndex)
{
// selection is more than one cell, merge is valid
MergePanel.Visibility = Visibility.Visible;
Canvas.SetTop(MergeButtons, mousePoint.Y);
Canvas.SetLeft(MergeButtons, mousePoint.X);
return;
}
}
}
// merge is only one zone. cancel merge;
ClearSelection();
}
private void OnMergeDrag(object o, MouseEventArgs e)
{
if (_startDragPos.X == -1)
{
_startDragPos = e.GetPosition(Preview);
}
GridLayoutModel model = Model;
if (_startDragPos.X != -1)
{
Point dragPos = e.GetPosition(Preview);
_startRow = -1;
_endRow = -1;
_startCol = -1;
_endCol = -1;
int rows = model.Rows;
int cols = model.Columns;
double minX, maxX;
if (dragPos.X < _startDragPos.X)
{
minX = dragPos.X;
maxX = _startDragPos.X;
}
else
{
minX = _startDragPos.X;
maxX = dragPos.X;
}
double minY, maxY;
if (dragPos.Y < _startDragPos.Y)
{
minY = dragPos.Y;
maxY = _startDragPos.Y;
}
else
{
minY = _startDragPos.Y;
maxY = dragPos.Y;
}
for (int row = 0; row < rows; row++)
{
if (_startRow == -1)
{
if (_rowInfo[row].End > minY)
{
_startRow = row;
}
}
else if (_rowInfo[row].Start > maxY)
{
_endRow = row - 1;
break;
}
}
if ((_startRow >= 0) && (_endRow == -1))
{
_endRow = rows - 1;
}
for (int col = 0; col < cols; col++)
{
if (_startCol == -1)
{
if (_colInfo[col].End > minX)
{
_startCol = col;
}
}
else if (_colInfo[col].Start > maxX)
{
_endCol = col - 1;
break;
}
}
if ((_startCol >= 0) && (_endCol == -1))
{
_endCol = cols - 1;
}
ExtendRangeToHaveEvenCellEdges();
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
((GridZone)Preview.Children[model.CellChildMap[row, col]]).IsSelected = (row >= _startRow) && (row <= _endRow) && (col >= _startCol) && (col <= _endCol);
}
}
e.Handled = true;
}
base.OnPreviewMouseMove(e);
}
private void ClearSelection()
{
foreach (UIElement zone in Preview.Children)
{
((GridZone) zone).IsSelected = false;
}
}
private void MergeClick(object sender, RoutedEventArgs e)
{
GridLayoutModel model = Model;
MergePanel.Visibility = Visibility.Collapsed;
int mergedIndex = model.CellChildMap[_startRow, _startCol];
for (int row = _startRow; row <= _endRow; row++)
{
for (int col = _startCol; col <= _endCol; col++)
{
int childIndex = model.CellChildMap[row, col];
if (childIndex != mergedIndex)
{
model.CellChildMap[row, col] = mergedIndex;
DeleteZone(childIndex);
}
}
}
OnGridDimensionsChanged();
ClearSelection();
}
private void CancelClick(object sender, RoutedEventArgs e)
{
MergePanel.Visibility = Visibility.Collapsed;
ClearSelection();
}
private void MergePanelMouseUp(object sender, MouseButtonEventArgs e)
{
CancelClick(null, null);
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
Size returnSize = base.ArrangeOverride(arrangeBounds);
ArrangeGridRects(arrangeBounds);
return returnSize;
}
Point _mouseDownPos = new Point(-1, -1);
private RowColInfo[] _rowInfo;
private RowColInfo[] _colInfo;
private int _startRow = -1;
private int _endRow = -1;
private int _startCol = -1;
private int _endCol = -1;
private const int c_multiplier = 10000;
}
}

View File

@@ -0,0 +1,184 @@
<local:EditorWindow x:Class="FancyZonesEditor.GridEditorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title=""
Width="528"
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Closed="OnClosed">
<Window.Resources>
<Style x:Key="titleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LineHeight" Value="24" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="16,20,0,12" />
</Style>
<Style x:Key="zoneCount" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="FontSize" Value="24"/>
<Setter Property="LineHeight" Value="24" />
<Setter Property="Margin" Value="20,0,20,0" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="tabText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="#C4C4C4"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="24,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<Style x:Key="settingText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="5,10,0,0" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="templateTitleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="250"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style x:Key="secondaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#BCBCBD"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="primaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#0078D7"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#024D89"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="spinnerButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="0,0,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="0,0,0,0" />
</Style>
<Style x:Key="templateBackground" TargetType="Rectangle">
<Setter Property="Fill" Value="Black"/>
<Setter Property="Opacity" Value="0.05"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
</Style>
<Style x:Key="templateBackgroundSelected" TargetType="Rectangle">
<Setter Property="Fill" Value="#F2F2F2"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
<Setter Property="Stroke" Value="#0078D7"/>
<Setter Property="StrokeThickness" Value="2"/>
</Style>
<Style x:Key="newZoneButton" TargetType="Button">
<Setter Property="Background" Value="#f2f2f2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="120"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="textLabel" TargetType="TextBlock">
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="16,12,0,0"/>
</Style>
<Style x:Key="textBox" TargetType="TextBox">
<Setter Property="BorderBrush" Value="#cccccc"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,5,0,0"/>
<Setter Property="Padding" Value="5,5,5,5"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Name="windowEditorDialogTitle" Text="Custom table layout creator" Style="{StaticResource titleText}" />
<TextBlock Text="Note: Hold down Shift Key to change orientation of splitter" Style="{StaticResource textLabel}" />
<TextBlock Text="Name" Style="{StaticResource textLabel}" />
<TextBox Text="{Binding Name}" Width="494" Style="{StaticResource textBox}" />
<!--
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="21,4,0,0"/>
<TextBlock Text="Show snap grid" Style="{StaticResource settingText}" />
<TextBlock Text="Grid spacing" Style="{StaticResource settingText}" Margin="40,10,10,0" />
<TextBox x:Name="gridValue" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}"/>
</StackPanel>
-->
<StackPanel Orientation="Horizontal" Margin="0,12,0,16">
<Button Content="Cancel" Style="{StaticResource secondaryButton}" Click="OnCancel" />
<Button Content="Save and apply" Style="{StaticResource primaryButton}" Click="OnSaveApplyTemplate" />
</StackPanel>
</StackPanel>
</local:EditorWindow>

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
using MahApps.Metro.Controls;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class GridEditorWindow : EditorWindow
{
public GridEditorWindow()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,18 @@
<Thumb x:Class="FancyZonesEditor.GridResizer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Thumb.Template>
<ControlTemplate>
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
<Ellipse Height="48" Width="48" Fill="#0078D7" />
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
</StackPanel>
</ControlTemplate>
</Thumb.Template>
</Thumb>

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for GridResizer.xaml
/// </summary>
public partial class GridResizer : Thumb
{
public GridResizer()
{
InitializeComponent();
}
public Orientation Orientation
{
get
{
return _orientation;
}
set
{
_orientation = value;
ApplyTemplate();
StackPanel body = (StackPanel)Template.FindName("Body", this);
if (value == Orientation.Vertical)
{
body.RenderTransform = null;
body.Cursor = Cursors.SizeWE;
}
else
{
body.RenderTransform = c_rotateTransform;
body.Cursor = Cursors.SizeNS;
}
}
}
private static RotateTransform c_rotateTransform = new RotateTransform(90, 24, 24);
public int Index;
public LayoutModel Model;
private Orientation _orientation;
}
}

View File

@@ -0,0 +1,15 @@
<UserControl x:Class="FancyZonesEditor.GridZone"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Background="LightGray"
Opacity="0.5"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="Frame" Visibility="Collapsed">
<Canvas x:Name="Body" />
<!--<TextBlock Margin="2" Text="Shift Key switches direction&#13;Ctrl Key repeats"/>-->
</Grid>
</UserControl>

View File

@@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace FancyZonesEditor
{
/// <summary>
/// Once you've "Committ"ed the starter grid, then the Zones within the grid come to life for you to be able to further subdivide them
/// using splitters
/// </summary>
public partial class GridZone : UserControl
{
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(GridZone), new PropertyMetadata(false, OnSelectionChanged));
private static void OnSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GridZone)d).OnSelectionChanged();
}
private void OnSelectionChanged()
{
Background = IsSelected ? Brushes.SteelBlue : Brushes.LightGray;
}
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public double[] VerticalSnapPoints;
public double[] HorizontalSnapPoints;
public GridZone()
{
InitializeComponent();
OnSelectionChanged();
_splitter = new Rectangle();
_splitter.Fill = Brushes.DarkGray;
Body.Children.Add(_splitter);
((App) Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsShiftKeyPressed")
{
_switchOrientation = ((App)Application.Current).ZoneSettings.IsShiftKeyPressed;
if (_lastPos.X != -1)
{
UpdateSplitter();
}
}
}
protected override Size ArrangeOverride(Size size)
{
_splitOrientation = (size.Width > size.Height) ? Orientation.Vertical : Orientation.Horizontal;
return base.ArrangeOverride(size);
}
private bool IsVerticalSplit
{
get
{
bool isVertical = _splitOrientation == Orientation.Vertical;
if (_switchOrientation)
{
isVertical = !isVertical;
}
return isVertical;
}
}
private int SplitterThickness { get { return Math.Max(((App)Application.Current).ZoneSettings.Spacing, 5); } }
private void UpdateSplitter()
{
int thickness = SplitterThickness;
if (IsVerticalSplit)
{
double bodyWidth = Body.ActualWidth;
double pos = _lastPos.X - thickness/2;
if (pos < 0)
{
pos = 0;
}
else if (pos > (bodyWidth - thickness))
{
pos = bodyWidth - thickness;
}
Canvas.SetLeft(_splitter, pos);
Canvas.SetTop(_splitter, 0);
_splitter.MinWidth = thickness;
_splitter.MinHeight = Body.ActualHeight;
}
else
{
double bodyHeight = Body.ActualHeight;
double pos = _lastPos.Y - thickness / 2;
if (pos < 0)
{
pos = 0;
}
else if (pos > (bodyHeight - thickness))
{
pos = bodyHeight - thickness;
}
Canvas.SetLeft(_splitter, 0);
Canvas.SetTop(_splitter, pos);
_splitter.MinWidth = Body.ActualWidth;
_splitter.MinHeight = thickness;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
Frame.Visibility = Visibility.Visible;
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
Frame.Visibility = Visibility.Collapsed;
base.OnMouseLeave(e);
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
_mouseDownPos = _lastPos;
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_inMergeDrag)
{
DoMergeDrag(e);
}
else
{
_lastPos = e.GetPosition(Body);
if (IsVerticalSplit)
{
if (VerticalSnapPoints != null)
{
int thickness = SplitterThickness;
foreach (double snapPoint in VerticalSnapPoints)
{
if (Math.Abs(_lastPos.X - snapPoint) <= (thickness * 2))
{
_lastPos.X = snapPoint;
break;
}
}
}
}
else // horizontal split
{
if (HorizontalSnapPoints != null)
{
int thickness = SplitterThickness;
foreach (double snapPoint in HorizontalSnapPoints)
{
if (Math.Abs(_lastPos.Y - snapPoint) <= (thickness * 2))
{
_lastPos.Y = snapPoint;
break;
}
}
}
}
if (_mouseDownPos.X == -1)
{
UpdateSplitter();
}
else
{
double threshold = SplitterThickness / 2;
if ((Math.Abs(_mouseDownPos.X - _lastPos.X) > threshold) || (Math.Abs(_mouseDownPos.Y - _lastPos.Y) > threshold))
{
// switch to merge (which is handled by parent GridEditor)
_inMergeDrag = true;
Mouse.Capture(this, CaptureMode.Element);
DoMergeDrag(e);
_splitter.Visibility = Visibility.Hidden;
}
}
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
if (_inMergeDrag)
{
Mouse.Capture(this, CaptureMode.None);
DoMergeComplete(e);
_inMergeDrag = false;
_splitter.Visibility = Visibility.Visible;
}
else
{
int thickness = SplitterThickness;
double delta = IsVerticalSplit ? _mouseDownPos.X - _lastPos.X : _mouseDownPos.Y - _lastPos.Y;
if (Math.Abs(delta) <= thickness / 2)
{
if (IsVerticalSplit)
{
DoSplit(Orientation.Vertical, _lastPos.X - (thickness / 2));
}
else
{
DoSplit(Orientation.Horizontal, _lastPos.Y - (thickness / 2));
}
}
}
_mouseDownPos = new Point(-1, -1);
base.OnMouseUp(e);
}
public event SplitEventHandler Split;
public event SplitEventHandler FullSplit;
public event MouseEventHandler MergeDrag;
public event MouseButtonEventHandler MergeComplete;
private Rectangle _splitter;
private bool _switchOrientation = false;
private Point _lastPos = new Point(-1,-1);
private Point _mouseDownPos = new Point(-1,-1);
private bool _inMergeDrag = false;
private Orientation _splitOrientation;
private void DoMergeDrag(MouseEventArgs e)
{
if (MergeDrag != null)
{
MergeDrag(this, e);
}
}
private void DoMergeComplete(MouseButtonEventArgs e)
{
if (MergeComplete != null)
{
MergeComplete(this, e);
}
}
private void DoSplit(Orientation orientation, double offset)
{
if (Split != null)
{
Split(this, new SplitEventArgs(orientation, offset));
}
}
private void FullSplit_Click(object sender, RoutedEventArgs e)
{
DoFullSplit();
}
private void DoFullSplit()
{
if (FullSplit != null)
{
FullSplit(this, new SplitEventArgs());
}
}
}
public class SplitEventArgs : EventArgs
{
public SplitEventArgs() { }
public SplitEventArgs(Orientation orientation, double offset)
{
_orientation = orientation;
_offset = offset;
}
public Orientation Orientation { get { return _orientation; } }
public double Offset { get { return _offset; } }
private Orientation _orientation;
private double _offset;
}
public delegate void SplitEventHandler(object sender, SplitEventArgs args);
}

View File

@@ -0,0 +1,13 @@
<UserControl x:Class="FancyZonesEditor.LayoutPreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Loaded="OnLoaded"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="Body">
</Grid>
</UserControl>

View File

@@ -0,0 +1,192 @@
using FancyZonesEditor.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for LayoutPreview.xaml
/// </summary>
public partial class LayoutPreview : UserControl
{
public static readonly DependencyProperty IsActualSizeProperty = DependencyProperty.Register("IsActualSize", typeof(bool), typeof(LayoutPreview), new PropertyMetadata(false));
public LayoutPreview()
{
InitializeComponent();
DataContextChanged += LayoutPreview_DataContextChanged;
((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
private void LayoutPreview_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
_model = (LayoutModel)DataContext;
RenderPreview();
}
public bool IsActualSize
{
get { return (bool)GetValue(IsActualSizeProperty); }
set { SetValue(IsActualSizeProperty, value); }
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "ZoneCount")
{
RenderPreview();
}
else if ((e.PropertyName == "ShowSpacing") || (e.PropertyName == "Spacing"))
{
if (IsActualSize)
{
Settings settings = ((App)Application.Current).ZoneSettings;
Body.Margin = new Thickness(settings.ShowSpacing ? settings.Spacing / 2 : 0);
}
else
{
Body.Margin = new Thickness(0);
}
if (_model is GridLayoutModel)
{
RenderPreview();
}
}
}
public Panel PreviewPanel { get { return Body; } }
private void OnLoaded(object sender, RoutedEventArgs e)
{
_model = (LayoutModel)DataContext;
RenderPreview();
}
private void RenderPreview()
{
if (_model == null)
{
return;
}
Body.Children.Clear();
GridLayoutModel gridModel = _model as GridLayoutModel;
if (gridModel != null)
{
RenderGridPreview(gridModel);
}
else
{
CanvasLayoutModel canvasModel = _model as CanvasLayoutModel;
if (canvasModel != null)
{
RenderCanvasPreview(canvasModel);
}
}
}
private void RenderGridPreview(GridLayoutModel grid)
{
Body.RowDefinitions.Clear();
foreach (int percent in grid.RowPercents)
{
RowDefinition def = new RowDefinition();
def.Height = new GridLength(percent, GridUnitType.Star);
Body.RowDefinitions.Add(def);
}
Body.ColumnDefinitions.Clear();
foreach (int percent in grid.ColumnPercents)
{
ColumnDefinition def = new ColumnDefinition();
def.Width = new GridLength(percent, GridUnitType.Star);
Body.ColumnDefinitions.Add(def);
}
Settings settings = ((App) Application.Current).ZoneSettings;
int divisor = IsActualSize ? 2 : 20;
Thickness margin = new Thickness(settings.ShowSpacing ? settings.Spacing / divisor : 0);
List<int> visited = new List<int>();
for (int row = 0; row < grid.Rows; row++)
{
for (int col = 0; col < grid.Columns; col++)
{
int childIndex = grid.CellChildMap[row,col];
if (!visited.Contains(childIndex))
{
visited.Add(childIndex);
Rectangle rect = new Rectangle();
Grid.SetRow(rect, row);
Grid.SetColumn(rect, col);
int span = 1;
int walk = row + 1;
while ((walk < grid.Rows) && grid.CellChildMap[walk,col] == childIndex)
{
span++;
walk++;
}
Grid.SetRowSpan(rect, span);
span = 1;
walk = col + 1;
while ((walk < grid.Columns) && grid.CellChildMap[row, walk] == childIndex)
{
span++;
walk++;
}
Grid.SetColumnSpan(rect, span);
rect.Margin = margin;
rect.StrokeThickness = 1;
rect.Stroke = Brushes.DarkGray;
rect.Fill = Brushes.LightGray;
Body.Children.Add(rect);
}
}
}
}
private void RenderCanvasPreview(CanvasLayoutModel canvas)
{
Body.RowDefinitions.Clear();
Body.ColumnDefinitions.Clear();
Viewbox viewbox = new Viewbox();
viewbox.Stretch = Stretch.Uniform;
Body.Children.Add(viewbox);
Canvas frame = new Canvas();
viewbox.Child = frame;
frame.Width = canvas.ReferenceWidth;
frame.Height = canvas.ReferenceHeight;
foreach (Int32Rect zone in canvas.Zones)
{
Rectangle rect = new Rectangle();
Canvas.SetTop(rect, zone.Y);
Canvas.SetLeft(rect, zone.X);
rect.MinWidth = zone.Width;
rect.MinHeight = zone.Height;
rect.StrokeThickness = 5;
rect.Stroke = Brushes.DarkGray;
rect.Fill = Brushes.LightGray;
frame.Children.Add(rect);
}
}
private LayoutModel _model;
}
}

View File

@@ -0,0 +1,277 @@
<Controls:MetroWindow x:Class="FancyZonesEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title="" Width="810"
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Loaded="OnLoaded"
Closed="OnClosed">
<Window.Resources>
<local:BooleanToBrushConverter x:Key="BooleanToBrushConverter" />
<local:ModelToVisibilityConverter x:Key="ModelToVisibilityConverter" />
<Style x:Key="titleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LineHeight" Value="24" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="16,20,0,0" />
</Style>
<Style x:Key="zoneCount" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="FontSize" Value="24"/>
<Setter Property="LineHeight" Value="24" />
<Setter Property="Margin" Value="20,0,20,0" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="tabText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="#C4C4C4"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="24,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<Style x:Key="settingText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="40,10,0,0" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="settingCheckBoxText" TargetType="CheckBox">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="5,10,0,0" />
</Style>
<Style x:Key="templateTitleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="secondaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="16,0,0,0" />
<Setter Property="Width" Value="378"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#BCBCBD"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="primaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#0078D7"/>
<Setter Property="Margin" Value="16,0,16,0" />
<Setter Property="Width" Value="377"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#024D89"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="spinnerButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="0,0,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="0,0,0,0" />
</Style>
<Style x:Key="newLayoutButton" TargetType="Button">
<Setter Property="Background" Value="#f2f2f2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="148"/>
</Style>
<Style x:Key="textBox" TargetType="TextBox">
<Setter Property="BorderBrush" Value="#cccccc"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="16,6,0,0"/>
<Setter Property="Padding" Value="5,5,5,5"/>
</Style>
<Style x:Key="templateBackground" TargetType="Border">
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="BorderThickness" Value="2"/>
</Style>
<ControlTemplate x:Key="myTabs">
<Grid Width="404">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="2"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Name="myTabText" Text="{TemplateBinding TabItem.Header}" Style="{StaticResource tabText}" />
<Rectangle Grid.Row="1" x:Name="myTabLine" Fill="#F2F2F2" Height="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabItem.IsSelected" Value="True">
<Setter TargetName="myTabText" Property="Foreground" Value="#2A79D7"/>
<Setter TargetName="myTabLine" Property="Fill" Value="#2A79D7" />
<Setter TargetName="myTabLine" Property="Height" Value="2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<TextBlock Name="dialog_Title" Text="Choose your layout" Style="{StaticResource titleText}" />
<TabControl BorderThickness="0" x:Name="TemplateTab">
<TabItem Header="Templates" Template="{StaticResource myTabs}">
<StackPanel>
<StackPanel Margin="0,15,0,8" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="decrementZones" Width="40" Height="40" Content="-" Style="{StaticResource spinnerButton}" Click="DecrementZones_Click"/>
<TextBlock x:Name="zoneCount" Text="{Binding ZoneCount}" Style="{StaticResource zoneCount}"/>
<Button x:Name="incrementZones" Width="40" Height="40" Content="+" Style="{StaticResource spinnerButton}" Click="IncrementZones_Click"/>
</StackPanel>
<ItemsControl ItemsSource="{Binding DefaultModels}" Margin="8,0,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ItemWidth="262" ItemHeight="262" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="8"
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
Style="{StaticResource templateBackground}"
MouseDown="LayoutItem_Click">
<DockPanel Margin="0,20,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock Padding="8,0,8,0"
TextTrimming="CharacterEllipsis"
DockPanel.Dock="Top"
Text="{Binding Name}"
Style="{StaticResource templateTitleText}" />
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal" Margin="10,4,0,8">
<CheckBox x:Name="spaceAroundSetting" Content="Show space around zones" Style="{StaticResource settingCheckBoxText}" IsChecked="{Binding ShowSpacing}"/>
<TextBlock Text="Space around zones" Style="{StaticResource settingText}"/>
<TextBox x:Name="paddingValue" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,12,0,16">
<Button x:Name="EditTemplateBUtton" Padding="8" Content="Edit selected layout" Style="{StaticResource secondaryButton}" Click="EditLayout_Click"/>
<Button x:Name="ApplyTemplateButton" Padding="8" Content="Apply" Style="{StaticResource primaryButton}" Click="Apply_Click"/>
</StackPanel>
</StackPanel>
</TabItem>
<TabItem Header="Custom" Template="{StaticResource myTabs}">
<StackPanel>
<ItemsControl ItemsSource="{Binding CustomModels}" Margin="8,8,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ItemWidth="262" ItemHeight="262" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="8"
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
Style="{StaticResource templateBackground}"
MouseDown="LayoutItem_Click">
<DockPanel Margin="0,20,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
LastChildFill="True">
<DockPanel DockPanel.Dock="Top" LastChildFill="True" >
<Button x:Name="DeleteButton" Visibility="{Binding Converter={StaticResource ModelToVisibilityConverter}}" DockPanel.Dock="Right" MaxHeight="10" Click="OnDelete" Padding="8,4,8,4" Margin="0,0,8,0" BorderThickness="0" Background="#f2f2f2">
<Image Source="images/Delete.png" />
</Button>
<TextBlock DockPanel.Dock="Top"
TextTrimming="CharacterEllipsis" Padding="8,0,8,0"
Text="{Binding Name}"
Style="{StaticResource templateTitleText}" />
</DockPanel>
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal" Margin="0,10,0,16">
<Button x:Name="EditCustomButton" Content="Edit selected layout" Padding="8" Style="{StaticResource secondaryButton}" Click="EditLayout_Click"/>
<Button x:Name="ApplyCustomButton" Content="Apply" Padding="8" Style="{StaticResource primaryButton}" Click="Apply_Click"/>
</StackPanel>
</StackPanel>
</TabItem>
</TabControl>
</StackPanel>
</Controls:MetroWindow>

View File

@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
using MahApps.Metro.Controls;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = _settings;
}
private void DecrementZones_Click(object sender, RoutedEventArgs e)
{
if (_settings.ZoneCount > 1)
{
_settings.ZoneCount--;
}
}
private void IncrementZones_Click(object sender, RoutedEventArgs e)
{
if (_settings.ZoneCount < 40)
{
_settings.ZoneCount++;
}
}
private Settings _settings = ((App)Application.Current).ZoneSettings;
private void NewCustomLayoutButton_Click(object sender, RoutedEventArgs e)
{
WindowLayout window = new WindowLayout();
window.Show();
this.Close();
}
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
{
Select(((Border)sender).DataContext as LayoutModel);
}
private void Select(LayoutModel newSelection)
{
LayoutModel currentSelection = EditorOverlay.Current.DataContext as LayoutModel;
if (currentSelection != null)
{
currentSelection.IsSelected = false;
}
newSelection.IsSelected = true;
EditorOverlay.Current.DataContext = newSelection;
}
private static string c_defaultNamePrefix = "Custom Layout ";
private bool _editing = false;
private void EditLayout_Click(object sender, RoutedEventArgs e)
{
_editing = true;
this.Close();
EditorOverlay mainEditor = EditorOverlay.Current;
LayoutModel model = mainEditor.DataContext as LayoutModel;
if (model == null)
{
mainEditor.Close();
return;
}
model.IsSelected = false;
bool isPredefinedLayout = Settings.IsPredefinedLayout(model);
if (!_settings.CustomModels.Contains(model) || isPredefinedLayout)
{
if (isPredefinedLayout)
{
// make a copy
model = model.Clone();
mainEditor.DataContext = model;
}
int maxCustomIndex = 0;
foreach (LayoutModel customModel in _settings.CustomModels)
{
string name = customModel.Name;
if (name.StartsWith(c_defaultNamePrefix))
{
int i;
if (Int32.TryParse(name.Substring(c_defaultNamePrefix.Length), out i))
{
if (maxCustomIndex < i)
{
maxCustomIndex = i;
}
}
}
}
model.Name = c_defaultNamePrefix + (++maxCustomIndex);
}
mainEditor.Edit();
EditorWindow window;
if (model is GridLayoutModel)
{
window = new GridEditorWindow();
}
else
{
window = new CanvasEditorWindow();
}
window.Owner = EditorOverlay.Current;
window.DataContext = model;
window.Show();
}
private void Apply_Click(object sender, RoutedEventArgs e)
{
EditorOverlay mainEditor = EditorOverlay.Current;
LayoutModel model = mainEditor.DataContext as LayoutModel;
if (model != null)
{
if (model is GridLayoutModel)
{
model.Apply(mainEditor.GetZoneRects());
}
else
{
model.Apply((model as CanvasLayoutModel).Zones.ToArray());
}
}
this.Close();
}
private void OnClosed(object sender, EventArgs e)
{
if (!_editing)
{
EditorOverlay.Current.Close();
}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
foreach(LayoutModel model in _settings.CustomModels)
{
if (model.IsSelected)
{
TemplateTab.SelectedIndex = 1;
return;
}
}
}
private void OnDelete(object sender, RoutedEventArgs e)
{
LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel;
if (model.IsSelected)
{
OnLoaded(null, null);
}
model.Delete();
}
}
public class BooleanToBrushConverter : IValueConverter
{
private static Brush c_selectedBrush = new SolidColorBrush(Color.FromRgb(0x00, 0x78, 0xD7));
private static Brush c_normalBrush = new SolidColorBrush(Color.FromRgb(0xF2, 0xF2, 0xF2));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? c_selectedBrush : c_normalBrush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == c_selectedBrush;
}
}
public class ModelToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Settings.IsPredefinedLayout((LayoutModel)value) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Converters;
using System.Windows.Documents;
namespace FancyZonesEditor.Models
{
// CanvasLayoutModel
// Free form Layout Model, which specifies independent zone rects
public class CanvasLayoutModel : LayoutModel
{
public CanvasLayoutModel(ushort version, string name, ushort id, byte[] data) : base(name, id)
{
if (version == c_latestVersion)
{
Load(data);
}
}
public CanvasLayoutModel(string name, ushort id, int referenceWidth, int referenceHeight) : base(name, id)
{
// Initialize Reference Size
_referenceWidth = referenceWidth;
_referenceHeight = referenceHeight;
}
public CanvasLayoutModel(string name, ushort id) : base(name, id) { }
public CanvasLayoutModel(string name) : base(name) { }
public CanvasLayoutModel() : base() { }
// ReferenceWidth - the reference width for the layout rect that all Zones are relative to
public int ReferenceWidth
{
get { return _referenceWidth; }
set
{
if (_referenceWidth != value)
{
_referenceWidth = value;
FirePropertyChanged("ReferenceWidth");
}
}
}
private int _referenceWidth;
// ReferenceHeight - the reference height for the layout rect that all Zones are relative to
public int ReferenceHeight
{
get { return _referenceHeight; }
set
{
if (_referenceHeight != value)
{
_referenceHeight = value;
FirePropertyChanged("ReferenceHeight");
}
}
}
private int _referenceHeight;
// Zones - the list of all zones in this layout, described as independent rectangles
public IList<Int32Rect> Zones { get { return _zones; } }
private IList<Int32Rect> _zones = new List<Int32Rect>();
// RemoveZoneAt
// Removes the specified index from the Zones list, and fires a property changed notification for the Zones property
public void RemoveZoneAt(int index)
{
Zones.RemoveAt(index);
FirePropertyChanged("Zones");
}
// AddZone
// Adds the specified Zone to the end of the Zones list, and fires a property changed notification for the Zones property
public void AddZone(Int32Rect zone)
{
Zones.Add(zone);
FirePropertyChanged("Zones");
}
private void Load(byte[] data)
{
// Initialize this CanvasLayoutModel based on the given persistence data
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
int i = 5;
_referenceWidth = data[i++] * 256 + data[i++];
_referenceHeight = data[i++] * 256 + data[i++];
int count = data[i++];
while (count-- > 0)
{
_zones.Add(new Int32Rect(
data[i++] * 256 + data[i++],
data[i++] * 256 + data[i++],
data[i++] * 256 + data[i++],
data[i++] * 256 + data[i++]));
}
}
// Clone
// Implements the LayoutModel.Clone abstract method
// Clones the data from this CanvasLayoutModel to a new CanvasLayoutModel
public override LayoutModel Clone()
{
CanvasLayoutModel layout = new CanvasLayoutModel(Name);
layout.ReferenceHeight = ReferenceHeight;
layout.ReferenceWidth = ReferenceWidth;
foreach(Int32Rect zone in Zones)
{
layout.Zones.Add(zone);
}
return layout;
}
// GetPersistData
// Implements the LayoutModel.GetPersistData abstract method
// Returns the state of this GridLayoutModel in persisted format
protected override byte[] GetPersistData()
{
byte[] data = new byte[10 + (_zones.Count * 8)];
int i = 0;
// Common persisted values between all layout types
data[i++] = (byte)(c_latestVersion / 256);
data[i++] = (byte)(c_latestVersion % 256);
data[i++] = 1; // LayoutModelType: 1 == CanvasLayoutModel
data[i++] = (byte)(Id / 256);
data[i++] = (byte)(Id % 256);
// End common
data[i++] = (byte)(_referenceWidth / 256);
data[i++] = (byte)(_referenceWidth % 256);
data[i++] = (byte)(_referenceHeight / 256);
data[i++] = (byte)(_referenceHeight % 256);
data[i++] = (byte)_zones.Count;
foreach (Int32Rect rect in _zones)
{
data[i++] = (byte)(rect.X / 256);
data[i++] = (byte)(rect.X % 256);
data[i++] = (byte)(rect.Y / 256);
data[i++] = (byte)(rect.Y % 256);
data[i++] = (byte)(rect.Width / 256);
data[i++] = (byte)(rect.Width % 256);
data[i++] = (byte)(rect.Height / 256);
data[i++] = (byte)(rect.Height % 256);
}
return data;
}
private static ushort c_latestVersion = 0;
}
}

View File

@@ -0,0 +1,224 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Documents;
namespace FancyZonesEditor.Models
{
// GridLayoutModel
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
public class GridLayoutModel : LayoutModel
{
public GridLayoutModel() : base() { }
public GridLayoutModel(string name) : base(name) { }
public GridLayoutModel(string name, ushort id) : base(name, id) { }
public GridLayoutModel(ushort version, string name, ushort id, byte[] data) : base(name, id)
{
if (version == c_latestVersion)
{
Reload(data);
}
}
// Rows - number of rows in the Grid
public int Rows
{
get { return _rows; }
set
{
if (_rows != value)
{
_rows = value;
FirePropertyChanged("Rows");
}
}
}
private int _rows = 1;
// Columns - number of columns in the Grid
public int Columns
{
get { return _cols; }
set
{
if (_cols != value)
{
_cols = value;
FirePropertyChanged("Columns");
}
}
}
private int _cols = 1;
// CellChildMap - represents which "children" belong in which grid cells;
// shows spanning children by the same index appearing in adjacent cells
// TODO: ideally no setter here - this means moving logic like "split" over to model
public int[,] CellChildMap { get { return _cellChildMap; } set { _cellChildMap = value; } }
private int[,] _cellChildMap;
// RowPercents - represents the %age height of each row in the grid
public int[] RowPercents { get { return _rowPercents; } set { _rowPercents = value; } }
private int[] _rowPercents;
// ColumnPercents - represents the %age width of each column in the grid
public int[] ColumnPercents { get { return _colPercents; } set { _colPercents = value; } }
private int[] _colPercents;
// FreeZones (not persisted) - used to keep track of child indices that are no longer in use in the CellChildMap,
// making them candidates for re-use when it's needed to add another child
// TODO: do I need FreeZones on the data model? - I think I do
public IList<int> FreeZones { get { return _freeZones; } }
private IList<int> _freeZones = new List<int>();
public void Reload(byte[] data)
{
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
int i = 5;
Rows = data[i++];
Columns = data[i++];
_rowPercents = new int[Rows];
for (int row = 0; row < Rows; row++)
{
_rowPercents[row] = data[i++]*256 + data[i++];
}
_colPercents = new int[Columns];
for (int col = 0; col < Columns; col++)
{
_colPercents[col] = data[i++]*256 + data[i++];
}
_cellChildMap = new int[Rows, Columns];
for (int row = 0; row < Rows; row++)
{
for (int col = 0; col < Columns; col++)
{
_cellChildMap[row, col] = data[i++];
}
}
}
// Clone
// Implements the LayoutModel.Clone abstract method
// Clones the data from this GridLayoutModel to a new GridLayoutModel
public override LayoutModel Clone()
{
GridLayoutModel layout = new GridLayoutModel(Name);
int rows = Rows;
int cols = Columns;
layout.Rows = rows;
layout.Columns = cols;
int[,] cellChildMap = new int[rows, cols];
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
cellChildMap[row, col] = CellChildMap[row, col];
}
}
layout.CellChildMap = cellChildMap;
int[] rowPercents = new int[rows];
for (int row = 0; row < rows; row++)
{
rowPercents[row] = RowPercents[row];
}
layout.RowPercents = rowPercents;
int[] colPercents = new int[cols];
for (int col = 0; col < cols; col++)
{
colPercents[col] = ColumnPercents[col];
}
layout.ColumnPercents = colPercents;
return layout;
}
// GetPersistData
// Implements the LayoutModel.GetPersistData abstract method
// Returns the state of this GridLayoutModel in persisted format
protected override byte[] GetPersistData()
{
int rows = Rows;
int cols = Columns;
int[,] cellChildMap;
if (_freeZones.Count == 0)
{
// no unused indices -- so we can just use the _cellChildMap as is
cellChildMap = _cellChildMap;
}
else
{
// compress cellChildMap to not have gaps for unused child indices;
List<int> mapping = new List<int>();
cellChildMap = new int[rows, cols];
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
int source = _cellChildMap[row, col];
int index = mapping.IndexOf(source);
if (index == -1)
{
index = mapping.Count;
mapping.Add(source);
}
cellChildMap[row, col] = index;
}
}
}
byte[] data = new byte[7 + (Rows * 2) + (Columns * 2) + (Rows * Columns)];
int i = 0;
// Common persisted values between all layout types
data[i++] = (byte)(c_latestVersion / 256);
data[i++] = (byte)(c_latestVersion % 256);
data[i++] = 0; // LayoutModelType: 0 == GridLayoutModel
data[i++] = (byte)(Id / 256);
data[i++] = (byte)(Id % 256);
// End common
data[i++] = (byte)Rows;
data[i++] = (byte)Columns;
for (int row = 0; row < Rows; row++)
{
int rowPercent = _rowPercents[row];
data[i++] = (byte)(rowPercent / 256);
data[i++] = (byte)(rowPercent % 256);
}
for (int col = 0; col < Columns; col++)
{
int colPercent = _colPercents[col];
data[i++] = (byte)(colPercent / 256);
data[i++] = (byte)(colPercent % 256);
}
for (int row = 0; row < Rows; row++)
{
for (int col = 0; col < Columns; col++)
{
data[i++] = (byte)cellChildMap[row, col];
}
}
return data;
}
private static ushort c_latestVersion = 0;
}
}

View File

@@ -0,0 +1,226 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
namespace FancyZonesEditor.Models
{
// Base LayoutModel
// Manages common properties and base persistence
public abstract class LayoutModel : INotifyPropertyChanged
{
protected LayoutModel() { }
protected LayoutModel(string name) : this()
{
Name = name;
}
protected LayoutModel(string name, ushort id) : this(name)
{
_id = id;
}
// Name - the display name for this layout model - is also used as the key in the registry
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
FirePropertyChanged("Name");
}
}
}
private string _name;
// Id - the unique ID for this layout model - is used to connect fancy zones' ZonesSets with the editor's Layouts
// - note: 0 means this is a new layout, which means it will have its ID auto-assigned on persist
public ushort Id
{
get
{
if (_id == 0)
{
_id = ++s_maxId;
}
return _id;
}
}
private ushort _id = 0;
// IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker
// TODO: once we switch to a picker per monitor, we need to move this state to the view
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
FirePropertyChanged("IsSelected");
}
}
}
private bool _isSelected;
// implementation of INotifyProeprtyChanged
public event PropertyChangedEventHandler PropertyChanged;
// FirePropertyChanged -- wrapper that calls INPC.PropertyChanged
protected virtual void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
// Removes this Layout from the registry and the loaded CustomModels list
public void Delete()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(c_registryPath, true);
if (key != null)
{
key.DeleteValue(Name);
}
int i = s_customModels.IndexOf(this);
if (i != -1)
{
s_customModels.RemoveAt(i);
}
}
// Loads all the Layouts persisted under the Layouts key in the registry
public static ObservableCollection<LayoutModel> LoadCustomModels()
{
s_customModels = new ObservableCollection<LayoutModel>();
RegistryKey key = Registry.CurrentUser.OpenSubKey(c_registryPath);
if (key != null)
{
foreach (string name in key.GetValueNames())
{
LayoutModel model = null;
byte[] data = (byte[])Registry.GetValue(c_fullRegistryPath, name, null);
ushort version = (ushort) (data[0]*256 + data[1]);
byte type = data[2];
ushort id = (ushort) (data[3]*256 + data[4]);
switch (type)
{
case 0: model = new GridLayoutModel(version, name, id, data); break;
case 1: model = new CanvasLayoutModel(version, name, id, data); break;
}
if (model != null)
{
if (s_maxId < id)
{
s_maxId = id;
}
s_customModels.Add(model);
}
}
}
return s_customModels;
}
private static ObservableCollection<LayoutModel> s_customModels = null;
private static ushort s_maxId = 0;
// Callbacks that the base LayoutModel makes to derived types
protected abstract byte[] GetPersistData();
public abstract LayoutModel Clone();
// PInvokes to handshake with fancyzones backend
internal static class Native
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
internal delegate int PersistZoneSet(
[MarshalAs(UnmanagedType.LPWStr)] string activeKey,
[MarshalAs(UnmanagedType.LPWStr)] string key,
ushort layoutId,
int zoneCount,
[MarshalAs(UnmanagedType.LPArray)] int[] zoneArray);
}
public void Persist(System.Windows.Int32Rect[] zones)
{
// Persist the editor data
Registry.SetValue(c_fullRegistryPath, Name, GetPersistData(), Microsoft.Win32.RegistryValueKind.Binary);
Apply(zones);
}
public void Apply(System.Windows.Int32Rect[] zones)
{
// Persist the zone data back into FZ
var module = Native.LoadLibrary("fancyzones.dll");
if (module == IntPtr.Zero)
{
return;
}
var pfn = Native.GetProcAddress(module, "PersistZoneSet");
if (pfn == IntPtr.Zero)
{
return;
}
// Scale all the zones to the DPI and then pack them up to be marshalled.
int zoneCount = zones.Length;
var zoneArray = new int[zoneCount * 4];
var graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
float dpi = graphics.DpiX / 96;
for (int i = 0; i < zones.Length; i++)
{
var left = (int)(zones[i].X * dpi);
var top = (int)(zones[i].Y * dpi);
var right = left + (int)(zones[i].Width * dpi);
var bottom = top + (int)(zones[i].Height * dpi);
var index = i * 4;
zoneArray[index] = left;
zoneArray[index+1] = top;
zoneArray[index+2] = right;
zoneArray[index+3] = bottom;
}
string[] args = Environment.GetCommandLineArgs();
if (args.Length > 1)
{
// args[1] = registry key value of currently active ZoneSet
// args[2] = id of layout to load at startup
string uniqueId = args[1];
// TODO: multimon
double height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
double width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
var key = width.ToString() + "_" + height.ToString();
var persistZoneSet = Marshal.GetDelegateForFunctionPointer<Native.PersistZoneSet>(pfn);
persistZoneSet(uniqueId, key, _id, zoneCount, zoneArray);
}
}
private static readonly string c_registryPath = Settings.RegistryPath + "\\Layouts";
private static readonly string c_fullRegistryPath = Settings.FullRegistryPath + "\\Layouts";
}
}

View File

@@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections;
using System.Collections.ObjectModel;
using FancyZonesEditor.Models;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
namespace FancyZonesEditor
{
//
// Settings
// These are the configuration settings used by the rest of the editor
// Other UIs in the editor will subscribe to change events on the properties to stay up to date as these properties change
//
public class Settings : INotifyPropertyChanged
{
public Settings()
{
Rect workArea = System.Windows.SystemParameters.WorkArea;
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
_defaultModels = new List<LayoutModel>(5);
_focusModel = new CanvasLayoutModel("Focus", c_focusModelId, (int)workArea.Width, (int)workArea.Height);
_defaultModels.Add(_focusModel);
_columnsModel = new GridLayoutModel("Columns", c_columnsModelId);
_columnsModel.Rows = 1;
_columnsModel.RowPercents = new int[1] { c_multiplier };
_defaultModels.Add(_columnsModel);
_rowsModel = new GridLayoutModel("Rows", c_rowsModelId);
_rowsModel.Columns = 1;
_rowsModel.ColumnPercents = new int[1] { c_multiplier };
_defaultModels.Add(_rowsModel);
_gridModel = new GridLayoutModel("Grid", c_gridModelId);
_defaultModels.Add(_gridModel);
_priorityGridModel = new GridLayoutModel("Priority Grid", c_priorityGridModelId);
_defaultModels.Add(_priorityGridModel);
_blankCustomModel = new CanvasLayoutModel("Create new custom", c_blankCustomModelId, (int)workArea.Width, (int)workArea.Height);
_zoneCount = (int)Registry.GetValue(FullRegistryPath, "ZoneCount", 3);
_spacing = (int)Registry.GetValue(FullRegistryPath, "Spacing", 16);
_showSpacing = (int)Registry.GetValue(FullRegistryPath, "ShowSpacing", 1) == 1;
UpdateLayoutModels();
}
// ZoneCount - number of zones selected in the picker window
public int ZoneCount
{
get { return _zoneCount; }
set
{
if (_zoneCount != value)
{
_zoneCount = value;
Registry.SetValue(FullRegistryPath, "ZoneCount", _zoneCount, RegistryValueKind.DWord);
UpdateLayoutModels();
FirePropertyChanged("ZoneCount");
}
}
}
private int _zoneCount;
// Spacing - how much space in between zones of the grid do you want
public int Spacing
{
get { return _spacing; }
set
{
if (_spacing != value)
{
_spacing = value;
Registry.SetValue(FullRegistryPath, "Spacing", _spacing, RegistryValueKind.DWord);
FirePropertyChanged("Spacing");
}
}
}
private int _spacing;
// ShowSpacing - is the Spacing value used or ignored?
public bool ShowSpacing
{
get { return _showSpacing; }
set
{
if (_showSpacing != value)
{
_showSpacing = value;
Registry.SetValue(FullRegistryPath, "ShowSpacing", _showSpacing, RegistryValueKind.DWord);
FirePropertyChanged("ShowSpacing");
}
}
}
private bool _showSpacing;
// IsShiftKeyPressed - is the shift key currently being held down
public bool IsShiftKeyPressed
{
get { return _isShiftKeyPressed; }
set
{
if (_isShiftKeyPressed != value)
{
_isShiftKeyPressed = value;
FirePropertyChanged("IsShiftKeyPressed");
}
}
}
private bool _isShiftKeyPressed;
// IsCtrlKeyPressed - is the ctrl key currently being held down
public bool IsCtrlKeyPressed
{
get { return _isCtrlKeyPressed; }
set
{
if (_isCtrlKeyPressed != value)
{
_isCtrlKeyPressed = value;
FirePropertyChanged("IsCtrlKeyPressed");
}
}
}
private bool _isCtrlKeyPressed;
// UpdateLayoutModels
// Update the five default layouts based on the new ZoneCount
private void UpdateLayoutModels()
{
int previousZoneCount = _focusModel.Zones.Count;
// Update the "Focus" Default Layout
_focusModel.Zones.Clear();
Int32Rect focusZoneRect = new Int32Rect((int)(_focusModel.ReferenceWidth * 0.1), (int)(_focusModel.ReferenceHeight * 0.1), (int)(_focusModel.ReferenceWidth * 0.6), (int)(_focusModel.ReferenceHeight * 0.6));
int focusRectXIncrement = (ZoneCount <= 1) ? 0 : (int)(_focusModel.ReferenceWidth * 0.2) / (ZoneCount - 1);
int focusRectYIncrement = (ZoneCount <= 1) ? 0 : (int)(_focusModel.ReferenceHeight * 0.2) / (ZoneCount - 1);
for (int i = 0; i < ZoneCount; i++)
{
_focusModel.Zones.Add(focusZoneRect);
focusZoneRect.X += focusRectXIncrement;
focusZoneRect.Y += focusRectYIncrement;
}
// Update the "Rows" and "Columns" Default Layouts
// They can share their model, just transposed
_rowsModel.CellChildMap = new int[ZoneCount, 1];
_columnsModel.CellChildMap = new int[1, ZoneCount];
_rowsModel.Rows = _columnsModel.Columns = ZoneCount;
_rowsModel.RowPercents = _columnsModel.ColumnPercents = new int[ZoneCount];
for (int i = 0; i < ZoneCount; i++)
{
_rowsModel.CellChildMap[i, 0] = i;
_columnsModel.CellChildMap[0, i] = i;
_rowsModel.RowPercents[i] = c_multiplier / ZoneCount; // _columnsModel is sharing the same array
}
// Update the "Grid" Default Layout
int rows = 1;
int cols = 1;
int mergeCount = 0;
while (ZoneCount / rows >= rows)
{
rows++;
}
rows--;
cols = ZoneCount / rows;
if (ZoneCount % rows == 0)
{
// even grid
}
else
{
cols++;
mergeCount = rows - (ZoneCount % rows);
}
_gridModel.Rows = rows;
_gridModel.Columns = cols;
_gridModel.RowPercents = new int[rows];
_gridModel.ColumnPercents = new int[cols];
_gridModel.CellChildMap = new int[rows, cols];
for (int row = 0; row < rows; row++)
{
_gridModel.RowPercents[row] = c_multiplier / rows;
}
for (int col = 0; col < cols; col++)
{
_gridModel.ColumnPercents[col] = c_multiplier / cols;
}
int index = 0;
for (int col = cols - 1; col >= 0; col--)
{
for (int row = rows - 1; row >= 0; row--)
{
_gridModel.CellChildMap[row, col] = index++;
if (index == ZoneCount)
{
index--;
}
}
}
// Update the "Priority Grid" Default Layout
if (ZoneCount <= s_priorityData.Length)
{
_priorityGridModel.Reload(s_priorityData[ZoneCount - 1]);
}
else
{
// same as grid;
_priorityGridModel.Rows = _gridModel.Rows;
_priorityGridModel.Columns = _gridModel.Columns;
_priorityGridModel.RowPercents = _gridModel.RowPercents;
_priorityGridModel.ColumnPercents = _gridModel.ColumnPercents;
_priorityGridModel.CellChildMap = _gridModel.CellChildMap;
}
}
public IList<LayoutModel> DefaultModels { get { return _defaultModels; } }
public ObservableCollection<LayoutModel> CustomModels
{
get
{
if (_customModels == null)
{
_customModels = LayoutModel.LoadCustomModels();
_customModels.Insert(0, _blankCustomModel);
}
return _customModels;
}
}
private ObservableCollection<LayoutModel> _customModels;
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
public static bool IsPredefinedLayout(LayoutModel model)
{
return (model.Id >= c_lastPrefinedId);
}
// implementation of INotifyProeprtyChanged
public event PropertyChangedEventHandler PropertyChanged;
// FirePropertyChanged -- wrapper that calls INPC.PropertyChanged
protected virtual void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
// storage for Default Layout Models
private IList<LayoutModel> _defaultModels;
private CanvasLayoutModel _focusModel;
private GridLayoutModel _rowsModel;
private GridLayoutModel _columnsModel;
private GridLayoutModel _gridModel;
private GridLayoutModel _priorityGridModel;
private CanvasLayoutModel _blankCustomModel;
private static readonly ushort c_focusModelId = 0xFFFF;
private static readonly ushort c_rowsModelId = 0xFFFE;
private static readonly ushort c_columnsModelId = 0xFFFD;
private static readonly ushort c_gridModelId = 0xFFFC;
private static readonly ushort c_priorityGridModelId = 0xFFFB;
private static readonly ushort c_blankCustomModelId = 0xFFFA;
private static readonly ushort c_lastPrefinedId = c_blankCustomModelId;
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
private static byte[][] s_priorityData = new byte[][]
{
new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 },
new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 },
new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 }
};
private const int c_multiplier = 10000;
}
}

View File

@@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FancyZonesEditor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft Corp.")]
[assembly: AssemblyProduct("FancyZonesEditor")]
[assembly: AssemblyCopyright("Copyright (C) 2019 Microsoft Corp.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[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)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.11.0.0")]
[assembly: AssemblyFileVersion("0.11.0.0")]

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FancyZonesEditor.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FancyZonesEditor.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FancyZonesEditor.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FancyZonesEditor
{
public class RowColInfo
{
public RowColInfo(int percent)
{
Percent = percent;
}
public RowColInfo(int index, int count)
{
Percent = (c_multiplier / count) + ((index == 0) ? (c_multiplier % count) : 0);
}
private const int c_multiplier = 10000;
public double SetExtent(double start, double totalExtent)
{
Start = start;
Extent = totalExtent * Percent / c_multiplier;
End = Start + Extent;
return Extent;
}
public RowColInfo[] Split(double offset)
{
RowColInfo[] info = new RowColInfo[2];
int newPercent = (int)(Percent * offset / Extent);
info[0] = new RowColInfo(newPercent);
info[1] = new RowColInfo(Percent - newPercent);
return info;
}
public int Percent;
public double Extent;
public double Start;
public double End;
}
}

View File

@@ -0,0 +1,52 @@
<Window x:Class="FancyZonesEditor.WindowLayout"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800"
WindowState="Maximized"
ShowInTaskbar="False"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Loaded="onLoad"
>
<Grid x:Name="Body">
<Rectangle Fill="White" Opacity="0.5"/>
<Grid x:Name="Window1" >
<Border BorderThickness="2" BorderBrush="Black">
<Grid>
<!--
<Thumb Background="Red" Height="36" VerticalAlignment="Top"/>
<Button Height="36" Width="36" VerticalAlignment="Top" Background="Red" FontFamily="Segoe UI Symbol" FontSize="16" Content="✖" HorizontalAlignment="Right"/>
-->
<Rectangle Fill="Black" Opacity="0.2"/>
<!--
<Grid Width="24" Height="24" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="White" Height="24" Width="2" Margin="-33,0,0,0">
<Rectangle.RenderTransform>
<RotateTransform Angle="-45"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Fill="White" Height="24" Width="2" Margin="0,-3,0,0" VerticalAlignment="Center" HorizontalAlignment="Center">
<Rectangle.RenderTransform>
<RotateTransform Angle="45"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
-->
</Grid>
</Border>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class WindowLayout : Window
{
public WindowLayout()
{
InitializeComponent();
}
void onLoad(object sender, RoutedEventArgs e)
{
//WindowEditor window = new WindowEditor(); window.Show();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B