Editor/XGUIView.Resizing.cs
using Editor;
using Sandbox;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Linq;
namespace XGUI
{
public partial class XGUIView
{
private void ResizeMousePress( MouseEvent e )
{
if ( e.Button == MouseButtons.Left && SelectedPanel != null )
{
// Check if we clicked on a resize handle
int handleIndex = GetHandleAtPosition( e.LocalPosition );
if ( handleIndex >= 0 )
{
_isDraggingHandle = true;
_activeHandle = handleIndex;
_dragStartPos = e.LocalPosition - WindowContent.Box.Rect.Position;
_originalRect = SelectedPanel.Box.Rect;
_originalRect.Position -= WindowContent.Box.Rect.Position; // Adjust for WindowContent position
e.Accepted = true;
}
}
}
private void ResizeMouseMove( MouseEvent e )
{
if ( _isDraggingHandle && SelectedPanel != null )
{
var pos = e.LocalPosition;
// Calculate how much we've moved
var delta = pos - _dragStartPos;
// Apply snapping during resize
delta = ApplyResizeSnapping( delta );
delta -= SelectedPanel.Parent.Box.Rect.Position; // Adjust for WindowContent position
// Apply the resize based on which handle is being dragged
if ( ShouldOnlyHorizontalResize( SelectedPanel ) )
{
ApplyResize( delta, true, verticalenabled: false, diagonalenabled: false );
}
else
{
ApplyResize( delta, true );
}
e.Accepted = true;
}
}
private void ResizeMouseReleased( MouseEvent e )
{
if ( _isDraggingHandle && SelectedPanel != null )
{
_isDraggingHandle = false;
_activeHandle = -1;
e.Accepted = true;
}
}
/// <summary>
/// Elements like buttons and textboxes should only resize horizontally
/// </summary>
/// <param name="position"></param>
/// <returns></returns>
private bool ShouldOnlyHorizontalResize( Panel panel )
{
if ( panel == null ) return false;
// Check if the panel is a button or textbox
if ( panel is Sandbox.UI.Button || panel is Sandbox.UI.TextEntry )
{
return true;
}
// Check if the panel has a specific class that indicates horizontal resizing
if ( panel.Class.Any( x => x == "horizontal-resize-only" ) )
{
return true;
}
return false;
}
private void DrawResizeHandles( Rect rect, bool verticalenabled = true, bool horizontalenabled = true, bool diagonalenabled = true )
{
float handleSize = 3;
Paint.SetBrush( Color.White );
Paint.SetPen( Color.Black, 1.0f );
// Draw the 8 resize handles (clockwise from top-left)
Rect[] handleRects = new Rect[8];
// Top-left
if ( diagonalenabled ) handleRects[0] = new Rect( rect.Left - handleSize, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Top-middle
if ( verticalenabled ) handleRects[1] = new Rect( rect.Left + (rect.Width - handleSize) / 2, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Top-right
if ( diagonalenabled ) handleRects[2] = new Rect( rect.Right - handleSize, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Middle-right
if ( horizontalenabled ) handleRects[3] = new Rect( rect.Right - handleSize, rect.Top + (rect.Height - handleSize) / 2, handleSize * 2, handleSize * 2 );
// Bottom-right
if ( diagonalenabled ) handleRects[4] = new Rect( rect.Right - handleSize, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Bottom-middle
if ( verticalenabled ) handleRects[5] = new Rect( rect.Left + (rect.Width - handleSize) / 2, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Bottom-left
if ( diagonalenabled ) handleRects[6] = new Rect( rect.Left - handleSize, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Middle-left
if ( horizontalenabled ) handleRects[7] = new Rect( rect.Left - handleSize, rect.Top + (rect.Height - handleSize) / 2, handleSize * 2, handleSize * 2 );
// Draw all handles
for ( int i = 0; i < handleRects.Length; i++ )
{
Paint.DrawRect( handleRects[i] );
}
}
private int GetHandleAtPosition( Vector2 position )
{
if ( SelectedPanel == null ) return -1;
var rect = SelectedPanel.Box.Rect;
float handleSize = 3;
// Check each handle in order
Rect[] handleRects = new Rect[8];
// Top-left
handleRects[0] = new Rect( rect.Left - handleSize, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Top-middle
handleRects[1] = new Rect( rect.Left + (rect.Width - handleSize) / 2, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Top-right
handleRects[2] = new Rect( rect.Right - handleSize, rect.Top - handleSize, handleSize * 2, handleSize * 2 );
// Middle-right
handleRects[3] = new Rect( rect.Right - handleSize, rect.Top + (rect.Height - handleSize) / 2, handleSize * 2, handleSize * 2 );
// Bottom-right
handleRects[4] = new Rect( rect.Right - handleSize, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Bottom-middle
handleRects[5] = new Rect( rect.Left + (rect.Width - handleSize) / 2, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Bottom-left
handleRects[6] = new Rect( rect.Left - handleSize, rect.Bottom - handleSize, handleSize * 2, handleSize * 2 );
// Middle-left
handleRects[7] = new Rect( rect.Left - handleSize, rect.Top + (rect.Height - handleSize) / 2, handleSize * 2, handleSize * 2 );
// Check if position is inside any handle
for ( int i = 0; i < handleRects.Length; i++ )
{
if ( handleRects[i].IsInside( position ) )
return i;
}
return -1;
}
private void UpdateResizeCursor( int handleIndex, bool verticalenabled = true, bool horizontalenabled = true, bool diagonalenabled = true )
{
switch ( handleIndex )
{
case 0: // Top-left
case 4: // Bottom-right
if ( diagonalenabled )
Cursor = CursorShape.SizeFDiag;
break;
case 2: // Top-right
case 6: // Bottom-left
if ( diagonalenabled )
Cursor = CursorShape.SizeBDiag;
break;
case 1: // Top-middle
case 5: // Bottom-middle
if ( verticalenabled )
Cursor = CursorShape.SizeV;
break;
case 3: // Middle-right
case 7: // Middle-left
if ( horizontalenabled )
Cursor = CursorShape.SizeH;
break;
}
}
private void ApplyResize( Vector2 delta, bool finalResize = false, bool verticalenabled = true, bool horizontalenabled = true, bool diagonalenabled = true )
{
if ( SelectedPanel == null || _activeHandle < 0 )
return;
// Calculate new rectangle based on delta
var oldRect = _originalRect;
var newRect = oldRect;
// Get the panel's alignment settings
var alignment = GetPanelAlignment( SelectedPanel );
var node = OwnerDesigner.LookupNodeByPanel( SelectedPanel );
// Determine which edges we're adjusting
bool isLeftEdge = (_activeHandle == 0 || _activeHandle == 7 || _activeHandle == 6) && horizontalenabled;
bool isRightEdge = (_activeHandle == 2 || _activeHandle == 3 || _activeHandle == 4) && horizontalenabled;
bool isTopEdge = (_activeHandle == 0 || _activeHandle == 1 || _activeHandle == 2) && verticalenabled;
bool isBottomEdge = (_activeHandle == 4 || _activeHandle == 5 || _activeHandle == 6) && verticalenabled;
// Parent dimensions for calculations
float parentLeft = (SelectedPanel.Parent?.Box.Rect.Left ?? 0);
float parentTop = (SelectedPanel.Parent?.Box.Rect.Top ?? 0);
float parentWidth = (SelectedPanel.Parent?.Box.Rect.Width ?? 400);
float parentHeight = (SelectedPanel.Parent?.Box.Rect.Height ?? 300);
// Apply changes based on which handle is being dragged
if ( isLeftEdge )
{
float newLeft = oldRect.Left + delta.x;
newRect.Left = newLeft;
newRect.Width = Math.Max( oldRect.Width - delta.x, 5 );
}
else if ( isRightEdge )
{
newRect.Width = Math.Max( oldRect.Width + delta.x, 5 );
}
if ( isTopEdge )
{
float newTop = oldRect.Top + delta.y;
newRect.Top = newTop;
newRect.Height = Math.Max( oldRect.Height - delta.y, 5 );
}
else if ( isBottomEdge )
{
newRect.Height = Math.Max( oldRect.Height + delta.y, 5 );
}
// Check if SelectedPanel is a Window
bool isWindow = SelectedPanel is Window;
// Apply visual preview - for Windows use width/height attributes, for others use style
if ( isWindow )
{
// For Window panels, update width/height attributes directly
SelectedPanel.SetAttribute( "width", $"{newRect.Width}" );
SelectedPanel.SetAttribute( "height", $"{newRect.Height}" );
if ( isLeftEdge && alignment.Left )
SelectedPanel.SetAttribute( "x", $"{newRect.Left - parentLeft}" );
if ( isTopEdge && alignment.Top )
SelectedPanel.SetAttribute( "y", $"{newRect.Top - parentTop}" );
}
else
{
// For non-Window panels, update the style as before
SelectedPanel.Style.Width = newRect.Width;
SelectedPanel.Style.Height = newRect.Height;
if ( isLeftEdge && alignment.Left )
SelectedPanel.Style.Left = newRect.Left - parentLeft;
if ( isTopEdge && alignment.Top )
SelectedPanel.Style.Top = newRect.Top - parentTop;
}
// If this is the final resize, update the node's style properties
if ( finalResize && node != null )
{
// Get current styles to check anchoring state
Dictionary<string, string> currentStyles = new Dictionary<string, string>();
if ( node.Attributes.TryGetValue( "style", out var styleString ) )
{
foreach ( var part in styleString.Split( ';', StringSplitOptions.RemoveEmptyEntries ) )
{
var kv = part.Split( ':', 2 );
if ( kv.Length == 2 )
currentStyles[kv[0].Trim()] = kv[1].Trim();
}
}
// Check if we have both horizontal anchors
bool hasHorizontalStretch = alignment.Left && alignment.Right;
bool hasVerticalStretch = alignment.Top && alignment.Bottom;
if ( isWindow )
{
// For Window panels, update width/height attributes in the markup node
if ( !hasHorizontalStretch && (isLeftEdge || isRightEdge) )
{
node.Attributes["width"] = $"{newRect.Width}";
}
if ( !hasVerticalStretch && (isTopEdge || isBottomEdge) )
{
node.Attributes["height"] = $"{newRect.Height}";
}
// Update position attributes if needed
if ( isLeftEdge && alignment.Left )
{
node.Attributes["x"] = $"{newRect.Left - parentLeft}";
}
if ( isTopEdge && alignment.Top )
{
node.Attributes["y"] = $"{newRect.Top - parentTop}";
}
}
else
{
// Only update width if we're not stretching horizontally
if ( !hasHorizontalStretch && (isLeftEdge || isRightEdge) )
{
node.TryModifyStyle( "width", $"{newRect.Width}px" );
}
// Only update height if we're not stretching vertically AND the resize operation involved vertical changes
if ( !hasVerticalStretch && (isTopEdge || isBottomEdge) )
{
node.TryModifyStyle( "height", $"{newRect.Height}px" );
}
// Update the position properties based on which handle was dragged and alignment
if ( SelectedPanel.ComputedStyle?.Position == PositionMode.Absolute )
{
// When resizing from left edge, update left position
if ( isLeftEdge && alignment.Left )
{
node.TryModifyStyle( "left", $"{newRect.Left - parentLeft}px" );
}
// When resizing from top edge, update top position
if ( isTopEdge && alignment.Top )
{
node.TryModifyStyle( "top", $"{newRect.Top - parentTop}px" );
}
// When resizing from right edge, update right position
if ( isRightEdge && alignment.Right )
{
float rightValue = parentWidth - (newRect.Left + newRect.Width - parentLeft);
node.TryModifyStyle( "right", $"{rightValue}px" );
}
// When resizing from bottom edge, update bottom position
if ( isBottomEdge && alignment.Bottom )
{
float bottomValue = parentHeight - (newRect.Top + newRect.Height - parentTop);
node.TryModifyStyle( "bottom", $"{bottomValue}px" );
}
}
}
// Force update in the designer
OwnerDesigner.ForceUpdate( false );
}
SelectedPanel.Style.Dirty();
}
}
}