Styling Elements

Ember comes with a number of ‘UI styles’ built-in. Each style is stored as a submodule of ember.style, and must be imported explicitly to be used. Each style contains a number of Element classes similar to those found under ember.ui. These classes inherit from ember.ui elements, and extend them to provide additional functionality.

You can import a UI style as shown below. The only style available right now is pixel_dark, which is a pixel-art style. By release, we’ll have a wider range of both pixel-art and non-pixel-art styles to choose from.

from ember.style import pixel_dark as ui

In this chapter, we’ll cover how to use the built-in styles. In later chapters, we’ll cover how you can create styles yourself.

Generic setup

The pixel_dark style is a pixel art style. Because the pixel art is rendered at a small scale, we’ll have to create an intermediate surface to draw the UI on, which we will then scale and draw to the display surface. This is standard practice when creating pixel art games in Pygame.

Here’s an example showing how you might do this. Note the ember.set_display_zoom call, which is highlighted. This tells ember how much it should scale the mouse position readings by. It does not affect how ember renders the UI.

Every style has a background_color attribute that you may wish to use as a background for your menus.

 1 import pygame
 2 import ember
 3 from ember.style import pixel_dark as ui
 4
 5 pygame.init()
 6 clock = pygame.time.Clock()
 7
 8 ember.init()
 9 ember.set_clock(clock)
10
11 ZOOM = 3
12 DISPLAY_SIZE = (150, 100)
13
14 screen = pygame.display.set_mode((DISPLAY_SIZE[0]*ZOOM, DISPLAY_SIZE[1]*ZOOM))
15 display = pygame.Surface(DISPLAY_SIZE)
16 ember.set_display_zoom(ZOOM)
17
18 running = True
19 while running:
20     for event in pygame.event.get():
21         if event.type == pygame.QUIT:
22             running = False
23
24     display.fill(ui.background_color)
25     screen.blit(pygame.transform.scale(display, screen.get_size()), (0,0))
26     clock.tick(60)
27     pygame.display.flip()
28
29 pygame.quit()

Text

The ui.Text class inherits from ember.Text. Unlike ember.Text, you don’t need to specify a font for this element. The default value for the font parameter is a pixel-art font that fits the style. This makes creating many Text objects much more concise!

with ember.View() as view:
    ui.Text("Hello, world!")
_images/text.png

Button

Previously, we’ve created buttons with backgrounds like so:

font = ember.PygameFont("arial", 40)

with ember.View() as view:
    with ember.Button(size=(200, 50)):
        ember.Panel("red")
        ember.Text("Click me!", color="white", font=font)

ui.Button makes this syntax much simpler. It creates a Panel internally when you create the button, so that you don’t have to specify it yourself.

Here’s what our syntax looks like now, by using both the ui.Text ui.Button elements.

with ember.View() as view:
    with ui.Button():
        ui.Text("Click me!")
_images/button.png

We’ve only seen how to apply solid colors to a Panel so far, but there are several other options too. ui.Button uses a more advanced type of Panel that renders a pygame.Surface texture rather than a solid color. We’ll look more at this later.

Note

The default size for the basic ember.Button element that we looked at previously is ember.FIT, which means that it will shrink to fit the size of it’s contents by default. ui.Button has a different default size of 70 x 21 pixels, which is a size that looks nice at this scale.

If you don’t like the default button size and want to set a new default, it’s easy to do so:

ui.Button.w.default_value = 100
ui.Button.h.default_value = 20

This works on every other element too.

This is much cleaner, right? But we can improve this even more! If you pass a string to the ui.Button constructor, it’ll create an instance of ui.Text for you!

with ember.View() as view:
    ui.Button("Click me!")

ToggleButton

ember.ToggleButton is a subclass of ember.Button, and adds an active property that is toggled between True and False by the button when it is clicked.

The pixel_dark style currently offers two different subclasses of ember.ToggleButton - ui.ToggleButton and ui.Switch.

with ember.View() as view:
    with ember.VStack(spacing=6):
        ui.Button("Click me!")
        ui.ToggleButton("Click me!")
        ui.Switch()
_images/toggle_button.png

Because these element types are subclasses of ember.Button, you can listen for ember.CLICKEDDOWN events to detect when they are clicked. In addition, you can listen for ember.TOGGLEDON and ember.TOGGLEDOFF to detect specific states.

Stacks

Styles provide subclasses of ember.VStack and ember.HStack too. In the case of pixel_dark, the minimum spacing of the Stacks has been increased from 0 to 6.

You can add ui.Divider elements to nicely separate elements within stacks. The orientation of the Divider is dependent on whether it is contained within ember.VStack or ember.HStack.

Here’s a more complex UI:

with ember.View() as view:
    with ui.VStack(w=140):
        ui.Text("Options")
        ui.Divider()
        for i in range(1, 4):
            with ui.HStack(w=ember.FILL):
                ui.Text(f"Option {i}")
                ui.Switch()
        ui.Divider()
        with ui.HStack(w=ember.FILL):
            ui.Button("Cancel", w=ember.FILL)
            ui.Button("Save", w=ember.FILL)
_images/divider.png