diff --git a/AGENTS.md b/AGENTS.md
index 0756b27..9407133 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,3 +8,4 @@
4. Relevante Änderungen sind immer tageweise in `Changelog.md` zu dokumentieren.
5. Korrekturschleifen oder reine Nachbesserungen dürfen nicht als eigene Changelog-Einträge auftauchen.
6. `Releasenotes.md` ist vor Veröffentlichung oder auf explizite Anforderung aus dem `Changelog.md` zu synchronisieren.
+7. Für Pushes ist standardmäßig `git pushr` (Retry/Backoff) zu verwenden; falls nicht verfügbar, ist `git push` mit manuellen Wiederholungen durchzuführen.
diff --git a/Changelog.md b/Changelog.md
index b97d51f..1285780 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,7 +5,13 @@
- Komplettes visuelles Redesign der Hauptoberfläche mit modernerem Layout und konsistenter Typografie.
- Navigation, Content und Header farblich auf dynamische Konfigurationsfarben umgestellt.
- Einheitliche Button-Optik für Primary-Actions, Top-Bar-Actions und dialogbezogene Aktionen.
-- Außenrahmen der Haupt-GUI als durchgehender Outline-Rahmen umgesetzt, damit die Kontur auch über die Rundungen konsistent sichtbar bleibt.
+- Außenrahmen der Haupt-GUI als durchgehende Border auf der Hauptfläche umgesetzt, damit die Kontur über alle Rundungen konsistent bleibt.
+- Eckradius von WindowChrome, Hauptfläche und innerem Inhalts-Clip abgestimmt, damit Inhalte in allen vier Ecken innerhalb des Rahmens bleiben.
+- Hauptlayout von festen `Window.ActualWidth`-Bindings entkoppelt, damit Header/Content nicht über die gerundete Innenfläche hinausragen.
+- Feste Hauptlayout-Höhen (Navigation/Content) auf die verfügbare Innenhöhe abgestimmt und Clipping im Dock-Bereich aktiviert, damit keine Inhalte in die Rundungsbereiche überlaufen.
+- Initialisierung des Rounded-Clips auf den finalen Layout-Zeitpunkt erweitert (Loaded/Render), damit die Rundungsbegrenzung stabil greift.
+- MainWindow-Ecküberstände beseitigt: Navigation-Rail unten links an den Fensterradius angepasst, Content-Bereich im `DockPanel` als Fill-Element geführt, zusätzlicher Surface-Clip auf Radius `20` gesetzt, den gesamten `MainWindowContentRoot` als konstanten `1px`-Inset innerhalb des Rahmens geführt, den Body-Host per Geometrie mit zum Innenradius passenden gerundeten unteren Ecken begrenzt, eine On-Top-Corner-Maske (`Rectangle - RoundedRectangle`) ergänzt, eine explizite On-Top-Rahmenkontur als `Path` ergänzt, die linke Rail-Kante ohne eigene Außenlinie geführt (kein Doppelrahmen links) und zusätzlich per Win32-Window-Region (`SetWindowRgn`) die Fensterkontur selbst auf Rundung begrenzt (WPF-Typen für Geometriepunkte/-größen explizit qualifiziert, um Mehrdeutigkeiten mit `System.Drawing` zu vermeiden).
+- Sichtbarkeit des Main-Contents wiederhergestellt: Navigations-/Content-Host auf feste Breitenaufteilung (`75 + 425`) umgestellt, damit das Inhaltspanel nicht mehr durch einen Zero-Width-Viewport abgeschnitten wird.
### Navigation und Interaktion
- Navigation-Buttons neu ausgerichtet (horizontal/vertikal), Icons vergrößert und Zustände vereinheitlicht.
@@ -49,6 +55,7 @@
- Mehrdeutigkeitsfehler (`Brushes`) behoben.
- Vor erfolgreichem Konfig-Ladevorgang wird die Oberfläche analog Offline-Zustand eingeschränkt (nur Info-Navigation und Info-Panel sichtbar).
- Header-Verbindungsindikator für Offline/Verbindungsaufbau als größerer farbiger Status-Badge (rot/orange) sichtbarer umgesetzt, mit Tooltip versehen und so positioniert, dass er nicht von Action-Buttons überdeckt wird.
+- AGENTS-Prozessregel ergänzt: Pushes standardmäßig über `git pushr` mit Retry/Backoff ausführen (Fallback: manuelle Wiederholungen mit `git push`).
### Lokalisierung
- DE/EN-Ressourcen sprachlich bereinigt und vereinheitlicht.
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 1533a7f..bbca6a1 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -12,7 +12,7 @@
WindowStyle="None"
AllowsTransparency="False"
ResizeMode="NoResize"
- Background="Transparent"
+ Background="{DynamicResource backgroundColor}"
Loaded="Window_Loaded"
Icon="Resources/icons/logo_CustomerPanel.ico"
KeyDown="App_KeyDown"
@@ -346,27 +346,28 @@
+ Background="Transparent">
-
-
+
+ HorizontalAlignment="Stretch">
-
-
+
-
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index 7e5a062..7e41f2e 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -10,12 +10,14 @@ using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Input;
+using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shell;
@@ -74,8 +76,18 @@ namespace C4IT_CustomerPanel
private TimeSpan _lastAdHocInterval;
private TimeSpan _lastRegularInterval;
private bool _allowApplicationShutdown = false;
+ private bool _mainSurfaceClipInitialized = false;
internal DateTime lastUpdate;
+ [DllImport("gdi32.dll")]
+ private static extern IntPtr CreateRoundRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nWidthEllipse, int nHeightEllipse);
+
+ [DllImport("user32.dll")]
+ private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
+
+ [DllImport("gdi32.dll")]
+ private static extern bool DeleteObject(IntPtr hObject);
+
//private Dictionary> announcementIDCollection = new Dictionary>();
public bool _userIsAuthenticated = false;
public enumOnlineState OnlineState = enumOnlineState.Initializing;
@@ -144,6 +156,7 @@ namespace C4IT_CustomerPanel
FormHelper.GetLocation((int)this.Width, (int)this.Height, (double)ConfigSettings.local_currentDPI / 96);
SetAppearance(true);
+ UpdateMainSurfaceClip();
SetLocation();
CustomerPanelSecurePassword.Init();
@@ -231,6 +244,16 @@ namespace C4IT_CustomerPanel
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ComputerInfoCtrl.FillMainInfoGrid();
+ UpdateMainSurfaceClip();
+ ApplyRoundedWindowRegion();
+
+ if (!_mainSurfaceClipInitialized)
+ {
+ Dispatcher.BeginInvoke(new Action(UpdateMainSurfaceClip), DispatcherPriority.Loaded);
+ Dispatcher.BeginInvoke(new Action(UpdateMainSurfaceClip), DispatcherPriority.Render);
+ Dispatcher.BeginInvoke(new Action(ApplyRoundedWindowRegion), DispatcherPriority.Loaded);
+ Dispatcher.BeginInvoke(new Action(ApplyRoundedWindowRegion), DispatcherPriority.Render);
+ }
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(AddressChangedCallback);
}
@@ -2157,9 +2180,139 @@ namespace C4IT_CustomerPanel
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
+ UpdateMainSurfaceClip();
+ ApplyRoundedWindowRegion();
SetLocation();
}
+ private void UpdateMainSurfaceClip()
+ {
+ if (MainWindowSurface == null || MainWindowContentRoot == null || MainGrid == null)
+ return;
+
+ double surfaceWidth = MainWindowSurface.ActualWidth;
+ double surfaceHeight = MainWindowSurface.ActualHeight;
+ double contentWidth = MainWindowContentRoot.ActualWidth;
+ double contentHeight = MainWindowContentRoot.ActualHeight;
+ if (surfaceWidth <= 0 || surfaceHeight <= 0 || contentWidth <= 0 || contentHeight <= 0)
+ return;
+
+ const double surfaceRadius = 20d;
+ const double contentRadius = 19d;
+
+ MainGrid.Clip = null;
+ this.Clip = null;
+
+ MainWindowSurface.Clip = new RectangleGeometry(new Rect(0d, 0d, surfaceWidth, surfaceHeight), surfaceRadius, surfaceRadius);
+ MainWindowContentRoot.Clip = new RectangleGeometry(new Rect(0d, 0d, contentWidth, contentHeight), contentRadius, contentRadius);
+
+ if (MainWindowCornerMaskOverlay != null)
+ {
+ MainWindowCornerMaskOverlay.Data = CreateCornerOverflowMaskGeometry(surfaceWidth, surfaceHeight, surfaceRadius);
+ }
+
+ if (MainWindowFrameOverlay != null)
+ {
+ MainWindowFrameOverlay.Data = CreateFrameOverlayGeometry(surfaceWidth, surfaceHeight, surfaceRadius, 1d);
+ }
+
+ if (btnSP != null && btnSP.ActualWidth > 0d && btnSP.ActualHeight > 0d)
+ {
+ btnSP.Clip = CreateBottomRoundedRectGeometry(btnSP.ActualWidth, btnSP.ActualHeight, contentRadius);
+ }
+
+ _mainSurfaceClipInitialized = true;
+ }
+
+ private static Geometry CreateBottomRoundedRectGeometry(double width, double height, double radius)
+ {
+ if (width <= 0d || height <= 0d)
+ return Geometry.Empty;
+
+ double r = Math.Max(0d, Math.Min(radius, Math.Min(width / 2d, height / 2d)));
+ if (r <= 0d)
+ return new RectangleGeometry(new Rect(0d, 0d, width, height));
+
+ var geometry = new StreamGeometry();
+ using (var context = geometry.Open())
+ {
+ context.BeginFigure(new System.Windows.Point(0d, 0d), true, true);
+ context.LineTo(new System.Windows.Point(width, 0d), true, false);
+ context.LineTo(new System.Windows.Point(width, height - r), true, false);
+ context.ArcTo(new System.Windows.Point(width - r, height), new System.Windows.Size(r, r), 0d, false, SweepDirection.Clockwise, true, false);
+ context.LineTo(new System.Windows.Point(r, height), true, false);
+ context.ArcTo(new System.Windows.Point(0d, height - r), new System.Windows.Size(r, r), 0d, false, SweepDirection.Clockwise, true, false);
+ context.LineTo(new System.Windows.Point(0d, 0d), true, false);
+ }
+
+ geometry.Freeze();
+ return geometry;
+ }
+
+ private static Geometry CreateCornerOverflowMaskGeometry(double width, double height, double radius)
+ {
+ if (width <= 0d || height <= 0d)
+ return Geometry.Empty;
+
+ double r = Math.Max(0d, Math.Min(radius, Math.Min(width / 2d, height / 2d)));
+ if (r <= 0d)
+ return Geometry.Empty;
+
+ var fullRect = new RectangleGeometry(new Rect(0d, 0d, width, height));
+ var roundedRect = new RectangleGeometry(new Rect(0d, 0d, width, height), r, r);
+ var mask = new CombinedGeometry(GeometryCombineMode.Exclude, fullRect, roundedRect);
+ mask.Freeze();
+ return mask;
+ }
+
+ private static Geometry CreateFrameOverlayGeometry(double width, double height, double radius, double strokeThickness)
+ {
+ if (width <= 0d || height <= 0d)
+ return Geometry.Empty;
+
+ double inset = Math.Max(0d, strokeThickness / 2d);
+ double frameWidth = Math.Max(0d, width - strokeThickness);
+ double frameHeight = Math.Max(0d, height - strokeThickness);
+ if (frameWidth <= 0d || frameHeight <= 0d)
+ return Geometry.Empty;
+
+ double frameRadius = Math.Max(0d, Math.Min(radius - inset, Math.Min(frameWidth / 2d, frameHeight / 2d)));
+ var frame = new RectangleGeometry(new Rect(inset, inset, frameWidth, frameHeight), frameRadius, frameRadius);
+ frame.Freeze();
+ return frame;
+ }
+
+ private void ApplyRoundedWindowRegion()
+ {
+ if (!IsLoaded || WindowState == WindowState.Minimized)
+ return;
+
+ IntPtr hwnd = new WindowInteropHelper(this).Handle;
+ if (hwnd == IntPtr.Zero)
+ return;
+
+ PresentationSource source = PresentationSource.FromVisual(this);
+ if (source?.CompositionTarget == null)
+ return;
+
+ Matrix toDevice = source.CompositionTarget.TransformToDevice;
+ int pxWidth = Math.Max(1, (int)Math.Ceiling(ActualWidth * toDevice.M11));
+ int pxHeight = Math.Max(1, (int)Math.Ceiling(ActualHeight * toDevice.M22));
+
+ const double radiusDip = 20d;
+ int ellipseWidth = Math.Max(2, (int)Math.Ceiling(radiusDip * 2d * toDevice.M11));
+ int ellipseHeight = Math.Max(2, (int)Math.Ceiling(radiusDip * 2d * toDevice.M22));
+
+ IntPtr hRgn = CreateRoundRectRgn(0, 0, pxWidth + 1, pxHeight + 1, ellipseWidth, ellipseHeight);
+ if (hRgn == IntPtr.Zero)
+ return;
+
+ if (SetWindowRgn(hwnd, hRgn, true) == 0)
+ {
+ DeleteObject(hRgn);
+ }
+ }
+
}
public class cMainFunctionInfo