Myra - UI Library for the MonoGame

Released Myra 0.6.6!
List of changes:

  1. Added transparency. Now Widget class has Opacity property which can take values from 0.0 to 1.0(default value is 1.0). If both container widget and its child have that property set then they are multiplied. I.e. if container opacity is 0.5 and its child opacity is 0.6. Then the child will be rendered with opacity 0.3.
  2. Added yet another sample FantasyMapGenerator, which is slightly more complicated than rest of samples. It provides utility for generation of fantasy continents:
  3. Some bug fixes.

I want to remind that all samples executables are part of the binary distribution.
Which is located here: https://github.com/rds1983/Myra/releases/download/0.6.6.130/Myra.0.6.6.130.zip

NuGet had been updated as well.

2 Likes

Hey, so I’ve been using Myra in my latest project (by the way, amazing framework, really easy to use and works well). But I’ve noticed something that could be a small bug (or I’m just doing things incorrectly), but my game has stuff that needs to be rendered under the UI, so the obvious first thing to do would be to change the order in which things are rendered. But even after changing the order, Myra still gets rendered under all my sprites for some reason. Is this a bug of Myra or am I doing something incorrectly? Here’s my code:

spriteBatch.Draw(background, new Rectangle(0, 0, 256, 256), Color.White);
_host.Bounds = new Rectangle(0, 0, 256, 256);
_host.Render();

As you can see, Myra is being rendered after everything else (yes, I checked the render order in spriteBatch.Begin(), it is correct.) but yet Myra is still getting rendered under the background sprite.

Hi,
Thanks for the kind words!
I think, the reason of that issue is Myra using it’s own SpriteBatch.
So your sprites are rendered when you call spriteBatch.End(), which happens after Myra.Render. That’s why your sprites on top of the UI.
To fix that issue you may try to do following:

spriteBatch.Draw(background, new Rectangle(0, 0, 256, 256), Color.White);
spriteBatch.End();
_host.Bounds = new Rectangle(0, 0, 256, 256);
_host.Render();

Thank you for the help! That fixed the issue.

Myra 0.6.7 is out!
List of changes:

  1. Added two more simple widgets: HorizontalSeparator and VerticalSeparator.
  2. Fixed #33
  3. Added ability to customize what parameters is passed to the Myra’s SpriteBatch.Begin. I.e. following code will make ui 1.5x scaled:
  _desktop = new Desktop();
  _desktop.SpriteBatchBeginParams.TransformMatrix = Matrix.CreateScale(1.5f);

Link to the binary release: https://github.com/rds1983/Myra/releases/download/0.6.7.133/Myra.0.6.7.133.zip
NuGet was updated as well!

1 Like

Released Myra 0.7.0.0
List of changes:

  • Added ColorPickerDialog
  • Added support for the MonoGame 3.7

Link to the binary release: https://github.com/rds1983/Myra/releases/download/0.7.0.137/Myra.0.7.0.137.zip
NuGet was updated as well!

2 Likes

Myra 0.7.1 is out.
CustomUIStylesheet sample had been customized so now it uses “commodore 64” skin:

The skin had been borrowed from here: https://github.com/czyzby/gdx-skins

New version binary distribution link: https://github.com/rds1983/Myra/releases/download/0.7.1.139/Myra.0.7.1.139.zip

Myra project site: https://github.com/rds1983/Myra

2 Likes

Hi Roman…

I have an issue with using Myra’s menu as an overlay to my game-board. Please note the menu system I have put in place in the image below…

The master section of the menu goes the width of my map-board. As you can see I have one master menu option with a sub-option within it.

What happens in this case is that every time I make a selection of either menu option, the Update Event is fired, which in turn causes the “Draw Event” to fire, prior to the actual functionality of either menu option taking place.

As a result of this sequence of events, the hexagon tile selection task is always being processed, which then causes a highlighted hex to be overdrawn over the original selected hexagon tile in the wrong area of the map board. This is causing processing and screen handling issues since when selecting a menu option in this scenario, I do not want an Update Event to fire.

Is there a way to work around this (avoiding the Update event from firing or circumventing it) or have I set up Myra incorrectly for the way I want to use your interface library?

Thank you… :slight_smile:

Hi Steve!
Sorry, I didnt understand the issue. Which object fires Update and Draw events?
Also what Myra version do you use?

Hi Roman…

I am using an older version of Myra; version .0.4.6.82…

The issue I am experiencing is that when a Myra menu option is selected, the MonoGame Engine is also interpreting this to be a mouse-press on the actual map-board or the MonoGame screen. As a result, the MonoGame Update Event is fired, which in turn fires the Draw Event, both of which look to find a hexagonal tile that the user has selected. However, in this scenario I don;t want these processes to run since I am only looking to process the menu option event.

