#nullable enable
using System.Text;
using UICatalog;
using UICatalog.Scenarios;
///
/// Demonstrates creating and drawing regions through mouse dragging.
///
[ScenarioMetadata ("Regions", "Region Tester")]
[ScenarioCategory ("Mouse and Keyboard")]
[ScenarioCategory ("Drawing")]
public class RegionScenario : Scenario
{
private readonly Region _region = new ();
private Point? _dragStart;
private bool _isDragging;
private readonly Rune? _previewFillRune = Glyphs.Stipple;
private RegionDrawStyles _drawStyle;
private RegionOp _regionOp;
public override void Main ()
{
Application.Init ();
Window appWindow = new ()
{
Title = GetQuitKeyAndName (),
TabStop = TabBehavior.TabGroup
};
appWindow.Padding!.Thickness = new (1);
var tools = new ToolsView { Title = "Tools", X = Pos.AnchorEnd (), Y = 2 };
tools.CurrentAttribute = appWindow.GetAttributeForRole (VisualRole.HotNormal);
tools.SetStyle += b =>
{
_drawStyle = b;
appWindow.SetNeedsDraw ();
};
tools.RegionOpChanged += (s, e) => { _regionOp = e; };
//tools.AddLayer += () => canvas.AddLayer ();
appWindow.Add (tools);
// Add drag handling to window
appWindow.MouseEvent += (s, e) =>
{
if (e.Flags.HasFlag (MouseFlags.Button1Pressed))
{
if (!e.Flags.HasFlag (MouseFlags.ReportMousePosition))
{ // Start drag
_dragStart = e.ScreenPosition;
_isDragging = true;
}
else
{
// Drag
if (_isDragging && _dragStart.HasValue)
{
appWindow.SetNeedsDraw ();
}
}
}
if (e.Flags.HasFlag (MouseFlags.Button1Released))
{
if (_isDragging && _dragStart.HasValue)
{
// Add the new region
AddRectangleFromPoints (_dragStart.Value, e.ScreenPosition, _regionOp);
_isDragging = false;
_dragStart = null;
}
appWindow.SetNeedsDraw ();
}
};
// Draw the regions
appWindow.DrawingContent += (s, e) =>
{
// Draw all regions with single line style
//_region.FillRectangles (_attribute.Value, _fillRune);
switch (_drawStyle)
{
case RegionDrawStyles.FillOnly:
_region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, _previewFillRune);
break;
case RegionDrawStyles.InnerBoundaries:
_region.DrawBoundaries (appWindow.LineCanvas, LineStyle.Single, tools.CurrentAttribute);
_region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, (Rune)' ');
break;
case RegionDrawStyles.OuterBoundary:
_region.DrawOuterBoundary (appWindow.LineCanvas, LineStyle.Single, tools.CurrentAttribute);
_region.FillRectangles (appWindow.App?.Driver, tools.CurrentAttribute!.Value, (Rune)' ');
break;
}
// If currently dragging, draw preview rectangle
if (_isDragging && _dragStart.HasValue)
{
Point currentMousePos = appWindow.App!.Mouse.LastMousePosition!.Value;
Rectangle previewRect = GetRectFromPoints (_dragStart.Value, currentMousePos);
var previewRegion = new Region (previewRect);
previewRegion.FillRectangles (appWindow.App.Driver, tools.CurrentAttribute!.Value, (Rune)' ');
previewRegion.DrawBoundaries (
appWindow.LineCanvas,
LineStyle.Dashed,
new (
tools.CurrentAttribute!.Value.Foreground.GetBrighterColor (),
tools.CurrentAttribute!.Value.Background));
}
};
Application.Run (appWindow);
// Clean up
appWindow.Dispose ();
Application.Shutdown ();
}
private void AddRectangleFromPoints (Point start, Point end, RegionOp op)
{
Rectangle rect = GetRectFromPoints (start, end);
var region = new Region (rect);
_region.Combine (region, op); // Or RegionOp.MinimalUnion if you want minimal rectangles
}
private Rectangle GetRectFromPoints (Point start, Point end)
{
int left = Math.Min (start.X, end.X);
int top = Math.Min (start.Y, end.Y);
int right = Math.Max (start.X, end.X);
int bottom = Math.Max (start.Y, end.Y);
// Ensure minimum width and height of 1
int width = Math.Max (1, right - left + 1);
int height = Math.Max (1, bottom - top + 1);
return new (left, top, width, height);
}
}
public enum RegionDrawStyles
{
FillOnly = 0,
InnerBoundaries = 1,
OuterBoundary = 2
}
public class ToolsView : Window
{
//private Button _addLayerBtn;
private readonly AttributeView _attributeView = new ();
private OptionSelector? _stylePicker;
private OptionSelector? _regionOpSelector;
public Attribute? CurrentAttribute
{
get => _attributeView.Value;
set => _attributeView.Value = value;
}
public ToolsView ()
{
BorderStyle = LineStyle.Dotted;
Border!.Thickness = new (1, 2, 1, 1);
Height = Dim.Auto ();
Width = Dim.Auto ();
}
//public event Action AddLayer;
public override void BeginInit ()
{
base.BeginInit ();
_attributeView.ValueChanged += (s, e) => AttributeChanged?.Invoke (this, e);
_stylePicker = new ()
{
Width = Dim.Fill (),
X = 0, Y = Pos.Bottom (_attributeView) + 1,
AssignHotKeys = true
};
_stylePicker.BorderStyle = LineStyle.Single;
_stylePicker.Border!.Thickness = new (0, 1, 0, 0);
_stylePicker.Title = "Draw Style";
_stylePicker.ValueChanged += (s, a) => { SetStyle?.Invoke ((RegionDrawStyles)a.Value!); };
_stylePicker.Value = RegionDrawStyles.FillOnly;
_regionOpSelector = new ()
{
X = 0,
Y = Pos.Bottom (_stylePicker) + 1,
AssignHotKeys = true
};
_regionOpSelector.ValueChanged += (s, a) =>
{
if (a.Value is { })
{
RegionOpChanged?.Invoke (this, (RegionOp)a.Value);
}
};
_regionOpSelector.Value = RegionOp.MinimalUnion;
//_addLayerBtn = new () { Text = "New Layer", X = Pos.Center (), Y = Pos.Bottom (_stylePicker) };
//_addLayerBtn.Accepting += (s, a) => AddLayer?.Invoke ();
Add (_attributeView, _stylePicker, _regionOpSelector); //, _addLayerBtn);
}
public event EventHandler? AttributeChanged;
public event EventHandler? RegionOpChanged;
public event Action? SetStyle;
}
public class AttributeView : View
{
public event EventHandler? ValueChanged;
private Attribute? _value;
public Attribute? Value
{
get => _value;
set
{
_value = value;
ValueChanged?.Invoke (this, value);
}
}
private static readonly HashSet<(int, int)> _foregroundPoints =
[
(0, 0), (1, 0), (2, 0),
(0, 1), (1, 1), (2, 1)
];
private static readonly HashSet<(int, int)> _backgroundPoints =
[
(3, 1),
(1, 2), (2, 2), (3, 2)
];
public AttributeView ()
{
Width = Dim.Fill ();
Height = 4;
BorderStyle = LineStyle.Single;
Border!.Thickness = new (0, 1, 0, 0);
Title = "Attribute";
}
///
protected override bool OnDrawingContent (DrawContext? context)
{
Color fg = Value?.Foreground ?? Color.Black;
Color bg = Value?.Background ?? Color.Black;
bool isTransparentFg = fg == GetAttributeForRole (VisualRole.Normal).Background;
bool isTransparentBg = bg == GetAttributeForRole (VisualRole.Normal).Background;
SetAttribute (new (fg, isTransparentFg ? Color.Gray : fg));
// Square of foreground color
foreach ((int, int) point in _foregroundPoints)
{
// Make pattern like this when it is same color as background of control
/*▓▒
▒▓*/
Rune rune;
if (isTransparentFg)
{
rune = (Rune)(point.Item1 % 2 == point.Item2 % 2 ? '▓' : '▒');
}
else
{
rune = (Rune)'█';
}
AddRune (point.Item1, point.Item2, rune);
}
SetAttribute (new (bg, isTransparentBg ? Color.Gray : bg));
// Square of background color
foreach ((int, int) point in _backgroundPoints)
{
// Make pattern like this when it is same color as background of control
/*▓▒
▒▓*/
Rune rune;
if (isTransparentBg)
{
rune = (Rune)(point.Item1 % 2 == point.Item2 % 2 ? '▓' : '▒');
}
else
{
rune = (Rune)'█';
}
AddRune (point.Item1, point.Item2, rune);
}
return true;
}
///
protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
{
if (IsForegroundPoint (mouseEvent.Position.X, mouseEvent.Position.Y))
{
ClickedInForeground ();
}
else if (IsBackgroundPoint (mouseEvent.Position.X, mouseEvent.Position.Y))
{
ClickedInBackground ();
}
}
mouseEvent.Handled = true;
return mouseEvent.Handled;
}
private bool IsForegroundPoint (int x, int y) { return _foregroundPoints.Contains ((x, y)); }
private bool IsBackgroundPoint (int x, int y) { return _backgroundPoints.Contains ((x, y)); }
private void ClickedInBackground ()
{
if (LineDrawing.PromptForColor ("Background", Value!.Value.Background, out Color newColor))
{
Value = new (Value!.Value.Foreground, newColor);
SetNeedsDraw ();
}
}
private void ClickedInForeground ()
{
if (LineDrawing.PromptForColor ("Foreground", Value!.Value.Foreground, out Color newColor))
{
Value = new (newColor, Value!.Value.Background);
SetNeedsDraw ();
}
}
}