Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use thumb for dragmove #2226

Merged
merged 12 commits into from
Dec 8, 2015
67 changes: 63 additions & 4 deletions MahApps.Metro/Controls/Flyout.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
Expand All @@ -15,6 +15,7 @@ namespace MahApps.Metro.Controls
/// <seealso cref="FlyoutsControl"/>
/// </summary>
[TemplatePart(Name = "PART_BackButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_WindowTitleThumb", Type = typeof(Thumb))]
[TemplatePart(Name = "PART_Header", Type = typeof(ContentPresenter))]
[TemplatePart(Name = "PART_Content", Type = typeof(ContentPresenter))]
public class Flyout : ContentControl
Expand Down Expand Up @@ -212,6 +213,13 @@ public Flyout()
this.Loaded += (sender, args) => UpdateFlyoutTheme();
}

private MetroWindow parentWindow;

private MetroWindow ParentWindow
{
get { return this.parentWindow ?? (this.parentWindow = this.TryFindParent<MetroWindow>()); }
}

private void UpdateFlyoutTheme()
{
var flyoutsControl = this.TryFindParent<FlyoutsControl>();
Expand All @@ -221,7 +229,7 @@ private void UpdateFlyoutTheme()
this.Visibility = flyoutsControl != null ? Visibility.Collapsed : Visibility.Visible;
}

var window = this.TryFindParent<MetroWindow>();
var window = this.ParentWindow;
if (window != null)
{
var windowTheme = DetectTheme(this);
Expand Down Expand Up @@ -282,7 +290,7 @@ private static Tuple<AppTheme, Accent> DetectTheme(Flyout flyout)
return null;

// first look for owner
var window = flyout.TryFindParent<MetroWindow>();
var window = flyout.ParentWindow;
var theme = window != null ? ThemeManager.DetectAppStyle(window) : null;
if (theme != null && theme.Item2 != null)
return theme;
Expand Down Expand Up @@ -460,6 +468,7 @@ static Flyout()
SplineDoubleKeyFrame fadeOutFrame;
ContentPresenter PART_Header;
ContentPresenter PART_Content;
Thumb windowTitleThumb;

public override void OnApplyTemplate()
{
Expand All @@ -471,7 +480,19 @@ public override void OnApplyTemplate()

PART_Header = (ContentPresenter)GetTemplateChild("PART_Header");
PART_Content = (ContentPresenter)GetTemplateChild("PART_Content");

this.windowTitleThumb = GetTemplateChild("PART_WindowTitleThumb") as Thumb;

if (this.windowTitleThumb != null)
{
this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;

this.windowTitleThumb.DragDelta += this.WindowTitleThumbMoveOnDragDelta;
this.windowTitleThumb.MouseDoubleClick += this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
this.windowTitleThumb.MouseRightButtonUp += this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}

hideStoryboard = (Storyboard)GetTemplateChild("HideStoryboard");
hideFrame = (SplineDoubleKeyFrame)GetTemplateChild("hideFrame");
hideFrameY = (SplineDoubleKeyFrame)GetTemplateChild("hideFrameY");
Expand All @@ -485,6 +506,44 @@ public override void OnApplyTemplate()
ApplyAnimation(Position, AnimateOpacity);
}

protected internal void CleanUp(FlyoutsControl flyoutsControl)
{
if (this.windowTitleThumb != null)
{
this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}
this.parentWindow = null;
}

private void WindowTitleThumbMoveOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
var window = this.ParentWindow;
if (window != null && this.Position != Position.Bottom)
{
MetroWindow.DoWindowTitleThumbMoveOnDragDelta(window, dragDeltaEventArgs);
}
}

private void WindowTitleThumbChangeWindowStateOnMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var window = this.ParentWindow;
if (window != null && this.Position != Position.Bottom)
{
MetroWindow.DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(window, mouseButtonEventArgs);
}
}

private void WindowTitleThumbSystemMenuOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
var window = this.ParentWindow;
if (window != null && this.Position != Position.Bottom)
{
MetroWindow.DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(window, e);
}
}

internal void ApplyAnimation(Position position, bool animateOpacity, bool resetShowFrame = true)
{
if (root == null || hideFrame == null || showFrame == null || hideFrameY == null || showFrameY == null || fadeOutFrame == null)
Expand Down
6 changes: 6 additions & 0 deletions MahApps.Metro/Controls/FlyoutsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
this.AttachHandlers((Flyout)element);
}

protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
((Flyout) element).CleanUp(this);
base.ClearContainerForItemOverride(element, item);
}

