Create a simple GUI from scratch

On a platform on which there are no libraries available, and with minimal graphics other than the on-screen measurement object (x, y, xx, yy) in coordinates (x, y)), I am trying to create a simple gui.

Can someone point me to a link where I can understand the logistic principles associated with displaying a set of objects on the screen and highlight the selected object, allowing users to move between objects and move the selection to each object. It sounds like it should be simple, but I would like to understand how people think about it.

How to create an object using the obj.highlight () method, where obj.highlight will turn off selection in all other objects? Is it possible to simply execute the next loop through an array of objects, skip the current object, turn off the selection, and then set the current object to true? Selection will be performed by drawing another object on top of the selected object with a transparent center.

This is a single-threaded system (but allows a small amount of asynchronous processing).

I'm looking for more conceptual ideas, but code in VB that doesn't use its own graphical calls might be useful.

+6
source share
2 answers

I coded a small sample application that makes its own control structure by drawing in form using .Net C #. Just something simple with this result:

enter image description here

I did IsSelected by recursively disabling all controls and clicking clicks. See Section with window.MouseUp += (sender, arg) => .

The selection can go through the mouse or the Tab key.

The code approach should be portable to other languages โ€‹โ€‹and online translated to VB.Net.

Corresponding code snippet:

 using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Drawing; using System.Threading; using System.Threading.Tasks; namespace CustomGUI { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Form window = new Form1(); window.BackColor = Color.Gray; Graphics api = window.CreateGraphics(); GUIControl form = new GUIControl(); form.Location = new Point(30,30); form.Size = new Size(200, 300); GUIControl control1 = new GUIControl(); control1.Location = new Point(0, 0); control1.Size = new Size(200, 130); control1.Background = Color.Blue; GUIControl control11 = new GUIControl(); control11.Location = new Point(140, 30); control11.Size = new Size(30, 30); control11.Background = Color.Red; GUIControl control12 = new GUIControl(); control12.Location = new Point(30, 30); control12.Size = new Size(30, 30); control12.Background = Color.Red; control12.BorderColor = Color.Green; control12.BorderWidth = 5; GuiLabel control2 = new GuiLabel(); control2.Location = new Point(10, 200); control2.Size = new Size(180, 30); control2.Background = Color.Green; control2.Text = "Hello World!"; control1.AddChild(control11); control1.AddChild(control12); form.AddChild(control1); form.AddChild(control2); window.MouseUp += (sender, arg) => { // hit test the control where the mouse has landed IGUIContainer control = form.HitTest(arg.Location); if (control != null) { // recursive on all controls foreach (var ct in (new IGUIContainer[] { form }).Traverse(c => c.Controls)) { //deselecting all others if (ct != control) ct.IsSelected = false; } control.IsSelected = !control.IsSelected; } window.Invalidate(); // force paint }; window.KeyUp += (sender, key) => { if (key.KeyCode == Keys.Tab && key.Modifiers == Keys.None) { var selected = (new IGUIContainer[] { form }).Traverse(c => c.Controls).FirstOrDefault(c => c.IsSelected); IGUIContainer parent; if (selected == null) { parent = form; } else { parent = selected; } IGUIContainer control; if (parent.Controls.Count > 0) { control = parent.Controls[0]; } else { control = GUIControl.Next(parent); } if (control == null) control = form; foreach (var ct in (new IGUIContainer[] { form }).Traverse(c => c.Controls)) { if (ct != control) ct.IsSelected = false; } control.IsSelected = true; window.Invalidate(); } }; window.Paint += (sender, args) => { form.Draw(api, new Point(0,0)); }; Application.Run(window); } } } 

All necessary classes and interfaces:

IDrawable:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace CustomGUI { public interface IDrawable { Point Location { get; set; } Size Size { get; set; } Rectangle GetRealRect(Point origin); void Draw(Graphics gfxApi, Point origin); } } 

IGUIContainer:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace CustomGUI { delegate void SelectionChangedHandler(object sender, bool newIsSelected); interface IGUIContainer : IUIElement { IGUIContainer Parent { get; set; } List<IGUIContainer> Controls { get; } void AddChild(IGUIContainer child); bool IsSelected { get; set; } event SelectionChangedHandler SelectionChanged; IGUIContainer HitTest(Point mouseCoord); } } 

UIElement:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Diagnostics; namespace CustomGUI { abstract class UIElement : IUIElement { private Point _location; private Size _size; private Color _background; private Color _foreground; private Color _borderColor; private int _borderWidth; public UIElement() { _foreground = Color.Black; _background = Color.White; _borderColor = Color.Transparent; } public Point Location { get { return _location; } set { _location = value; } } public Size Size { get { return _size; } set { _size = value; } } public virtual void Draw(Graphics drawingApi, Point origin) { Rectangle inside = GetRealRect(origin); Pen borderPen = new Pen(new SolidBrush(_borderColor), _borderWidth); drawingApi.FillRectangle(new SolidBrush(_background), inside); drawingApi.DrawRectangle(borderPen, inside); } public Rectangle ClientRect { get { return new Rectangle(_location, _size); } } public Color Background { get { return _background; } set { _background = value; } } public Color Foreground { get { return _foreground; } set { _foreground = value; } } public Rectangle GetRealRect(Point origin) { int left = ClientRect.Left + origin.X; int top = ClientRect.Top + origin.Y; int width = ClientRect.Width; int height = ClientRect.Height; Debug.WriteLine("GetRealRect " + left + ", " + top + ", " + width + ", " + height); return new Rectangle(left, top, width, height); } public int BorderWidth { get { return _borderWidth; } set { _borderWidth = value; } } public Color BorderColor { get { return _borderColor; } set { _borderColor = value; } } } } 

GuiControl:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace CustomGUI { class GUIControl : UIElement, IGUIContainer { private IGUIContainer _parent; private List<IGUIContainer> _controls = new List<IGUIContainer>(); private bool _isSelected; public List<IGUIContainer> Controls { get { return _controls; } } public override void Draw(Graphics api, Point origin) { Point original = origin; base.Draw(api, origin); origin.Offset(this.Location); foreach (var ctrl in Controls) { ctrl.Draw(api, origin); } if (IsSelected) { Pen selection = new Pen(Color.Yellow, 3); selection.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; api.DrawRectangle(selection, GetRealRect(original)); } } public IGUIContainer HitTest(Point coord) { Point newOrigin = coord; newOrigin.Offset(-this.Location.X, -this.Location.Y); foreach (var ctrl in Controls) { IGUIContainer hit = ctrl.HitTest(newOrigin); if (hit != null) { return hit; } } return ClientRect.Contains(coord) ? this : null; } public bool IsSelected { get { return _isSelected; } set { _isSelected = value; if (SelectionChanged != null) { SelectionChanged(this, _isSelected); } } } public event SelectionChangedHandler SelectionChanged; public void AddChild(IGUIContainer child) { // if you need to implement event propagation this is the place to attach them to children child.Parent = this; Controls.Add(child); } public IGUIContainer Parent { get { return _parent; } set { _parent = value; } } public static IGUIContainer Next(IGUIContainer self) { if (self.Parent != null && self.Parent.Controls.Count - 1 > self.Parent.Controls.IndexOf(self)) { return self.Parent.Controls[self.Parent.Controls.IndexOf(self) + 1]; } else if (self.Parent != null) { return Next(self.Parent); } else { return null; } } } } 

GUILabel:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace CustomGUI { class GuiLabel : GUIControl { public string Text { get; set; } public Font Font { get; set; } public GuiLabel() { Font = new Font(new FontFamily("Tahoma"), 12, FontStyle.Regular); } public override void Draw(System.Drawing.Graphics api, System.Drawing.Point origin) { base.Draw(api, origin); Rectangle controlRect = GetRealRect(origin); SizeF size = api.MeasureString(Text, Font); Point textPosition = new Point(controlRect.Location.X + (int)(controlRect.Width - size.Width) / 2, controlRect.Location.Y + (int)(controlRect.Height - size.Height) / 2); api.DrawString(Text, Font, new SolidBrush(Foreground), textPosition); } } } 

Extension (for the Traverse method for smoothing recursion):

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CustomGUI { static class Extensions { public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse) { foreach (T item in source) { yield return item; IEnumerable<T> seqRecurse = fnRecurse(item); if (seqRecurse != null) { foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse)) { yield return itemRecurse; } } } } } } 
+3
source

Well, this is one question that can be answered in millions of ways ... :)

But while you can draw pixels (or something similar remotely), you can draw a graphical interface. If you have an object-oriented language at hand, I would not select and highlight the current object. I would give if focus and remove focus from it, and let the object itself decide whether to redraw it and how to do it.

You can automatically defocus the previous object if the entire object is placed in a container. When you press a navigation key (e.g. Tab) or click the mouse button, this container can process this message and focus the next object and focus the last object.

This requires some programming, but the concept is pretty simple. It gets harder when you want it to work well, look smooth, have all kinds of anomalies and transitions ... But, as I said, the concept is simple, and you wonโ€™t even need OO, although it will probably give you a lot cleaner result. I think I can program the ASCII-based GUI in DOS Batch on a rainy day if I need to.

+2
source

Source: https://habr.com/ru/post/891597/


All Articles