/// <summary> /// Adds the current control to the current focus context /// </summary> /// <param name="c">The control to add</param> public void Add(ConsoleControl c) { if(focusStack.Peek().Controls.Contains(c)) { throw new InvalidOperationException("Item already being tracked"); } focusStack.Peek().Controls.Add(c); }
public Dialog(ConsoleControl content) { Add(content).Fill(padding: new Thickness(0, 0, 1, 1)); closeButton = Add(new Button() { Text = "Close (ESC)",Background = Theme.DefaultTheme.H1Color, Foreground = ConsoleColor.Black }).DockToRight(padding: 1); closeButton.Pressed.SubscribeForLifetime(Escape, this.LifetimeManager); BeforeAddedToVisualTree.SubscribeForLifetime(OnBeforeAddedToVisualTree, this.LifetimeManager); AddedToVisualTree.SubscribeForLifetime(OnAddedToVisualTree, this.LifetimeManager); RemovedFromVisualTree.SubscribeForLifetime(OnRemovedFromVisualTree, this.LifetimeManager); }
public override void OnAdd(ConsoleControl parent) { base.OnAdd(parent); if (Width == 0) Width = parent.Width; if (Height == 0) Height = parent.Height; // start monitoring CPU and memory when added Task.Factory.StartNew(() => { WatchCPUAndMemory(); }); }
public void Move(ConsoleControl control, int column, int row, int columnSpan = 1, int rowSpan = 1) { var assignment = layoutAssignments.Where(a => a.Control == control).First(); assignment.Column = column; assignment.Row = row; assignment.ColumnSpan = columnSpan; assignment.RowSpan = rowSpan; control.Bounds = GetCellArea(assignment); }
private void HandleControlRemoved(ConsoleControl c) { var toRemove = layoutAssignments .Where(a => a.Control == c) .FirstOrDefault(); if (toRemove != null) { layoutAssignments.Remove(toRemove); } }
public override void OnAdd(ConsoleControl parent) { base.OnAdd(parent); this.Foreground = parent.Foreground; this.Background = parent.Background; XAxisValueCompactFormatter = XAxisValueCompactFormatter ?? ((d) => { return(new ConsoleString(string.Format("{0:0,0.0}", d), Foreground.ForegroundColor, Foreground.BackgroundColor)); }); YAxisValueCompactFormatter = YAxisValueCompactFormatter ?? ((d) => { return(new ConsoleString("" + string.Format("{0:0,0.0}", d), Foreground.ForegroundColor, Foreground.BackgroundColor)); }); XAxisValueFormatter = XAxisValueFormatter ?? ((d) => { return(new ConsoleString(string.Format("{0:0,0.0}", d), Foreground.ForegroundColor, Foreground.BackgroundColor)); }); YAxisValueFormatter = YAxisValueFormatter ?? ((d) => { return(new ConsoleString("" + string.Format("{0:0,0.0}", d), Foreground.ForegroundColor, Foreground.BackgroundColor)); }); }
public Dialog(ConsoleControl content) { Add(content).Fill(padding: new Thickness(0, 0, 1, 1)); closeButton = Add(new Button() { Text = "Close (ESC)", Background = Theme.DefaultTheme.H1Color, Foreground = ConsoleColor.Black }).DockToRight(padding: 1); closeButton.Activated.SubscribeForLifetime(Escape, this.LifetimeManager); BeforeAddedToVisualTree.SubscribeForLifetime(OnBeforeAddedToVisualTree, this.LifetimeManager); AddedToVisualTree.SubscribeForLifetime(OnAddedToVisualTree, this.LifetimeManager); RemovedFromVisualTree.SubscribeForLifetime(OnRemovedFromVisualTree, this.LifetimeManager); }
public ViewModelBinding(ConsoleControl control, PropertyInfo controlProperty, ObservableObject viewModel, string observablePath, Func <object, object> mapper, Func <object, object> reverseMapper) { this.mapper = mapper; this.reverseMapper = reverseMapper; var exp = ObjectPathExpression.Parse(observablePath); var trace = exp.EvaluateAndTraceInfo(viewModel); var observableObject = trace[trace.Count - 2].Value as ObservableObject; var viewModelObservableProperty = trace.Last()?.MemberInfo as PropertyInfo; if (observableObject == null) { throw new InvalidOperationException($"ViewModel property '{viewModel.GetType().FullName}.{observablePath}' is not observable"); } if (viewModelObservableProperty == null) { throw new InvalidOperationException($"Cannot resolve ViewModel property '{viewModel.GetType().FullName}.{observablePath}'"); } if (mapper != null && reverseMapper != null) { if (viewModelObservableProperty.PropertyType != controlProperty.PropertyType && viewModelObservableProperty.PropertyType.IsSubclassOf(controlProperty.PropertyType) == false && viewModelObservableProperty.PropertyType.GetInterfaces().Contains(controlProperty.PropertyType) == false) { throw new InvalidOperationException($"ViewModel type '{viewModel.GetType().FullName} property {observablePath}' of type {viewModelObservableProperty.PropertyType.FullName} is not compatible with control property '{controlProperty.DeclaringType.FullName}.{controlProperty.Name}' of type {controlProperty.PropertyType.FullName} "); } } observableObject.SynchronizeForLifetime(trace.Last().MemberInfo.Name, () => { var newValue = viewModelObservableProperty.GetValue(observableObject); if (newValue == latestValue) { return; } latestValue = newValue; controlProperty.SetValue(control, mapper != null ? mapper(newValue) : newValue); }, control.LifetimeManager); control.SubscribeForLifetime(controlProperty.Name, () => { var newValue = controlProperty.GetValue(control); if (newValue == latestValue) { return; } latestValue = newValue; viewModelObservableProperty.SetValue(observableObject, reverseMapper != null ? reverseMapper(newValue) : newValue); }, control.LifetimeManager); }
public Dialog(ConsoleControl content) { Add(content).Fill(padding: new Thickness(0, 0, 1, 1)); AllowEscapeToCancel = true; closeButton = Add(new Button() { Text = "Close (ESC)".ToConsoleString(), Background = DefaultColors.H1Color, Foreground = ConsoleColor.Black }).DockToRight(padding: 1); closeButton.Pressed.SubscribeForLifetime(Escape, this); BeforeAddedToVisualTree.SubscribeForLifetime(OnBeforeAddedToVisualTree, this); AddedToVisualTree.SubscribeForLifetime(OnAddedToVisualTree, this); RemovedFromVisualTree.SubscribeForLifetime(OnRemovedFromVisualTree, this); }
public static T FillVertically <T>(this T child, ConsoleControl parent = null, Thickness?padding = null) where T : ConsoleControl { parent = parent ?? child.Parent; var effectivePadding = padding.HasValue ? padding.Value : new Thickness(0, 0, 0, 0); Action syncAction = () => { child.Bounds = new Rectangle(child.X, effectivePadding.Top, child.Width, parent.Height - (effectivePadding.Top + effectivePadding.Bottom)); }; parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
/// <summary> /// Clears the focus, but preserves the focus index /// </summary> public void ClearFocus() { if (ConsoleApp.Current?.ShouldContinue == false) { return; } if (FocusedControl != null) { FocusedControl.HasFocus = false; FocusedControl.FireFocused(false); FocusedControl = null; } }
public static T DockToLeft <T>(this T child, ConsoleControl parent = null, int padding = 0) where T : ConsoleControl { parent = parent ?? child.Parent; Action syncAction = () => { child.X = padding; }; child.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
private void ComposePaintOver(ConsoleControl control) { var maxX = control.X + control.Width; var maxY = control.Y + control.Height; for (var x = control.X; x < maxX; x++) { for (var y = control.Y; y < maxY; y++) { var controlPixel = control.Bitmap.GetPixel(x - control.X, y - control.Y).Value; Bitmap.DrawPoint(controlPixel.Value, x, y); } } }
private void ControlAddedToVisualTree(ConsoleControl c) { c.Application = this; if (c is ConsolePanel) { var childPanel = c as ConsolePanel; childPanel.Controls.SynchronizeForLifetime((cp) => { ControlAddedToVisualTree(cp); }, (cp) => { ControlRemovedFromVisualTree(cp); }, () => { }, c.LifetimeManager); } FocusManager.Add(c); c.AddedToVisualTreeInternal(); ControlAdded.Fire(c); }
public static T DockToBottom <T>(this T child, ConsoleControl parent = null, int padding = 0) where T : ConsoleControl { parent = parent ?? child.Parent; Action syncAction = () => { if (parent.Height == 0) { return; } child.Y = parent.Height - child.Height - padding; }; child.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
private void ComposePaintOver(ConsoleControl control) { var minX = Math.Max(control.X, 0); var minY = Math.Max(control.Y, 0); var maxX = Math.Min(Width, control.X + control.Width); var maxY = Math.Min(Height, control.Y + control.Height); for (var x = minX; x < maxX; x++) { for (var y = minY; y < maxY; y++) { var controlPixel = control.Bitmap.GetPixel(x - control.X, y - control.Y).Value; Bitmap.Pen = controlPixel.Value; Bitmap.DrawPointUnsafe(x, y); } } }
public static T CenterVertically <T>(this T child, ConsoleControl parent = null) where T : ConsoleControl { parent = parent ?? child.Parent; parent.SynchronizeForLifetime(nameof(ConsoleControl.Bounds), () => { if (parent.Height == 0 || child.Height == 0) { return; } var gap = parent.Height - child.Height; var y = gap / 2; child.Y = Math.Max(0, y); }, parent); return(child); }
public static T DockToRight <T>(this T child, ConsoleControl parent = null, int padding = 0) where T : ConsoleControl { parent = parent ?? child.Parent; Action syncAction = () => { if (parent.Width == 0 || child.Width == 0) { return; } child.X = parent.Width - child.Width - padding; }; child.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent.LifetimeManager); parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent.LifetimeManager); syncAction(); return(child); }
public static T FillHorizontally <T>(this T child, ConsoleControl parent = null, Thickness?padding = null) where T : ConsoleControl { parent = parent ?? child.Parent; var effectivePadding = padding.HasValue ? padding.Value : new Thickness(0, 0, 0, 0); Action syncAction = () => { if (parent.Width == 0) { return; } child.Bounds = new Rectangle(effectivePadding.Left, child.Y, parent.Width - (effectivePadding.Right + effectivePadding.Left), child.Height); }; parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
/// <summary> /// Tries to set focus on the given control. /// </summary> /// <param name="newFocusControl">the control to focus. </param> /// <returns>True if the focus was set or if it was already set, false if the control cannot be focused</returns> public bool TrySetFocus(ConsoleControl newFocusControl) { var index = focusStack.Peek().Controls.IndexOf(newFocusControl); if (index < 0) { return(false); } if (newFocusControl.CanFocus == false) { return(false); } else if (newFocusControl == FocusedControl) { return(true); } else { var oldFocusedControl = FocusedControl; if (oldFocusedControl != null) { oldFocusedControl.HasFocus = false; } newFocusControl.HasFocus = true; FocusedControl = newFocusControl; focusStack.Peek().FocusIndex = index; if (oldFocusedControl != null) { oldFocusedControl.FireFocused(false); } if (FocusedControl != null) { FocusedControl.FireFocused(true); } return(true); } }
internal Point CalculateRelativePosition(ConsoleControl parent) { var x = X; var y = Y; var tempParent = Parent; while (tempParent != null && tempParent != parent) { if (tempParent is ScrollablePanel) { throw new InvalidOperationException("Controls within scrollable panels cannot have their relative positions calculated"); } x += tempParent.X; y += tempParent.Y; tempParent = tempParent.Parent; } return(new Point(x, y)); }
/// <summary> /// Adds the current control to the current focus context /// </summary> /// <param name="c">The control to add</param> internal void Add(ConsoleControl c) { if (focusStack.Peek().Controls.Contains(c)) { throw new InvalidOperationException("Item already being tracked"); } focusStack.Peek().Controls.Add(c); if (c.Id != null && c.Id == currentFocusedControlId) { c.TryFocus(); } c.SubscribeForLifetime(nameof(c.CanFocus), () => { if (c.CanFocus == false && c.HasFocus) { TryMoveFocus(); } }, c); }
protected void Compose(ConsoleControl control) { control.Paint(); foreach (var filter in control.RenderFilters) { filter.Filter(control.Bitmap); } if (control.CompositionMode == CompositionMode.PaintOver) { ComposePaintOver(control); } else if (control.CompositionMode == CompositionMode.BlendBackground) { ComposeBlendBackground(control); } else { ComposeBlendVisible(control); } }
public static T CenterVertically <T>(this T child, ConsoleControl parent = null) where T : ConsoleControl { parent = parent ?? child.Parent; parent.SynchronizeForLifetime(nameof(ConsoleControl.Bounds), () => { if (child == ConsoleApp.Current.LayoutRoot) { } if (parent.Height == 0 || child.Height == 0) { return; } var gap = parent.Height - child.Height; var y = gap / 2; child.Y = y; }, parent.LifetimeManager); return(child); }
public static T CenterHorizontally <T>(this T child, ConsoleControl parent = null) where T : ConsoleControl { parent = parent ?? child.Parent; Action syncAction = () => { if (parent.Width == 0 || child.Width == 0) { return; } var gap = parent.Width - child.Width; var x = gap / 2; child.X = Math.Max(0, x); }; parent.SynchronizeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); child.SynchronizeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
private void ComposeBlendBackground(ConsoleControl control) { var minX = Math.Max(control.X, 0); var minY = Math.Max(control.Y, 0); var maxX = Math.Min(Width, control.X + control.Width); var maxY = Math.Min(Height, control.Y + control.Height); for (var x = minX; x < maxX; x++) { for (var y = minY; y < maxY; y++) { var controlPixel = control.Bitmap.GetPixel(x - control.X, y - control.Y).Value; if (controlPixel?.BackgroundColor != ConsoleString.DefaultBackgroundColor) { Bitmap.Pen = controlPixel.Value; Bitmap.DrawPointUnsafe(x, y); } else { var myPixel = Bitmap.GetPixel(x, y).Value; if (myPixel.HasValue && myPixel.Value.BackgroundColor != ConsoleString.DefaultBackgroundColor) { var composedValue = new ConsoleCharacter(controlPixel.Value.Value, controlPixel.Value.ForegroundColor, myPixel.Value.BackgroundColor); Bitmap.Pen = composedValue; Bitmap.DrawPointUnsafe(x, y); } else { Bitmap.Pen = controlPixel.Value; Bitmap.DrawPointUnsafe(x, y); } } } } }
public static T Fill <T>(this T child, ConsoleControl parent = null, Thickness?padding = null) where T : ConsoleControl { parent = parent ?? child.Parent; var effectivePadding = padding.HasValue ? padding.Value : new Thickness(0, 0, 0, 0); Action syncAction = () => { if (parent.Width == 0 || parent.Height == 0) { return; } var newBounds = new Rectangle(new Point(0, 0), parent.Size); newBounds.X += effectivePadding.Left; newBounds.Width -= effectivePadding.Left; newBounds.Width -= effectivePadding.Right; newBounds.Y += effectivePadding.Top; newBounds.Height -= effectivePadding.Top; newBounds.Height -= effectivePadding.Bottom; if (newBounds.Width < 0) { newBounds.Width = 0; } if (newBounds.Height < 0) { newBounds.Height = 0; } child.Bounds = newBounds; }; parent.SubscribeForLifetime(nameof(ConsoleControl.Bounds), syncAction, parent); syncAction(); return(child); }
private void ComposeBlendVisible(ConsoleControl control) { var minX = Math.Max(control.X, 0); var minY = Math.Max(control.Y, 0); var maxX = Math.Min(Width, control.X + control.Width); var maxY = Math.Min(Height, control.Y + control.Height); for (var x = minX; x < maxX; x++) { for (var y = minY; y < maxY; y++) { var controlPixel = control.Bitmap.GetPixel(x - control.X, y - control.Y); var controlPixelHasRenderableContent = IsVisibleOnMyPanel(controlPixel); if (controlPixelHasRenderableContent) { Bitmap.Pen = controlPixel.Value.Value; Bitmap.DrawPointUnsafe(x, y); } } } }
/// <summary> /// Removes the given control from the layout /// </summary> /// <param name="control">the control to remove</param> public void Remove(ConsoleControl control) { ProtectedPanel.Controls.Remove(control); }
private void ScrollableControls_Added(ConsoleControl c) { c.SubscribeForLifetime(nameof(Bounds), UpdateScrollbars, c.LifetimeManager); }
private void Controls_Added(ConsoleControl obj) { obj.SynchronizeForLifetime(nameof(Bounds), RedoLayout, Controls.GetMembershipLifetime(obj)); }
public virtual void OnAdd(ConsoleControl parent) { if(Added != null) { Added(); } }
private static void ParseControlAttributes(ConsoleControl control, ParserContext context) { foreach (var attribute in context.CurrentElement.Attributes) { var extensions = control.GetType().Attrs<MarkupExtensionAttribute>().Where(a => a.AttributeName == attribute.Name); if(extensions.Count() > 1) { throw new InvalidOperationException("More than 1 extension registered for property "+attribute.Name); } else if(extensions.Count() == 1) { extensions.First().Processor.Process(context); } else { var propertyInfo = control.GetType().GetProperty(attribute.Name); var methodInfo = control.GetType().GetMethod(attribute.Name); if (propertyInfo.GetGetMethod() == null || propertyInfo.GetSetMethod() == null) { if (propertyInfo.PropertyType == typeof(Event)) { // there is special handling for events downstream so let this through } else { throw new InvalidOperationException($"Property {control.GetType().FullName}.{attribute.Name} does not have a public getter and setter"); } } SetPropertyFromTextValue(context, control, propertyInfo, attribute.Value); } } }
private bool ControlRemovedFromVisualTreeRecursive(ConsoleControl c) { bool focusChanged = false; if (c is ConsolePanel) { foreach (var child in (c as ConsolePanel).Controls) { focusChanged = ControlRemovedFromVisualTreeRecursive(child) || focusChanged; } } if (FocusManager.FocusedControl == c) { FocusManager.ClearFocus(); focusChanged = true; } FocusManager.Remove(c); c.RemovedFromVisualTreeInternal(); c.Application = null; ControlRemoved.Fire(c); c.Dispose(); return focusChanged; }
/// <summary> /// Tries to set focus on the given control. /// </summary> /// <param name="newFocusControl">the control to focus. </param> /// <returns>True if the focus was set or if it was already set, false if the control cannot be focused</returns> public bool TrySetFocus(ConsoleControl newFocusControl) { var index = focusStack.Peek().Controls.IndexOf(newFocusControl); if (index < 0) { throw new InvalidOperationException("The given control is not in the control tree"); } if(newFocusControl.CanFocus == false) { return false; } else if(newFocusControl == FocusedControl) { return true; } else { if (FocusedControl != null) { ClearFocus(); } newFocusControl.HasFocus = true; FocusedControl = newFocusControl; focusStack.Peek().FocusIndex = index; if (FocusedControl != null) { FocusedControl.FireFocused(true); } return true; } }
public virtual void OnRemove(ConsoleControl parent) { if(Removed != null) { Removed(); } }
public ViewModelBinding(ConsoleControl control, PropertyInfo controlProperty, ObservableObject viewModel, string observablePropertyName) { var viewModelObservableProperty = viewModel.GetType().GetProperty(observablePropertyName); if (viewModelObservableProperty.PropertyType != controlProperty.PropertyType && viewModelObservableProperty.PropertyType.IsSubclassOf(controlProperty.PropertyType) == false && viewModelObservableProperty.PropertyType.GetInterfaces().Contains(controlProperty.PropertyType) == false) { throw new InvalidOperationException($"ViewModel property '{viewModel.GetType().FullName}.{observablePropertyName}' of type {viewModelObservableProperty.PropertyType.FullName} is not compatible with control property '{controlProperty.DeclaringType.FullName}.{controlProperty.Name}' of type {controlProperty.PropertyType.FullName} "); } viewModel.SynchronizeForLifetime(observablePropertyName, () => { var newValue = viewModelObservableProperty.GetValue(viewModel); if (newValue == latestValue) return; latestValue = newValue; controlProperty.SetValue(control, newValue); }, control.LifetimeManager); control.SubscribeForLifetime(controlProperty.Name, () => { var newValue = controlProperty.GetValue(control); if (newValue == latestValue) return; latestValue = newValue; viewModelObservableProperty.SetValue(viewModel, newValue); }, control.LifetimeManager); }
/// <summary> /// Removes the control from all focus contexts /// </summary> /// <param name="c">The control to remove</param> public void Remove(ConsoleControl c) { foreach(var context in focusStack) { context.Controls.Remove(c); } }
protected virtual (int X, int Y) Transform(ConsoleControl c) => (c.X, c.Y);
public void SetFocus(ConsoleControl newFocusControl) { var index = focusableControls.IndexOf(newFocusControl); if (index < 0) throw new InvalidOperationException("The given control is not in the control tree"); if (newFocusControl != focusedControl) { if (focusedControl != null) { focusedControl.HasFocus = false; focusedControl.FireFocused(false); } focusedControl = newFocusControl; focusedControl.HasFocus = true; focusIndex = index; if (focusedControl != null) focusedControl.FireFocused(true); } }
private void ControlRemovedFromVisualTree(ConsoleControl c) { if (ControlRemovedFromVisualTreeRecursive(c)) { FocusManager.TryRestoreFocus(); } }
/// <summary> /// Clears the focus, but preserves the focus index /// </summary> public void ClearFocus() { FocusedControl.HasFocus = false; FocusedControl.FireFocused(false); FocusedControl = null; }
private static void SetPropertyFromTextValue(ParserContext context, ConsoleControl control, PropertyInfo property, string textValue) { bool isObservable = textValue.StartsWith("{") && textValue.EndsWith("}"); if (isObservable) { var observablePropertyName = textValue.Substring(1, textValue.Length - 2); var viewModelObservable = context.CurrentViewModel as ObservableObject; if (viewModelObservable == null) throw new InvalidOperationException("View model is not observable"); new ViewModelBinding(control, property, viewModelObservable, observablePropertyName); } else if(property.HasAttr<MarkupPropertyAttribute>()) { property.Attr<MarkupPropertyAttribute>().Processor.Process(context); } else if (property.PropertyType == typeof(string)) { property.SetValue(control, textValue); } else if (property.PropertyType == typeof(ConsoleString)) { property.SetValue(control, new ConsoleString(textValue)); } else if (property.PropertyType.IsEnum) { var enumVal = Enum.Parse(property.PropertyType, textValue); property.SetValue(control, enumVal); } else if (property.PropertyType == typeof(Event)) { Event ev = property.GetValue(control) as Event; var target = context.CurrentViewModel; Action handler = () => { var method = target.GetType().GetMethod(textValue, new Type[0]); if (method != null) { method.Invoke(target, new object[0]); } else { var action = target.GetType().GetProperty(textValue); if (action == null || action.PropertyType != typeof(Action)) { throw new InvalidOperationException("Not a method or action"); } ((Action)action.GetValue(target)).Invoke(); } }; ev.SubscribeForLifetime(handler, control.LifetimeManager); } else { var parseMethod = property.PropertyType.GetMethod("Parse", new Type[] { typeof(string) }); var parsed = parseMethod.Invoke(null, new object[] { textValue }); property.SetValue(control, parsed); } }
/// <summary> /// Autogenerates form options for the given object by reflecting on its properties. All public properties with getters /// and setters will be included in the form unless it has the FormIgnore attribute on it. This method supports strings, /// ints, and enums. /// /// The form will be configured to two way bind all the form elements to the property values. /// </summary> /// <param name="o">The object to create form options for</param> /// <param name="labelColumnPercentage">the label column percentage to use</param> /// <returns></returns> public static FormOptions FromObject(object o, double labelColumnPercentage = .25) { var properties = o.GetType().GetProperties().Where(p => p.HasAttr <FormIgnoreAttribute>() == false && p.GetSetMethod() != null && p.GetGetMethod() != null).ToList(); var ret = new FormOptions() { Elements = new ObservableCollection <FormElement>(), LabelColumnPercentage = labelColumnPercentage, }; foreach (var property in properties) { if (o is IObservableObject && property.Name == nameof(IObservableObject.SuppressEqualChanges)) { continue; } ConsoleControl editControl = null; if (property.HasAttr <FormReadOnlyAttribute>() == false && property.PropertyType == typeof(string)) { var value = (string)property.GetValue(o); var textBox = new TextBox() { Foreground = ConsoleColor.White, Value = value == null ? ConsoleString.Empty : value.ToString().ToWhite() }; textBox.SynchronizeForLifetime(nameof(textBox.Value), () => property.SetValue(o, textBox.Value.ToString()), textBox); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => { var valueRead = property.GetValue(o); if (valueRead is ICanBeAConsoleString) { textBox.Value = (valueRead as ICanBeAConsoleString).ToConsoleString(); } else { textBox.Value = (valueRead + "").ToWhite(); } }, textBox); editControl = textBox; } else if (property.HasAttr <FormReadOnlyAttribute>() == false && property.PropertyType == typeof(int)) { if (property.HasAttr <FormSliderAttribute>()) { var value = (int)property.GetValue(o); var slider = property.Attr <FormSliderAttribute>().Factory(); slider.Value = value; slider.SynchronizeForLifetime(nameof(slider.Value), () => { property.SetValue(o, (int)slider.Value); }, slider); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => { var valueRead = (int)property.GetValue(o); slider.Value = valueRead; }, slider); editControl = slider; } else { var value = (int)property.GetValue(o); var textBox = new TextBox() { Foreground = ConsoleColor.White, Value = value.ToString().ToWhite() }; textBox.SynchronizeForLifetime(nameof(textBox.Value), () => { if (textBox.Value.Length == 0) { textBox.Value = "0".ToConsoleString(); } if (textBox.Value.Length > 0 && int.TryParse(textBox.Value.ToString(), out int result)) { property.SetValue(o, result); } else if (textBox.Value.Length > 0) { textBox.Value = property.GetValue(o).ToString().ToWhite(); } }, textBox); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => { var valueRead = property.GetValue(o); if (valueRead is ICanBeAConsoleString) { textBox.Value = (valueRead as ICanBeAConsoleString).ToConsoleString(); } else { textBox.Value = (valueRead + "").ToConsoleString(); } }, textBox); textBox.AddedToVisualTree.SubscribeForLifetime(() => { var previouslyFocusedControl = textBox.Application.FocusManager.FocusedControl; var emptyStringAction = new Action(() => { if (previouslyFocusedControl == textBox && textBox.Application.FocusManager.FocusedControl != textBox) { if (textBox.Value.Length == 0) { textBox.Value = "0".ToConsoleString(); property.SetValue(o, 0); } } previouslyFocusedControl = textBox.Application.FocusManager.FocusedControl; }); textBox.Application.FocusManager.SubscribeForLifetime(nameof(FocusManager.FocusedControl), emptyStringAction, textBox); }, textBox); editControl = textBox; } } else if (property.HasAttr <FormReadOnlyAttribute>() == false && property.PropertyType.IsEnum) { var options = new List <DialogOption>(); foreach (var val in Enum.GetValues(property.PropertyType)) { var enumField = property.PropertyType.GetField(Enum.GetName(property.PropertyType, val)); var display = enumField.HasAttr <FormLabelAttribute>() ? enumField.Attr <FormLabelAttribute>().Label.ToConsoleString() : (val + "").ToConsoleString(); options.Add(new DialogOption() { DisplayText = display, Id = val.ToString(), Value = val, }); } var dropdown = new Dropdown(options); dropdown.Width = Math.Min(40, options.Select(option => option.DisplayText.Length).Max() + 8); dropdown.SubscribeForLifetime(nameof(dropdown.Value), () => property.SetValue(o, dropdown.Value.Value), dropdown); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => dropdown.Value = options.Where(option => option.Value.Equals(property.GetValue(o))).Single(), dropdown); editControl = dropdown; } else if (property.HasAttr <FormReadOnlyAttribute>() == false && property.PropertyType == typeof(RGB)) { var dropdown = new ColorPicker(); dropdown.Width = Math.Min(40, Enums.GetEnumValues <ConsoleColor>().Select(option => option.ToString().Length).Max() + 8); dropdown.SubscribeForLifetime(nameof(dropdown.Value), () => property.SetValue(o, dropdown.Value), dropdown); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => dropdown.Value = (RGB)(property.GetValue(o)), dropdown); editControl = dropdown; } else if (property.HasAttr <FormReadOnlyAttribute>() == false && property.PropertyType == typeof(bool)) { var toggle = new ToggleControl(); if (property.HasAttr <FormYesNoAttribute>()) { toggle.OnLabel = " Yes "; toggle.OffLabel = " No "; } toggle.SubscribeForLifetime(nameof(toggle.On), () => property.SetValue(o, toggle.On), toggle); (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => toggle.On = (bool)property.GetValue(o), toggle); editControl = toggle; } else { var value = property.GetValue(o); var valueString = value != null?value.ToString().ToDarkGray() : "<null>".ToDarkGray(); var valueLabel = new Label() { CanFocus = true, Text = valueString + " (read only)".ToDarkGray() }; (o as IObservableObject)?.SynchronizeForLifetime(property.Name, () => valueLabel.Text = (property.GetValue(o) + "").ToConsoleString() + " (read only)".ToDarkGray(), valueLabel); editControl = valueLabel; } if (property.HasAttr <FormWidth>()) { editControl.Width = property.Attr <FormWidth>().Width; } ret.Elements.Add(new FormElement() { Label = property.HasAttr <FormLabelAttribute>() ? property.Attr <FormLabelAttribute>().Label.ToYellow() : property.Name.ToYellow(), ValueControl = editControl }); } return(ret); }
internal Point CalculateRelativePosition(ConsoleControl parent) { var x = X; var y = Y; var tempParent = Parent; while (tempParent != null && tempParent != parent) { if (tempParent is ScrollablePanel) { throw new InvalidOperationException("Controls within scrollable panels cannot have their relative positions calculated"); } x += tempParent.X; y += tempParent.Y; tempParent = tempParent.Parent; } return new Point(x, y); }
private void Commands_Removed(ConsoleControl c) { Layout.StackHorizontally(1, Controls); }