private void AttachHandlers(Flyout flyout)
{
var isOpenNotifier = new PropertyChangeNotifier(flyout, Flyout.IsOpenProperty);
Expand Down
129 changes: 74 additions & 55 deletions MahApps.Metro/Controls/MetroWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using MahApps.Metro.Native;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Controls.Primitives;

namespace MahApps.Metro.Controls
{
Expand All @@ -19,6 +20,7 @@ namespace MahApps.Metro.Controls
[TemplatePart(Name = PART_Icon, Type = typeof(UIElement))]
[TemplatePart(Name = PART_TitleBar, Type = typeof(UIElement))]
[TemplatePart(Name = PART_WindowTitleBackground, Type = typeof(UIElement))]
[TemplatePart(Name = PART_WindowTitleThumb, Type = typeof(Thumb))]
[TemplatePart(Name = PART_LeftWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_RightWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_WindowButtonCommands, Type = typeof(WindowButtonCommands))]
Expand All @@ -31,6 +33,7 @@ public class MetroWindow : Window
private const string PART_Icon = "PART_Icon";
private const string PART_TitleBar = "PART_TitleBar";
private const string PART_WindowTitleBackground = "PART_WindowTitleBackground";
private const string PART_WindowTitleThumb = "PART_WindowTitleThumb";
private const string PART_LeftWindowCommands = "PART_LeftWindowCommands";
private const string PART_RightWindowCommands = "PART_RightWindowCommands";
private const string PART_WindowButtonCommands = "PART_WindowButtonCommands";
Expand Down Expand Up @@ -99,6 +102,7 @@ public class MetroWindow : Window
UIElement icon;
UIElement titleBar;
UIElement titleBarBackground;
Thumb windowTitleThumb;
internal ContentPresenter LeftWindowCommandsPresenter;
internal ContentPresenter RightWindowCommandsPresenter;
internal WindowButtonCommands WindowButtonCommands;
Expand Down Expand Up @@ -890,29 +894,24 @@ public override void OnApplyTemplate()
icon = GetTemplateChild(PART_Icon) as UIElement;
titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
titleBarBackground = GetTemplateChild(PART_WindowTitleBackground) as UIElement;
this.windowTitleThumb = GetTemplateChild(PART_WindowTitleThumb) as Thumb;

this.SetVisibiltyForAllTitleElements(this.TitlebarHeight > 0);
}

private void ClearWindowEvents()
{
// clear all event handlers first:
if (titleBarBackground != null)
if (this.windowTitleThumb != null)
{
titleBarBackground.MouseDown -= TitleBarMouseDown;
titleBarBackground.MouseUp -= TitleBarMouseUp;
}
if (titleBar != null)
{
titleBar.MouseDown -= TitleBarMouseDown;
titleBar.MouseUp -= TitleBarMouseUp;
this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}
if (icon != null)
{
icon.MouseDown -= IconMouseDown;
}
MouseDown -= TitleBarMouseDown;
MouseUp -= TitleBarMouseUp;
SizeChanged -= MetroWindow_SizeChanged;
}

Expand All @@ -927,30 +926,17 @@ private void SetWindowEvents()
icon.MouseDown += IconMouseDown;
}

// handle mouse events for PART_WindowTitleBackground -> MetroWindow
if (titleBarBackground != null && titleBarBackground.Visibility == Visibility.Visible)
if (this.windowTitleThumb != null)
{
titleBarBackground.MouseDown += TitleBarMouseDown;
titleBarBackground.MouseUp += TitleBarMouseUp;
this.windowTitleThumb.DragDelta += this.WindowTitleThumbMoveOnDragDelta;
this.windowTitleThumb.MouseDoubleClick += this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
this.windowTitleThumb.MouseRightButtonUp += this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}

// handle mouse events for PART_TitleBar -> MetroWindow
if (titleBar != null && titleBar.Visibility == Visibility.Visible)
{
titleBar.MouseDown += TitleBarMouseDown;
titleBar.MouseUp += TitleBarMouseUp;

// special title resizing for centered title
if (titleBar.GetType() == typeof(Grid))
{
SizeChanged += MetroWindow_SizeChanged;
}
}
else
// handle size if we have a Grid for the title (e.g. clean window have a centered title)
if (titleBar != null && titleBar.GetType() == typeof(Grid))
{
// handle mouse events for windows without PART_WindowTitleBackground or PART_TitleBar
MouseDown += TitleBarMouseDown;
MouseUp += TitleBarMouseUp;
SizeChanged += MetroWindow_SizeChanged;
}
}

Expand All @@ -969,49 +955,82 @@ private void IconMouseDown(object sender, MouseButtonEventArgs e)
}
}

protected void TitleBarMouseDown(object sender, MouseButtonEventArgs e)
private void WindowTitleThumbMoveOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
DoWindowTitleThumbMoveOnDragDelta(this, dragDeltaEventArgs);
}

private void WindowTitleThumbChangeWindowStateOnMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(this, mouseButtonEventArgs);
}

private void WindowTitleThumbSystemMenuOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
// if UseNoneWindowStyle = true no movement, no maximize please
if (e.ChangedButton == MouseButton.Left && !this.UseNoneWindowStyle)
DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(this, e);
}