How then do I separate the menu option selection from these two event noted above?

I got it, you want to suppress MonoGame Engine mouse events if mouse is over Myra menu.
Probably easiest way would be to change the Update Event handler to ignore processing if mouse is over one of Myra’s widgets.
The code would look like following:

private void UpdateHandler(object sender, EventArgs args)
{
        var isMouseOverGUI = false;
	foreach(var widget in _desktop.Widgets)
	{
		if (widget.IsMouseOver)
		{
			isMouseOverGUI = true;
		}
	}

       if (isMouseOverGUI) return;
       ...
}

Let me know, if this helped.

Thank you so much, Roman… :slight_smile:

I will give your coding suggestion a try after lunch…

Thank you again…

Hi Roman…

I tried your suggestion and several variations on it, including testing for a left-mouse button. However, I could not seem to prevent the Update\Draw Events from firing when selecting a menu option.

If you look at the extracted image from my map-board shown below you sill see that the master menu option (TEST FUNCTIONS & CLEAR SELECTED HEXAGONS) have both been selected. Under the word, HEXAGONS, you will note that the hexagon underneath the displayed menu options is highlighted in yellow\orange. This is because when selecting from the menu, the mouse point selected on the actual map-board is still being processed by the selected-hexagon routines.

This is what I am trying to avoid so that when the menu options are selected only the processing for the menu is being run…

Thank you again for all your help… :slight_smile:

Steve Naidamast

Hi Steve!
Well, my suggestion wasnt about preventing Update/Draw Events, but about changing its handlers logic so if mouse pointer is over Myra menu than the event processing is interrupted by calling return.

Frankly I still dont understand what are those Update/Draw Events. As MonoGame framework doesnt have it, but it has Game.Update and Game.Draw methods. Which are called every game tick.

Hi Roman…

Thank you for your reply…

Just to clarify, I learned events as being any routine that is called by a timer or a user action. Since MonoGame’s Update and Draw routines are fired with every “tick” of an internal clock, to me they would be considered timer-events.

However, we are simply talking about semantics here. Nonetheless, it appears that we have no control over the firing of any of the MonoGame events\methods as they are designed to process on their own making them the cyclic aspect of the MonoGame processing infrastructure. I believe this the way all such game engines process their major level tasks.

