//
// Dialog.cs: Dialog box
//
// Authors:
// Miguel de Icaza (miguel@gnome.org)
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text;
using Terminal.Gui;
using static Terminal.Gui.ConfigurationManager;
namespace Terminal.Gui {
///
/// The is a that by default is centered and contains one
/// or more s. It defaults to the color scheme and has a 1 cell padding around the edges.
///
///
/// To run the modally, create the , and pass it to .
/// This will execute the dialog until it terminates via the [ESC] or [CTRL-Q] key, or when one of the views
/// or buttons added to the dialog calls .
///
public class Dialog : Window {
///
/// The default for .
///
///
/// This property can be set in a Theme.
///
[SerializableConfigurationProperty (Scope = typeof (ThemeScope)), JsonConverter (typeof (JsonStringEnumConverter))]
public static ButtonAlignments DefaultButtonAlignment { get; set; } = ButtonAlignments.Center;
// TODO: Reenable once border/borderframe design is settled
///
/// Defines the default border styling for . Can be configured via .
///
//[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
//public static Border DefaultBorder { get; set; } = new Border () {
// LineStyle = LineStyle.Single,
//};
internal List buttons = new List ();
///
/// Initializes a new instance of the class using positioning
/// with no s.
///
///
/// By default, and are set to Pos.Center () and and are set
/// to Width = Dim.Percent (85) , centering the Dialog vertically and horizontally.
///
public Dialog () : this (null) { }
///
/// Initializes a new instance of the class using positioning
/// and an optional set of s to display
///
/// Optional buttons to lay out at the bottom of the dialog.
///
/// By default, and are set to Pos.Center () and and are set
/// to Width = Dim.Percent (85) , centering the Dialog vertically and horizontally.
///
public Dialog (params Button [] buttons) : base ()
{
SetInitialProperties (buttons);
}
private void SetInitialProperties (Button [] buttons)
{
X = Pos.Center ();
Y = Pos.Center ();
Width = Dim.Percent (85);
Height = Dim.Percent (85);
ColorScheme = Colors.Dialog;
Modal = true;
ButtonAlignment = DefaultButtonAlignment;
if (buttons != null) {
foreach (var b in buttons) {
AddButton (b);
}
}
LayoutComplete += (s, args) => {
LayoutButtons ();
};
}
///
/// Adds a to the , its layout will be controlled by the
///
/// Button to add.
public void AddButton (Button button)
{
if (button == null) {
return;
}
//button.AutoSize = false; // BUGBUG: v2 - Hack to get around autosize not accounting for Margin?
buttons.Add (button);
Add (button);
SetNeedsDisplay ();
if (IsInitialized) {
LayoutSubviews ();
}
}
// Get the width of all buttons, not including any Margin.
internal int GetButtonsWidth ()
{
if (buttons.Count == 0) {
return 0;
}
//var widths = buttons.Select (b => b.TextFormatter.GetFormattedSize ().Width + b.BorderFrame.Thickness.Horizontal + b.Padding.Thickness.Horizontal);
var widths = buttons.Select (b => b.Frame.Width);
return widths.Sum ();
}
///
/// Determines the horizontal alignment of the Dialog buttons.
///
public enum ButtonAlignments {
///
/// Center-aligns the buttons (the default).
///
Center = 0,
///
/// Justifies the buttons
///
Justify,
///
/// Left-aligns the buttons
///
Left,
///
/// Right-aligns the buttons
///
Right
}
///
/// Determines how the s are aligned along the
/// bottom of the dialog.
///
public ButtonAlignments ButtonAlignment { get; set; }
void LayoutButtons ()
{
if (buttons.Count == 0 || !IsInitialized) return;
int shiftLeft = 0;
int buttonsWidth = GetButtonsWidth ();
switch (ButtonAlignment) {
case ButtonAlignments.Center:
// Center Buttons
shiftLeft = (Bounds.Width - buttonsWidth - buttons.Count - 1) / 2 + 1;
for (int i = buttons.Count - 1; i >= 0; i--) {
Button button = buttons [i];
shiftLeft += button.Frame.Width + (i == buttons.Count - 1 ? 0 : 1);
if (shiftLeft > -1) {
button.X = Pos.AnchorEnd (shiftLeft);
} else {
button.X = Bounds.Width - shiftLeft;
}
button.Y = Pos.AnchorEnd (1);
}
break;
case ButtonAlignments.Justify:
// Justify Buttons
// leftmost and rightmost buttons are hard against edges. The rest are evenly spaced.
var spacing = (int)Math.Ceiling ((double)(Bounds.Width - buttonsWidth) / (buttons.Count - 1));
for (int i = buttons.Count - 1; i >= 0; i--) {
Button button = buttons [i];
if (i == buttons.Count - 1) {
shiftLeft += button.Frame.Width;
button.X = Pos.AnchorEnd (shiftLeft);
} else {
if (i == 0) {
// first (leftmost) button
var left = Bounds.Width;
button.X = Pos.AnchorEnd (left);
} else {
shiftLeft += button.Frame.Width + (spacing);
button.X = Pos.AnchorEnd (shiftLeft);
}
}
button.Y = Pos.AnchorEnd (1);
}
break;
case ButtonAlignments.Left:
// Left Align Buttons
var prevButton = buttons [0];
prevButton.X = 0;
prevButton.Y = Pos.AnchorEnd (1);
for (int i = 1; i < buttons.Count; i++) {
Button button = buttons [i];
button.X = Pos.Right (prevButton) + 1;
button.Y = Pos.AnchorEnd (1);
prevButton = button;
}
break;
case ButtonAlignments.Right:
// Right align buttons
shiftLeft = buttons [buttons.Count - 1].Frame.Width;
buttons [buttons.Count - 1].X = Pos.AnchorEnd (shiftLeft);
buttons [buttons.Count - 1].Y = Pos.AnchorEnd (1);
for (int i = buttons.Count - 2; i >= 0; i--) {
Button button = buttons [i];
shiftLeft += button.Frame.Width + 1;
button.X = Pos.AnchorEnd (shiftLeft);
button.Y = Pos.AnchorEnd (1);
}
break;
}
}
///
public override bool ProcessKey (KeyEvent kb)
{
switch (kb.Key) {
case Key.Esc:
Application.RequestStop (this);
return true;
}
return base.ProcessKey (kb);
}
}
}