internal static void DoWindowTitleThumbMoveOnDragDelta(MetroWindow window, DragDeltaEventArgs dragDeltaEventArgs)
{
// drag only if IsWindowDraggable is set to true
if (!window.IsWindowDraggable ||
(!(Math.Abs(dragDeltaEventArgs.HorizontalChange) > 2) &&
!(Math.Abs(dragDeltaEventArgs.VerticalChange) > 2))) return;

var windowHandle = new WindowInteropHelper(window).Handle;
var cursorPos = Standard.NativeMethods.GetCursorPos();

// if the window is maximized dragging is only allowed on title bar (also if not visible)
var windowIsMaximized = window.WindowState == WindowState.Maximized;
var isMouseOnTitlebar = cursorPos.y <= window.TitlebarHeight && window.TitlebarHeight > 0;
if (!isMouseOnTitlebar && windowIsMaximized)
{
var mPoint = Mouse.GetPosition(this);
return;
}

if (IsWindowDraggable)
{
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
UnsafeNativeMethods.ReleaseCapture();
var wpfPoint = this.PointToScreen(mPoint);
var x = Convert.ToInt16(wpfPoint.X);
var y = Convert.ToInt16(wpfPoint.Y);
var lParam = (int) (uint) x | (y << 16);
UnsafeNativeMethods.SendMessage(windowHandle, Constants.WM_NCLBUTTONDOWN, Constants.HT_CAPTION, lParam);
}
if (windowIsMaximized)
{
window.Top = 2;
window.Left = Math.Max(cursorPos.x - window.RestoreBounds.Width / 2, 0);
}
var lParam = (int)(uint)cursorPos.x | (cursorPos.y << 16);
Standard.NativeMethods.SendMessage(windowHandle, Standard.WM.LBUTTONUP, (IntPtr)Standard.HT.CAPTION, (IntPtr)lParam);
Standard.NativeMethods.SendMessage(windowHandle, Standard.WM.SYSCOMMAND, (IntPtr)Standard.SC.MOUSEMOVE, IntPtr.Zero);
}

var canResize = this.ResizeMode == ResizeMode.CanResizeWithGrip || this.ResizeMode == ResizeMode.CanResize;
internal static void DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(MetroWindow window, MouseButtonEventArgs mouseButtonEventArgs)
{
// restore/maximize only with left button
if (mouseButtonEventArgs.ChangedButton == MouseButton.Left)
{
// we can maximize or restore the window if the title bar height is set (also if title bar is hidden)
var isMouseOnTitlebar = mPoint.Y <= this.TitlebarHeight && this.TitlebarHeight > 0;
if (e.ClickCount == 2 && canResize && isMouseOnTitlebar)
var canResize = window.ResizeMode == ResizeMode.CanResizeWithGrip || window.ResizeMode == ResizeMode.CanResize;
var mousePos = Mouse.GetPosition(window);
var isMouseOnTitlebar = mousePos.Y <= window.TitlebarHeight && window.TitlebarHeight > 0;
if (canResize && isMouseOnTitlebar)
{
if (this.WindowState == WindowState.Maximized)
if (window.WindowState == WindowState.Maximized)
{
Microsoft.Windows.Shell.SystemCommands.RestoreWindow(this);
Microsoft.Windows.Shell.SystemCommands.RestoreWindow(window);
}
else
{
Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(this);
Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(window);
}
mouseButtonEventArgs.Handled = true;
}
}
}

protected void TitleBarMouseUp(object sender, MouseButtonEventArgs e)
internal static void DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(MetroWindow window, MouseButtonEventArgs e)
{
if (ShowSystemMenuOnRightClick)
if (window.ShowSystemMenuOnRightClick)
{
var mousePosition = e.GetPosition(this);
if (e.ChangedButton == MouseButton.Right && (UseNoneWindowStyle || mousePosition.Y <= TitlebarHeight))
// show menu only if mouse pos is on title bar or if we have a window with none style and no title bar
var mousePos = e.GetPosition(window);
if ((mousePos.Y <= window.TitlebarHeight && window.TitlebarHeight > 0) || (window.UseNoneWindowStyle && window.TitlebarHeight <= 0))
{
ShowSystemMenuPhysicalCoordinates(this, PointToScreen(mousePosition));
ShowSystemMenuPhysicalCoordinates(window, window.PointToScreen(mousePos));
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions MahApps.Metro/MahApps.Metro.NET45.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Thumb.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Themes\Tile.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down
4 changes: 4 additions & 0 deletions MahApps.Metro/MahApps.Metro.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Themes\Thumb.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Tile.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,7 @@ internal enum SC
{
SIZE = 0xF000,
MOVE = 0xF010,
MOUSEMOVE = 0xF012,
MINIMIZE = 0xF020,
MAXIMIZE = 0xF030,
NEXTWINDOW = 0xF040,
Expand Down
5 changes: 5 additions & 0 deletions MahApps.Metro/Themes/Flyout.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Themes/Thumb.xaml" />
</ResourceDictionary.MergedDictionaries>

<DataTemplate x:Key="HeaderTemplate"
Expand Down Expand Up @@ -231,6 +232,10 @@
DockPanel.Dock="Bottom" />
</DockPanel>
</AdornerDecorator>
<Thumb Style="{StaticResource WindowTitleThumbStyle}"
Height="{Binding Path=TitlebarHeight, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:MetroWindow}}}"
VerticalAlignment="Top"
x:Name="PART_WindowTitleThumb" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Position, RelativeSource={RelativeSource Self}}"
Expand Down
Loading