/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of. /// </param> public CheckBox(Game game, GUIManager guiManager) : base(game, guiManager) { #region Create Child Controls this.button = new ImageButton(game, guiManager); this.label = new Label(game, guiManager); #endregion #region Add Child Controls Add(this.button); Add(this.label); #endregion #region Set Properties CanHaveFocus = false; #endregion #region Set Default Properties Width = defaultHeight; Height = defaultHeight; HMargin = defaultHMargin; Skin = defaultSkin; HoverSkin = defaultHoverSkin; PressedSkin = defaultPressedSkin; CheckedSkin = defaultCheckedSkin; CheckedHoverSkin = defaultCheckedHoverSkin; CheckedPressedSkin = defaultCheckedPressedSkin; #endregion #region Event Handlers this.button.Click += new ClickHandler(OnClick); #endregion }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of.</param> public TextButton(Game game, GUIManager guiManager) : base(game, guiManager) { #region Create Child Controls this.buttonBar = new Bar(game, guiManager); this.label = new Label(game, guiManager); #endregion #region Add Child Controls Add(this.buttonBar); Add(this.label); #endregion #region Set Properties this.buttonBar.IsVertical = false; MinWidth = defaultHeight; MinHeight = defaultHeight; #endregion #region Set Default Properties Width = defaultWidth; Height = defaultHeight; EdgeSize = defaultEdgeSize; Skin = defaultSkin; HoverSkin = defaultHoverSkin; PressedSkin = defaultPressedSkin; #endregion }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of.</param> public MenuItem(Game game, GUIManager guiManager) : base(game, guiManager) { this.numMenuItems = 0; this.numClicks = 0; this.isPopUpShown = false; this.isHighlightShown = false; this.isEnabled = true; this.canClose = true; #region Create Child Controls this.label = new Label(game, guiManager); this.highlightBox = new Box(game, guiManager); this.popUpMenu = new PopUpMenu(game, guiManager); this.arrow = new Icon(game, guiManager); #endregion #region Add Child Controls base.Add(this.label); #endregion #region Set Default Properties HMargin = defaultHMargin; VMargin = defaultVMargin; HighlightSkin = defaultHighlightSkin; ArrowSkin = defaultArrowSkin; #endregion #region Event Handlers this.popUpMenu.Close += new CloseHandler(OnPopUpClosed); #endregion }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of. /// </param> public TextBox(Game game, GUIManager guiManager) : base(game, guiManager) { this.seconds = 0.0; #region Create Child Controls this.box = new Box(game, guiManager); this.label = new Label(game, guiManager); #endregion #region Add Child Controls Add(this.box); Add(this.label); #endregion #region Set Default Properties Width = defaultWidth; Height = defaultHeight; HMargin = defaultHMargin; VMargin = defaultVMargin; Skin = defaultSkin; #endregion }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of.</param> /// <param name="message">Message to display.</param> /// <param name="title">Window title.</param> /// <param name="buttons">Tyoe of buttons to display.</param> /// <param name="type">Type of icon to use.</param> public MessageBox( Game game, GUIManager guiManager, string message, string title, MessageBoxButtons buttons, MessageBoxType type ) : base(game, guiManager) { this.buttonList = new List<TextButton>(); #region Create Child Controls this.icon = new Icon(game, guiManager); this.message = new Label(game, guiManager); #endregion this.buttons = buttons; if (this.buttons == MessageBoxButtons.OK || this.buttons == MessageBoxButtons.Yes_No) HasCloseButton = false; this.infoSkin = defaultInfoSkin; this.errorSkin = defaultErrorSkin; this.warningSkin = defaultWarningSkin; this.questionSkin = defaultQuestionSkin; this.type = type; Add(this.message); this.TitleText = title; this.message.Text = message; int numButtons = 0; if (this.buttons == MessageBoxButtons.OK) numButtons = 1; else if (this.buttons == MessageBoxButtons.OK_Cancel || this.buttons == MessageBoxButtons.Yes_No) numButtons = 2; else if (this.buttons == MessageBoxButtons.Yes_No_Cancel) numButtons = 3; for (int i = 0; i < numButtons; i++) { TextButton newButton = new TextButton(game, guiManager); this.buttonList.Add(newButton); Add(newButton); if (i == 0) { if (this.buttons == MessageBoxButtons.OK || this.buttons == MessageBoxButtons.OK_Cancel) newButton.Text = "OK"; else newButton.Text = "Yes"; } else if (i == 1) { if (this.buttons == MessageBoxButtons.OK_Cancel) newButton.Text = "Cancel"; else newButton.Text = "No"; } else newButton.Text = "Cancel"; newButton.Click += new ClickHandler(OnClick); } ArrangeWindow(type); }
/// <summary> /// Selects a specified label. Changes the label to the selection /// colour, and scrolls to show entry if necessary Also invokes /// SelectionChanged event. /// </summary> /// <param name="label">Label to select.</param> /// <param name="index">Index of selected item.</param> protected void Select(Label label, int index) { if (index == -1) { if (this.selectedLabel != null) this.selectedLabel.Color = Color.Black; this.selectedIndex = index; } else { if (label != this.selectedLabel) { // Deselect current item if (this.selectedLabel != null) this.selectedLabel.Color = Color.Black; // Select new item this.selectedLabel = label; this.selectedLabel.Color = Color.Blue; this.selectedIndex = index; } if (SelectedChanged != null) SelectedChanged.Invoke(this); // Automatically scroll to selected item if (-(this.selectedLabel.Y + this.selectedLabel.Height) < (this.surface.Y - this.viewPort.Height) ) // Scroll down { this.surface.Y = (this.viewPort.Height - (this.selectedLabel.Y + this.selectedLabel.Height)); this.scrollBar.Value = -this.surface.Y; } else if (-this.selectedLabel.Y > this.surface.Y) // Scroll up { this.surface.Y = -this.selectedLabel.Y; this.scrollBar.Value = -this.surface.Y; } } }
/// <summary> /// Finds the index of the supplied label. /// </summary> /// <param name="label">Search key.</param> /// <returns>Index of entry, or -1 if it was not found.</returns> protected int FindIndex(Label label) { for (int i = 0; i < this.entries.Count; i++) { if (this.entries[i] == label) return i; } return -1; }
/// <summary> /// Adds a string as an entry in the listbox. /// </summary> /// <param name="text">Text of the new entry.</param> public void AddEntry(string text) { // Create a new label Label newEntry = new Label(Game, GUIManager); newEntry.Text = text; newEntry.Font = this.fontFileName; // Add new entry this.entries.Add(newEntry); this.surface.Add(newEntry); RefreshEntries(); }
protected override bool initGUI() { _guiMgr = new GUIManager(_screenMgr); _screenMgr.Components.Add(_guiMgr); _guiMgr.Initialize(); #region Shorthand GUI item creation // Create a MenuItem Func<UIComponent, String, MenuItem> create_mi = (UIComponent parent, String text) => { MenuItem mi = new MenuItem(_screenMgr, _guiMgr); mi.Text = text; if (parent is MenuBar) (parent as MenuBar).Add(mi); else if (parent is MenuItem) (parent as MenuItem).Add(mi); return mi; }; // Create a TextButton Func<UIComponent, String, int, int, int, int, TextButton> create_btn = (UIComponent parent, String text, int w, int h, int x, int y) => { TextButton btn = new TextButton(_screenMgr, _guiMgr); btn.Text = text; btn.Width = w; btn.Height = h; btn.X = x; btn.Y = y; if (parent is Dialog) (parent as Dialog).Add(btn); else if (parent is Window) (parent as Window).Add(btn); return btn; }; // Create a Dialog Func<String, int, int, int, int, Dialog> create_dialog = (String text, int w, int h, int x, int y) => { Dialog dialog = new Dialog(_screenMgr, _guiMgr); _guiMgr.Add(dialog); dialog.TitleText = text; dialog.Width = w; dialog.Height = h; dialog.X = x; dialog.Y = y; dialog.HasCloseButton = false; int bwidth = 50; int bheight = 20; int bxoffs = 10; int byoffs = dialog.Height - 60; // Ok button TextButton btnOk = create_btn( dialog, "Ok", bwidth, bheight, bxoffs, byoffs); btnOk.Click += delegate(UIComponent sender) { dialog.DialogResult = DialogResult.OK; dialog.CloseWindow(); }; // Cancel button TextButton btnCancel = create_btn( dialog, "Cancel", bwidth, bheight, bxoffs * 2 + bwidth, byoffs); btnCancel.Click += delegate(UIComponent sender) { dialog.DialogResult = DialogResult.Cancel; dialog.CloseWindow(); }; return dialog; }; // Create a text box Func<UIComponent, String, int, int, int, TextBox> create_textbox = (UIComponent parent, String text, int w, int x, int y) => { TextBox textBox = new TextBox(_screenMgr, _guiMgr); textBox.Width = w; textBox.X = x; textBox.Y = y; Label label = new Label(_screenMgr, _guiMgr); label.Text = text; label.Width = 100; label.Height = 50; label.X = x - label.Width; label.Y = y + 5; if (parent is Dialog) { (parent as Dialog).Add(textBox); (parent as Dialog).Add(label); } return textBox; }; #endregion { // Main menu bar MenuBar menuBar = new MenuBar(_screenMgr, _guiMgr); _guiMgr.Add(menuBar); //----------------------------------------------------------------- { // File MenuItem fileButton = create_mi(menuBar, "File"); //------------------------------------------------------------- { // New MenuItem newButton = create_mi(fileButton, "New"); newButton.Click += delegate(UIComponent sender) { Dialog d = create_dialog("New", 300, 200, 100, 100); TextBox rows = create_textbox(d, "Rows", 50, 150, 10); TextBox cols = create_textbox(d, "Cols", 50, 150, 40); TextBox tile = create_textbox(d, "Tile", 100, 150, 70); d.Close += delegate(UIComponent dsender) { switch (d.DialogResult) { case DialogResult.Cancel: return; case DialogResult.OK: int numRows = Convert.ToInt32(rows.Text); int numCols = Convert.ToInt32(cols.Text); _gameLevelMgr.newLevel(numRows, numCols, tile.Text); return; } }; }; } //------------------------------------------------------------- { // Save as MenuItem saveAsButton = create_mi(fileButton, "Save as"); saveAsButton.Click += delegate(UIComponent sender) { Dialog d = create_dialog("Save as", 300, 200, 100, 100); TextBox file = create_textbox(d, "Path", 200, 100, 50); d.Close += delegate(UIComponent dsender) { switch (d.DialogResult) { case DialogResult.Cancel: return; case DialogResult.OK: _gameLevelMgr.saveLevel(LEVEL_DIRECTORY + file.Text); return; } }; }; } //------------------------------------------------------------- { // Load MenuItem loadButton = create_mi(fileButton, "Load"); loadButton.Click += delegate(UIComponent sender) { Dialog d = create_dialog("Load", 300, 200, 100, 100); TextBox file = create_textbox(d, "File", 200, 100, 50); d.Close += delegate(UIComponent dsender) { switch (d.DialogResult) { case DialogResult.Cancel: return; case DialogResult.OK: _gameLevelMgr.loadLevel("levels\\" + file.Text); return; } }; }; } //------------------------------------------------------------- { // Quit to menu MenuItem quitButton = create_mi(fileButton, "Quit to menu"); quitButton.Click += delegate(UIComponent sender) { }; } } //----------------------------------------------------------------- { // Windows MenuItem windows = create_mi(menuBar, "Windows"); Func<String, int, int, int, int, Window> create_win = (String text, int w, int h, int x, int y) => { Window win = new Window(_screenMgr, _guiMgr); _guiMgr.Add(win); win.Width = w; win.Height = h; win.X = x; win.Y = y; win.TitleText = text; return win; }; //------------------------------------------------------------- { // Tile browser MenuItem tbrowser = create_mi(windows, "Tile Browser"); tbrowser.Click += delegate(UIComponent sender) { Window tbwin = create_win("Tile Browser", 300, 500, 400, 100); //----------------------------------------------------- { // Tile buttons TextButton stoneButton = create_btn(tbwin, "Stone", 60, 30, 10, 10); stoneButton.Click += delegate(UIComponent bsender) { Drawable tdrwble = _gameContentMgr.loadDrawable("tile_stone"); foreach (GameTile tile in _selection) tile.Entity.Drawable = tdrwble; }; TextButton grassButton = create_btn(tbwin, "Grass", 60, 30, 10, 50); grassButton.Click += delegate(UIComponent bsender) { Drawable tdrwble = _gameContentMgr.loadDrawable("tile_grass"); foreach (GameTile tile in _selection) tile.Entity.Drawable = tdrwble; }; TextButton rockButton = create_btn(tbwin, "Rock", 60, 30, 10, 90); rockButton.Click += delegate(UIComponent bsender) { Drawable tdrwble = _gameContentMgr.loadDrawable("tile_rock"); foreach (GameTile tile in _selection) tile.Entity.Drawable = tdrwble; }; TextButton sandButton = create_btn(tbwin, "Sand", 60, 30, 10, 130); sandButton.Click += delegate(UIComponent bsender) { Drawable tdrwble = _gameContentMgr.loadDrawable("tile_sand"); foreach (GameTile tile in _selection) tile.Entity.Drawable = tdrwble; }; TextButton stoneSandButton = create_btn(tbwin, "S-Sand", 60, 30, 10, 170); stoneSandButton.Click += delegate(UIComponent bsender) { Drawable tdrwble = _gameContentMgr.loadDrawable("tile_stonesand"); foreach (GameTile tile in _selection) tile.Entity.Drawable = tdrwble; }; } }; } //------------------------------------------------------------- { // Character browser MenuItem chrbrowser = create_mi(windows, "Character Browser"); chrbrowser.Click += delegate(UIComponent sender) { Window chrbwin = create_win("Character Browser", 300, 500, 400, 100); }; } //------------------------------------------------------------- { // Object browser MenuItem itembrowser = create_mi(windows, "Item Browser"); itembrowser.Click += delegate(UIComponent sender) { Window ibwin = create_win("Item Browser", 300, 500, 400, 100); //----------------------------------------------------- { // Item buttons TextButton tree1btn = create_btn(ibwin, "Tree1", 60, 30, 10, 10); tree1btn.Click += delegate(UIComponent bsender) { foreach (GameTile tile in _selection) { GameObject tree = _gameLevelMgr.createGameObject<GameObject>("tree" + _gameLevelMgr.GameObjectCount, "tree1"); tile.Node.attachChildNode(tree.Node); tree.Node.translateTo(tile.Node.PositionIsometric); } }; } }; } } //----------------------------------------------------------------- { // Tool bar Window toolBar = new Window(_screenMgr, _guiMgr); _guiMgr.Add(toolBar); toolBar.HasCloseButton = false; toolBar.Width = _screenMgr.GraphicsDevice.Viewport.Width; toolBar.Height = 60; toolBar.Y = menuBar.Y + menuBar.Height; toolBar.Resizable = false; toolBar.HasFullWindowMovableArea = false; toolBar.TitleBarHeight = 4; int btncount, btnx, btny, btnw, btnh; btncount = 0; btnx = 8; btny = 4; btnw = 60; btnh = 24; //------------------------------------------------------------- { // Deselect TextButton noneButton = create_btn(toolBar, "None", btnw, btnh, ((btnx + btnw) * btncount++), btny); noneButton.Click += delegate(UIComponent sender) { Tool = EditorTool.TOOL_NONE; }; } //------------------------------------------------------------- { // Select TextButton selectButton = create_btn(toolBar, "Select", btnw, btnh, ((btnx + btnw) * btncount++), btny); selectButton.Click += delegate(UIComponent sender) { Tool = EditorTool.TOOL_SELECT; }; } //------------------------------------------------------------- { // Zero TextButton zeroButton = create_btn(toolBar, "Zero", btnw, btnh, ((btnx + btnw) * btncount++), btny); zeroButton.Click += delegate(UIComponent sender) { batchElevation(0.0f); }; } //------------------------------------------------------------- { // Elevate TextButton elevateButton = create_btn(toolBar, "Elevate", btnw, btnh, ((btnx + btnw) * btncount++), btny); elevateButton.Click += delegate(UIComponent sender) { Tool = EditorTool.TOOL_ELEVATE; }; } //------------------------------------------------------------- { // Activate TextButton activateButton = create_btn(toolBar, "Activate", btnw, btnh, ((btnx + btnw) * btncount++), btny); activateButton.Click += delegate(UIComponent sender) { batchActivate(true); }; } //------------------------------------------------------------- { // Deactivate TextButton deactivateButton = create_btn(toolBar, "Deact", btnw, btnh, ((btnx + btnw) * btncount++), btny); deactivateButton.Click += delegate(UIComponent sender) { batchActivate(false); }; } } } return base.initGUI(); }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of.</param> public Window(Game game, GUIManager guiManager) : base(game, guiManager) { this.isResizable = true; this.transparency = -1; #region Create Child Controls this.box = new Box(game, guiManager); this.viewPort = new UIComponent(game, guiManager); this.titleBar = new Bar(game, guiManager); this.movableArea = new MovableArea(game, guiManager); this.backgroundMovableArea = new MovableArea(game, guiManager); this.label = new Label(game, guiManager); this.closeButton = new ImageButton(game, guiManager); #endregion #region Add Child Controls base.Add(this.box); base.Add(this.viewPort); base.Add(this.titleBar); base.Add(this.label); base.Add(this.movableArea); #endregion #region Add Resizable Areas this.resizableAreas = new ResizableArea[8]; for (int i = 0; i < 8; i++) { this.resizableAreas[i] = new ResizableArea(game, guiManager); this.resizableAreas[i].ZOrder = 0.3f; this.resizableAreas[i].StartResizing += new StartResizingHandler(OnStartAnimating); this.resizableAreas[i].EndResizing += new EndResizingHandler(OnEndAnimating); base.Add(this.resizableAreas[i]); } this.resizableAreas[0].ResizeArea = ResizeAreas.TopLeft; this.resizableAreas[1].ResizeArea = ResizeAreas.Top; this.resizableAreas[2].ResizeArea = ResizeAreas.TopRight; this.resizableAreas[3].ResizeArea = ResizeAreas.Left; this.resizableAreas[4].ResizeArea = ResizeAreas.Right; this.resizableAreas[5].ResizeArea = ResizeAreas.BottomLeft; this.resizableAreas[6].ResizeArea = ResizeAreas.Bottom; this.resizableAreas[7].ResizeArea = ResizeAreas.BottomRight; #endregion #region Set Non-Default Properties this.movableArea.ZOrder = 0.1f; this.closeButton.ZOrder = 0.4f; this.viewPort.ZOrder = 0.2f; this.viewPort.CanHaveFocus = true; MinWidth = 150; #endregion #region Set Default Properties Margin = defaultMargin; HasCloseButton = defaultHasCloseButton; HasFullWindowMovableArea = defaultFullWindowMovableArea; TitleBarHeight = defaultTitleBarHeight; Width = MinWidth; Height = MinHeight; ButtonSize = defaultButtonSize; Skin = defaultSkin; TitleBarSkin = defaultTitleBarSkin; CloseButtonSkin = defaultCloseButtonSkin; CloseButtonHoverSkin = defaultCloseButtonHoverSkin; CloseButtonPressedSkin = defaultCloseButtonPressedSkin; #endregion #region Event Handlers this.closeButton.Click += new ClickHandler(OnClose); this.movableArea.StartMoving += new StartMovingHandler(OnStartAnimating); this.movableArea.EndMoving += new EndMovingHandler(OnEndAnimating); this.backgroundMovableArea.StartMoving += new StartMovingHandler(OnStartAnimating); this.backgroundMovableArea.EndMoving += new EndMovingHandler(OnEndAnimating); #endregion Refresh(); }
/// <summary> /// Constructor. /// </summary> /// <param name="game">The currently running Game object.</param> /// <param name="guiManager">GUIManager that this control is part of.</param> /// <param name="graphics">Graphics device manager to change settings.</param> public DisplaySettingsDialog(Game game, GUIManager guiManager, GraphicsDeviceManager graphics) : base(game, guiManager) { #region Create Child Controls this.fullscreenCheckBox = new CheckBox(game, guiManager); this.resolutionLabel = new Label(game, guiManager); this.resolutionCombo = new ComboBox(game, guiManager); this.OKButton = new TextButton(game, guiManager); this.cancelButton = new TextButton(game, guiManager); this.applyButton = new TextButton(game, guiManager); #endregion #region Add Child Controls Add(this.fullscreenCheckBox); Add(this.resolutionLabel); Add(this.resolutionCombo); Add(this.OKButton); Add(this.cancelButton); Add(this.applyButton); #endregion this.graphics = graphics; // Set checkbox to current value if (graphics.IsFullScreen) this.fullscreenCheckBox.IsChecked = true; // Populate combobox this.resolutionCombo.AddEntry("640x480"); this.resolutionCombo.AddEntry("800x600"); this.resolutionCombo.AddEntry("1024x768"); this.resolutionCombo.AddEntry("1280x800"); this.resolutionCombo.AddEntry("1280x960"); this.resolutionCombo.AddEntry("1280x1024"); // Set combobox to current value if (graphics.PreferredBackBufferWidth == 640 && graphics.PreferredBackBufferHeight == 480) this.resolutionCombo.SelectedIndex = 0; else if (graphics.PreferredBackBufferWidth == 800 && graphics.PreferredBackBufferHeight == 600) this.resolutionCombo.SelectedIndex = 1; else if (graphics.PreferredBackBufferWidth == 1024 && graphics.PreferredBackBufferHeight == 768) this.resolutionCombo.SelectedIndex = 2; else if (graphics.PreferredBackBufferWidth == 1280 && graphics.PreferredBackBufferHeight == 800) this.resolutionCombo.SelectedIndex = 3; else if (graphics.PreferredBackBufferWidth == 1280 && graphics.PreferredBackBufferHeight == 960) this.resolutionCombo.SelectedIndex = 4; else if (graphics.PreferredBackBufferWidth == 1280 && graphics.PreferredBackBufferHeight == 1024) this.resolutionCombo.SelectedIndex = 5; // Child settings TitleText = "Display Settings"; this.fullscreenCheckBox.Text = "Fullscreen"; this.resolutionLabel.Text = "Select desired display resolution"; this.resolutionLabel.Width = this.resolutionLabel.TextWidth; this.resolutionCombo.IsEditable = false; this.OKButton.Text = "OK"; this.cancelButton.Text = "Cancel"; this.applyButton.Text = "Apply"; Resizable = false; // Layout this.fullscreenCheckBox.X = LargeSeperation; this.fullscreenCheckBox.Y = LargeSeperation; this.resolutionLabel.X = LargeSeperation; this.resolutionLabel.Y = this.fullscreenCheckBox.Y + this.fullscreenCheckBox.Height + LargeSeperation; this.resolutionCombo.X = LargeSeperation; this.resolutionCombo.Y = this.resolutionLabel.Y + this.resolutionLabel.Height + SmallSeperation; this.OKButton.Y = this.resolutionCombo.Y + this.resolutionCombo.Height + (LargeSeperation * 2); this.cancelButton.Y = this.OKButton.Y; this.applyButton.Y = this.OKButton.Y; // Check if buttons require more space int buttonsWidth = this.OKButton.Width + this.cancelButton.Width + this.applyButton.Width + (2 * SmallSeperation); if (buttonsWidth > this.resolutionCombo.Width) { this.resolutionCombo.Width = buttonsWidth; ClientWidth = buttonsWidth + (2 * LargeSeperation); } else ClientWidth = this.resolutionCombo.Width + (2 * LargeSeperation); this.ClientHeight = this.applyButton.Y + this.applyButton.Height + LargeSeperation; // Align buttons to the right of the window this.OKButton.X = ClientWidth - this.OKButton.Width - LargeSeperation; this.cancelButton.X = OKButton.X - this.cancelButton.Width - SmallSeperation; this.applyButton.X = cancelButton.X - this.cancelButton.Width - SmallSeperation; #region Event Handlers this.OKButton.Click += new ClickHandler(OnOK); this.cancelButton.Click += new ClickHandler(OnCancel); this.applyButton.Click += new ClickHandler(OnApply); #endregion }