diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..d9c4c72
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,26 @@
+name: Build SDUI
+
+on:
+ pull_request:
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v1
+
+ - name: Restore NuGet packages
+ uses: nuget/setup-nuget@v1
+ with:
+ nuget-version: "5.x"
+
+ - name: Restore solution
+ run: nuget restore SDUI.sln
+
+ - name: Build solution
+ run: msbuild SDUI.sln /p:Configuration=Release
diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml
deleted file mode 100644
index f767f79..0000000
--- a/.github/workflows/dotnet-desktop.yml
+++ /dev/null
@@ -1,115 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-# This workflow will build, test, sign and package a WPF or Windows Forms desktop application
-# built on .NET Core.
-# To learn how to migrate your existing application to .NET Core,
-# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework
-#
-# To configure this workflow:
-#
-# 1. Configure environment variables
-# GitHub sets default environment variables for every workflow run.
-# Replace the variables relative to your project in the "env" section below.
-#
-# 2. Signing
-# Generate a signing certificate in the Windows Application
-# Packaging Project or add an existing signing certificate to the project.
-# Next, use PowerShell to encode the .pfx file using Base64 encoding
-# by running the following Powershell script to generate the output string:
-#
-# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte
-# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt'
-#
-# Open the output file, SigningCertificate_Encoded.txt, and copy the
-# string inside. Then, add the string to the repo as a GitHub secret
-# and name it "Base64_Encoded_Pfx."
-# For more information on how to configure your signing certificate for
-# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing
-#
-# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key".
-# See "Build the Windows Application Packaging project" below to see how the secret is used.
-#
-# For more information on GitHub Actions, refer to https://github.com/features/actions
-# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications,
-# refer to https://github.com/microsoft/github-actions-for-desktop-apps
-
-name: .NET Core Desktop
-
-on:
- push:
- branches: [ "master" ]
- pull_request:
- branches: [ "master" ]
-
-jobs:
-
- build:
-
- strategy:
- matrix:
- configuration: [Debug, Release]
-
- runs-on: windows-latest # For a list of available runner types, refer to
- # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
-
- env:
- Solution_Name: your-solution-name # Replace with your solution name, i.e. MyWpfApp.sln.
- Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj.
- Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package.
- Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj.
-
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- # Install the .NET Core workload
- - name: Install .NET Core
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
-
- # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
- - name: Setup MSBuild.exe
- uses: microsoft/setup-msbuild@v1.0.2
-
- # Execute all unit tests in the solution
- - name: Execute unit tests
- run: dotnet test
-
- # Restore the application to populate the obj folder with RuntimeIdentifiers
- - name: Restore the application
- run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
- env:
- Configuration: ${{ matrix.configuration }}
-
- # Decode the base 64 encoded pfx and save the Signing_Certificate
- - name: Decode the pfx
- run: |
- $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}")
- $certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx
- [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte)
-
- # Create the app package by building and packaging the Windows Application Packaging project
- - name: Create the app package
- run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }}
- env:
- Appx_Bundle: Always
- Appx_Bundle_Platforms: x86|x64
- Appx_Package_Build_Mode: StoreUpload
- Configuration: ${{ matrix.configuration }}
-
- # Remove the pfx
- - name: Remove the pfx
- run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx
-
- # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact
- - name: Upload build artifacts
- uses: actions/upload-artifact@v3
- with:
- name: MSIX Package
- path: ${{ env.Wap_Project_Directory }}\AppPackages
diff --git a/SDUI/AnimationEngine/AnimationManager.cs b/SDUI/AnimationEngine/AnimationManager.cs
index 7ed2606..4406d20 100644
--- a/SDUI/AnimationEngine/AnimationManager.cs
+++ b/SDUI/AnimationEngine/AnimationManager.cs
@@ -1,16 +1,17 @@
using System;
using System.Windows.Forms;
using SDUI.AnimationEngine;
+using SDUI.Helpers;
namespace SDUI.Animation
{
///
- /// Modern, optimize edilmiþ animation manager - ValueProvider tabanlý
+ /// Modern, optimize edilmiş animation manager - ValueProvider tabanlı
///
public class AnimationEngine : IDisposable
{
private readonly ValueProvider _valueProvider;
- private Timer _timer; // Lazy initialization için readonly kaldýrýldý
+ private Timer _timer; // Lazy initialization için readonly kaldırıldı
private bool _isRunning;
private bool _disposed;
private System.Drawing.Point _animationSource;
@@ -35,19 +36,19 @@ public AnimationEngine(bool singular = true)
AnimationType = AnimationType.EaseInOut;
_valueProvider = new ValueProvider(0, ValueFactories.DoubleFactory, EasingMethods.DefaultEase);
-
- // Timer'ý lazy initialization ile oluþtur - handle sorununu çözer
+
+ // Timer'ı lazy initialization ile oluştur - handle sorununu çözer
// _timer = new Timer { Interval = 16 }; // BU SATIR KALDIRILDI
// _timer.Tick += OnTick; // BU SATIR KALDIRILDI
}
public bool Running => _isRunning;
-
- // Lazy initialization - Timer sadece gerektiðinde oluþturulur
+
+ // Lazy initialization - Timer sadece gerektiÄŸinde oluturulur
private void EnsureTimer()
{
if (_timer != null) return;
-
+
try
{
_timer = new Timer { Interval = 16 }; // ~60 FPS
@@ -55,8 +56,8 @@ private void EnsureTimer()
}
catch (Exception ex)
{
- System.Diagnostics.Debug.WriteLine($"AnimationManager: Timer oluþturulamadý - {ex.Message}");
- // Timer oluþturulamazsa animasyon devre dýþý býrakýlýr
+ System.Diagnostics.Debug.WriteLine($"AnimationManager: Timer oluturulamad - {ex.Message}");
+ // Timer oluşturulamazsa animasyon devre dışı bırakılır
_timer = null;
}
}
@@ -78,6 +79,15 @@ public void StartNewAnimation(AnimationDirection direction, object[] data)
public void StartNewAnimation(AnimationDirection direction, System.Drawing.Point source, object[] data)
{
+ if (!SystemAnimations.AreAnimationsEnabled)
+ {
+ var instantTarget = direction == AnimationDirection.In || direction == AnimationDirection.InOutIn ? 1.0 : 0.0;
+ SetProgress(instantTarget);
+ OnAnimationProgress?.Invoke(this);
+ OnAnimationFinished?.Invoke(this);
+ return;
+ }
+
if (_isRunning && !InterruptAnimation)
return;
@@ -91,12 +101,12 @@ public void StartNewAnimation(AnimationDirection direction, System.Drawing.Point
double duration = Math.Abs(target - _valueProvider.CurrentValue) / currentIncrement * 16; // milliseconds
_valueProvider.StartTransition(_valueProvider.CurrentValue, target, TimeSpan.FromMilliseconds(Math.Max(16, duration)));
-
+
_isRunning = true;
-
- // Timer'ý lazy initialization ile oluþtur
+
+ // Timer'ı lazy initialization ile oluştur
EnsureTimer();
-
+
if (_timer != null && !_timer.Enabled)
_timer.Start();
}
@@ -176,7 +186,7 @@ private void OnTick(object sender, EventArgs e)
_timer.Stop();
OnAnimationFinished?.Invoke(this);
}
-
+
OnAnimationProgress?.Invoke(this);
}
@@ -201,16 +211,16 @@ private void UpdateEasingMethod()
public void Dispose()
{
if (_disposed) return;
-
+
if (_timer != null)
{
_timer.Stop();
- _timer.Tick -= OnTick; // Event handler'ý kaldýr
+ _timer.Tick -= OnTick; // Event handler'ı kaldır
_timer.Dispose();
_timer = null;
}
-
+
_disposed = true;
}
}
-}
+}
\ No newline at end of file
diff --git a/SDUI/Controls/UIWindow.cs b/SDUI/Controls/UIWindow.cs
index 536b94f..8253a99 100644
--- a/SDUI/Controls/UIWindow.cs
+++ b/SDUI/Controls/UIWindow.cs
@@ -49,6 +49,16 @@ public enum TabDesingMode
///
private bool _formMoveMouseDown;
+ ///
+ /// Determines whether the user is dragging the form
+ ///
+ private bool _isDragging;
+
+ ///
+ /// The starting point of the drag
+ ///
+ private Point _dragStartPoint;
+
///
/// The position of the form when the left mouse button is pressed
///
@@ -699,6 +709,9 @@ protected override void OnMouseClick(MouseEventArgs e)
if (!ShowTitle)
return;
+ if (_isDragging)
+ return;
+
if (_inCloseBox)
{
_inCloseBox = false;
@@ -760,9 +773,6 @@ protected override void OnMouseClick(MouseEventArgs e)
if (pageRect == null)
UpdateTabRects();
- if (_formMoveMouseDown && !MousePosition.Equals(_mouseOffset))
- return;
-
for (int i = 0; i < pageRect.Count; i++)
{
if (pageRect[i].Contains(e.Location))
@@ -793,6 +803,7 @@ protected override void OnMouseDown(MouseEventArgs e)
if (e.Button == MouseButtons.Left && Movable)
{
_formMoveMouseDown = true;
+ _dragStartPoint = e.Location;
_location = Location;
_mouseOffset = MousePosition;
}
@@ -852,46 +863,25 @@ protected override void OnMouseUp(MouseEventArgs e)
IsStayAtTopBorder = false;
Cursor.Clip = new Rectangle();
_formMoveMouseDown = false;
+ _isDragging = false;
animationSource = e.Location;
}
protected override void OnMouseMove(MouseEventArgs e)
{
- if (_formMoveMouseDown && !MousePosition.Equals(_mouseOffset))
+ if (_formMoveMouseDown)
{
- if (WindowState == FormWindowState.Maximized)
- {
- int maximizedWidth = Width;
- int locationX = Left;
- ShowMaximize();
-
- float offsetXRatio = 1 - (float)Width / maximizedWidth;
- _mouseOffset.X -= (int)((_mouseOffset.X - locationX) * offsetXRatio);
- }
-
- var offsetX = _mouseOffset.X - MousePosition.X;
- var offsetY = _mouseOffset.Y - MousePosition.Y;
- var _workingArea = Screen.GetWorkingArea(this);
-
- // If the current mouse stays on the upper edge of the container, it will trigger an edge wait for MaximumBorderInterval(ms),
- // If the movement ends at this time, the window will be automatically maximized, this function is provided for multiple monitors arranged up and down
- // The advantage of setting the judgment to a specific value here is that it is difficult to trigger the stay event if the form is quickly moved across the monitor
- if (MousePosition.Y - _workingArea.Top == 0)
+ if (!_isDragging)
{
- if (!IsStayAtTopBorder)
+ var dragRect = new Rectangle(_dragStartPoint, Size.Empty);
+ dragRect.Inflate(SystemInformation.DragSize);
+ if (!dragRect.Contains(e.Location))
{
- Cursor.Clip = _workingArea;
- TopBorderStayTicks = DateTime.Now.Ticks;
- IsStayAtTopBorder = true;
- }
- else if (DateTime.Now.Ticks - TopBorderStayTicks > _stickyBorderTime)
- {
- Cursor.Clip = new Rectangle();
+ _isDragging = true;
+ PostDragForm(Handle);
}
}
-
- Location = new Point(_location.X - offsetX, _location.Y - offsetY);
}
else
{
@@ -985,6 +975,17 @@ protected override void OnMouseMove(MouseEventArgs e)
base.OnMouseMove(e);
}
+ protected override void WndProc(ref Message m)
+ {
+ base.WndProc(ref m);
+
+ if (m.Msg == NativeMethods.WM_EXITSIZEMOVE)
+ {
+ _formMoveMouseDown = false;
+ _isDragging = false;
+ }
+ }
+
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
@@ -1401,7 +1402,7 @@ private void DrawControlBoxButtons(Graphics g, Color foreColor)
{
// Cache hover colors
var closeHoverColor = Color.FromArgb(222, 179, 30, 30);
-
+
if (_inCloseBox)
{
var alpha = (int)(closeBoxHoverAnimationManager.GetProgress() * closeHoverColor.A);
diff --git a/SDUI/Controls/UIWindowBase.cs b/SDUI/Controls/UIWindowBase.cs
index 629137c..1f10e53 100644
--- a/SDUI/Controls/UIWindowBase.cs
+++ b/SDUI/Controls/UIWindowBase.cs
@@ -83,6 +83,12 @@ protected void DragForm(IntPtr handle)
SendMessage(handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
+ protected void PostDragForm(IntPtr handle)
+ {
+ ReleaseCapture();
+ PostMessage(handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
+ }
+
protected override CreateParams CreateParams
{
get
diff --git a/SDUI/Helpers/SystemAnimations.cs b/SDUI/Helpers/SystemAnimations.cs
new file mode 100644
index 0000000..7c62eac
--- /dev/null
+++ b/SDUI/Helpers/SystemAnimations.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDUI.Helpers
+{
+ public static class SystemAnimations
+ {
+ private const uint SPI_GETANIMATION = 0x0048;
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ANIMATIONINFO
+ {
+ public uint cbSize;
+ public int iMinAnimate;
+ }
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref ANIMATIONINFO pvParam, uint fWinIni);
+
+ private static readonly bool _areAnimationsEnabled = GetAreAnimationsEnabled();
+
+ public static bool AreAnimationsEnabled => _areAnimationsEnabled;
+
+ private static bool GetAreAnimationsEnabled()
+ {
+ try
+ {
+ ANIMATIONINFO ai = new ANIMATIONINFO();
+ ai.cbSize = (uint)Marshal.SizeOf(ai);
+
+ if (SystemParametersInfo(SPI_GETANIMATION, ai.cbSize, ref ai, 0))
+ {
+ return ai.iMinAnimate != 0;
+ }
+
+ return true; // Default to true if the call fails
+ }
+ catch
+ {
+ return true; // Default to true in case of an exception
+ }
+ }
+ }
+}
diff --git a/SDUI/NativeMethods.cs b/SDUI/NativeMethods.cs
index 5251b23..4214b53 100644
--- a/SDUI/NativeMethods.cs
+++ b/SDUI/NativeMethods.cs
@@ -52,6 +52,7 @@ public class NativeMethods
public const int LVM_GETGROUPINFOBYINDEX = LVM_FIRST + 153;
public const int LVM_MOVEITEMTOGROUP = LVM_FIRST + 154;
public const int WM_LBUTTONUP = 0x202;
+ public const int WM_EXITSIZEMOVE = 0x0232;
public const int LVGF_NONE = 0x0;
public const int LVGF_HEADER = 0x1;
@@ -141,7 +142,7 @@ public struct SubclassInfo
[DllImport("user32.dll")]
public static extern IntPtr GetDCEx(IntPtr hwnd, IntPtr hrgnclip, uint fdwOptions);
-
+
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetRect(Rect rect, int w, int h, int x, int y);
@@ -211,6 +212,9 @@ public struct SubclassInfo
[DllImport(user32, EntryPoint = "SendMessageW", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref Rect lParam);
+ [DllImport(user32, SetLastError = true)]
+ public static extern bool PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
+
[DllImport(user32, EntryPoint = "PostMessageW", SetLastError = true)]
public static extern int PostMessage(IntPtr hWnd, int Msg, int wParam, ref IntPtr lParam);
@@ -452,12 +456,12 @@ public static void EnableAcrylic(IWin32Window window, Color blurColor)
window.Handle,
ref data);
}
-
+
///
- /// Fills an area for glass rendering
- ///
- ///
- ///
+ /// Fills an area for glass rendering
+ ///
+ ///
+ ///
public static void FillForGlass(Graphics g, Rectangle r)
{
var rc = new Rect