Simply forcing a return within either the Update and\or Draw events\methods will not do anything to prevent the testing for a mouse-click within these events\methods. This is due to the following code I implemented in the Update routine…

            if (coMouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
            {
                Find_MouseSelectedHex(ref loHexTile);

                coSelectedHexTile = new HexMapEngine.Structures.HexTile();   
                coSelectedHexTile = loHexTile;

                coSelectedHexTile.HEX_TILE_SELECTED = true;
            }

In this case, the state of the left mouse-button is being maintained until it changes through another user action (ie: release). As a result, no matter whether the Myra component event fires first or the Update\Draw events fire, the state of the mouse-button is being maintained and will eventually be processed for at least one cycle.

Since your menu-component allows for a user click to be passed through to the underlying screen, my code above will always process the mouse-button state as the last one created.

The optimum solution is to find a way in which a mouse-click on an overlaying menu item is compartmentalized from the rest of the MonoGame infrastructure. However, given the nature of how the Myra events are being processed as secondary in this situation, I am not sure if this is possible.

Is there a way in which the Myra components can be self-contained? In other words, separate them from the MonoGame internal infrastructure so that whatever happens on the Myra components as it regards user-actions have no effect on the underlying screen.

Steve Naidamast

Hi Steve!
Thanks for the clarification. I thought you were talking about some sort of C# events, which caused some confusion.
Well, I still think that you simply need to prevent processing mouse input if the mouse pointer is over Myra menu-component.
To make that first of all add following method:

private bool IsMouseOverGUI()
{
        var isMouseOverGUI = false;
	foreach(var widget in _desktop.Widgets)
	{
		if (widget.IsMouseOver)
		{
			isMouseOverGUI = true;
                        break;
		}
	}

        return isMouseOverGUI;
}

Where _desktop is Myra’s Desktop (it is named _host in some samples).
Then add call to the above method to your code:

	if (coMouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed && !IsMouseOverGUI())
	{
		Find_MouseSelectedHex(ref loHexTile);
		coSelectedHexTile = new HexMapEngine.Structures.HexTile();
		coSelectedHexTile = loHexTile;
		coSelectedHexTile.HEX_TILE_SELECTED = true;
	}

As for separating Myra’s input processing from the rest of MonoGame infrastructure. Well, current Myra versions doesnt allow that. In future prob I could hack into Mouse object and erase mouse’s state if mouse click had been processed by Myra. Though I am unsure if it’s correct approach for such problem.It definitely requires more research.

Myra 0.7.2 is out!
UI Form Serialization format had been changed to the human readable xml.
Now Myra UI Editor could be used in conjunction with a text editor(click on the below animated gif to see it):

Also I’ve wrote wiki entry describing Myra UI Form Serialization:

Link to the latest binary release distribution: https://github.com/rds1983/Myra/releases/download/0.7.2.142/Myra.0.7.2.142.zip

Myra Project Site: https://github.com/rds1983/Myra

2 Likes

Hi Roman…

I have been trying multiple variations on your recommended coding suggestion with no success. I believe I have found the reasons why, which has to do with the way your internal object structures for the menu reflect their information.

The top-most structure that is used as the container for your menu is the Desktop structure. As my code shows below, the attributes and properties of this structure are fairly generic…

>>>
?coMyraUIDesktop
{Myra.Graphics2D.UI.Desktop}
Bounds: 0 0 0 0
ContextMenu: null
FocusedWidget: null
KeyboardState: {Microsoft.Xna.Framework.Input.KeyboardState}
ModalWidget: null
MousePosition: 0 0
MouseState: {Microsoft.Xna.Framework.Input.MouseState}
MouseWheel: 0
Widgets: Count = 1
_widgets: Count = 1
<<<

The next structure, which actually defines the container for the menu is the Grid structure as shown below…

>>> (At generation of menu components)
?coMyraUIDesktop.Widgets[0]
{Myra.Graphics2D.UI.Grid}
AbsoluteBounds: 0 0 0 0
AbsoluteLocation: 0 0
AcceptsTab: false
Background: null
Border: null
Bounds: 0 0 0 0
CanFocus: false
ChildCount: 1
Children: Count = 1
ClipToBounds: false
ColumnSpacing: 1
ColumnsProportions: Count = 1
Desktop: {Myra.Graphics2D.UI.Desktop}
DisabledBackground: null
DisabledBorder: null
DisabledOverBackground: null
DrawLines: false
DrawLinesColor: 255 255 255 255
Enabled: true
GridPositionX: 0
GridPositionY: 0
GridSpanX: 1
GridSpanY: 1
HeightHint: null
HorizontalAlignment: Stretch
Id: null
IsFocused: false
IsMouseOver: false
LayoutBounds: 0 0 0 0
Location: 0 0
MouseButtonsDown: null
OverBackground: null
OverBorder: null
PaddingBottom: 0
PaddingHeight: 0
PaddingLeft: 0
PaddingRight: 0
PaddingTop: 0
PaddingWidth: 0
Parent: null
RenderBounds: 0 0 0 0
RowSpacing: 1
RowsProportions: Count = 1
Size: 0 0
Tag: null
TotalColumnsPart: null
TotalRowsPart: null
VerticalAlignment: Stretch
Visible: true
Widgets: Count = 1
WidthHint: null
XHint: 0
YHint: 0
_widgets: Count = 1
<<<

>>> (at runtime when menu is displayed)
?coMyraUIDesktop.Widgets[0]
{Myra.Graphics2D.UI.Grid}
AbsoluteBounds: 0 0 1200 1000
AbsoluteLocation: 0 0
AcceptsTab: false
Background: null
Border: null
Bounds: 0 0 1200 1000
CanFocus: false
ChildCount: 1
Children: Count = 1
ClipToBounds: false
ColumnSpacing: 1
ColumnsProportions: Count = 1
Desktop: {Myra.Graphics2D.UI.Desktop}
DisabledBackground: null
DisabledBorder: null
DisabledOverBackground: null
DrawLines: false
DrawLinesColor: 255 255 255 255
Enabled: true
GridPositionX: 0
GridPositionY: 0
GridSpanX: 1
GridSpanY: 1
HeightHint: null
HorizontalAlignment: Stretch
Id: null
IsFocused: false
IsMouseOver: true
LayoutBounds: 0 0 1200 1000
Location: 0 0
MouseButtonsDown: null
OverBackground: null
OverBorder: null
PaddingBottom: 0
PaddingHeight: 0
PaddingLeft: 0
PaddingRight: 0
PaddingTop: 0
PaddingWidth: 0
Parent: null
RenderBounds: 0 0 1200 1000
RowSpacing: 1
RowsProportions: Count = 1
Size: 1200 1000
Tag: null
TotalColumnsPart: null
TotalRowsPart: null
VerticalAlignment: Stretch
Visible: true
Widgets: Count = 1
WidthHint: null
XHint: 0
YHint: 0
_widgets: Count = 1
<<<

If you notice the RenderBounds between the when the grid structure is created and when it is finally displayed, you will note that the dimensions change from “0,0,0,0” to that of “0,0,1200,1000”. This appears to mean that the display grid will cover a much larger area of the screen than the actual displayed components take. This also appears to mean that the capturing of mouse-over events can be trapped at the much larger grid-level than should be. I have been able to test this by selecting a point on the displayed screen outside the bounds of the actually displayed menu and yet the function that validates for a mouse-over test for the menu returns “true” when it should be false.

In addition, I have not been able to easily drill down further into the menu’s internal structure to be able to test the actual, individual menu component structures for similar properties. And yet, even if I can, these similar properties appear to be unaffected by any mouse-over events.

For example the structure for the actual “Horizontal Menu” has the following attributes and properties…

>>>
?coMyraUIDesktop.Widgets[0].FindWidgetById(“MyraMasterMenu”)
{Myra.Graphics2D.UI.HorizontalMenu}
AbsoluteBounds: 0 0 0 0
AbsoluteLocation: 0 0
AcceptsTab: false
Background: {button {X:309 Y:162 Width:12 Height:20}}
Border: null
Bounds: 0 0 0 0
CanFocus: false
ChildCount: 1
Children: Count = 1
ClipToBounds: false
ColumnSpacing: 0
ColumnsProportions: Count = 1
Desktop: {Myra.Graphics2D.UI.Desktop}
DisabledBackground: null
DisabledBorder: null
DisabledOverBackground: null
DrawLines: false
DrawLinesColor: 255 255 255 255
Enabled: true
GridPositionX: 0
GridPositionY: 0
GridSpanX: 1
GridSpanY: 1
HeightHint: null
HorizontalAlignment: Stretch
Id: “MyraMasterMenu”
IsFocused: false
IsMouseOver: false
Items: Count = 1
LayoutBounds: 0 0 0 0
Location: 0 0
MenuItemStyle: {Myra.Graphics2D.UI.Styles.MenuItemStyle}
MouseButtonsDown: null
OpenMenuItem: null
Orientation: Horizontal
OverBackground: null
OverBorder: null
PaddingBottom: 0
PaddingHeight: 0
PaddingLeft: 0
PaddingRight: 0
PaddingTop: 0
PaddingWidth: 0
Parent: {Myra.Graphics2D.UI.Grid}
RenderBounds: 0 0 0 0
RowSpacing: 0
RowsProportions: Count = 0
SeparatorStyle: {Myra.Graphics2D.UI.Styles.MenuSeparatorStyle}
Size: 0 0
Tag: null
TotalColumnsPart: null
TotalRowsPart: null
VerticalAlignment: Stretch
Visible: true
Widgets: Count = 1
WidthHint: 1200
XHint: 0
YHint: 0
_widgets: Count = 1
<<<

If you notice, the “IsMouseOver” boolean property is “false” for the “Horizontal Menu” structure even if one were to place the mouse directly over the main menu strip that is stretched across the entire top part of the screen.

If I have done my analysis correctly, these disparities are why your suggested code is not yielding the expected results.

This would be fine if such interfaces were to be defined on completely separate screens as is often shown for beginning game tutorials for 2D games. However, I am attempting to develop a rather complex simulation and to date the Myra UI is the top interface library I can find for MonoGame. And I have researched such libraries quite extensively.

I am hoping that with the information have provided you can provide a resolution to this issue as I really do not want to start looking for another interface library since I feel that the Myra UI has great potential for the MonoGame Development Community… :relaxed:

Steve Naidamast

Hi Steve,
So the Grid stretches over the whole screen, that is why it always returns IsMouseOver=true and my suggestion fails.
Well, in that case IsMouseGUI method should look like following:

private bool IsMouseOverGUI()
{
  if (_horizontalMenu.IsMouseOver)
  {
    return true;
  }

  if (_desktop.ContextMenu != null && _desktop.ContextMenu.IsMouseOver)
  {
    return true;
  }

  return false;
}

I realize that above code is not obvious, therefore I’ve created new ticket related to the discussed issue: #40

Thanks for bringing it!

Hi Roman…

I just sent you an email with the notes I posted yesterday and then noticed that you had responded in the forums here.

I will give your suggestions a try in a little while.

Thank you again for all your wonderful assistance in this… :relaxed: