platform for developing on SQFMI's Watchy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

263 lines
9.0 KiB

#ifndef _MODULE_UI_h
#define _MODULE_UI_h
#include <Arduino.h>
#include "watchos_consts.h"
#include "watchos_hw.h"
#include "watchos_types.h"
#include "Task.h"
#include "Module_Power.h"
#include "IRunnable.h"
#include "IDrawable.h"
// The maximum number of windows that can be created within the UI system.
#define MODULE_UI_MAX_WINDOWS 32
// The maximum number of fonts that can be registered with the UI system.
#define MODULE_UI_MAX_FONTS 32
/// <summary>
/// How to lay out the children of a given window
/// </summary>
enum ui_layout_mode : byte {
/// <summary>
/// Perform no layout, all children will overlap
/// </summary>
NONE = 0,
/// <summary>
/// Split vertically, windows laid out left to right
/// </summary>
SPLIT_VERTICAL = 1,
/// <summary>
/// Split horizontally, windows laid out top to bottom
/// </summary>
SPLIT_HORIZONTAL = 2,
/// <summary>
/// Manually lay out windows, each child must have its position and size set
/// </summary>
ABSOLUTE = 3
};
struct UiWindow;
struct UiFont;
/// <summary>
/// Module to handle layout out and putting things on the screen
/// </summary>
class Module_UI : public Task, public IRunnable
{
/// <summary>
/// Reference to the power module so we can disable sleep while draws are in progress; the display chip
/// doesn't like it when you kill power mid-draw
/// </summary>
Module_Power* module_power;
/// <summary>
/// All registered/created windows
/// </summary>
UiWindow* m_window[MODULE_UI_MAX_WINDOWS];
/// <summary>
/// All fonts registered with the UI system
/// </summary>
UiFont* m_font[MODULE_UI_MAX_FONTS];
/// <summary>
/// Whether we've initialized the graphics hardware
/// </summary>
/// <remarks>
/// This has to be done after every power-on (reset or deep sleep), but we do it lazily since it's often
/// not necessary and is time consuming.
/// </remarks>
bool m_initialized = false;
/// <summary>
/// Whether any windows have been marked as "dirty" (requiring a redraw) since the last draw
/// </summary>
bool m_dirty = false;
/// <summary>
/// Whether we desire a full-screen redraw at the next available opportunity
/// </summary>
bool m_redraw = false;
/// <summary>
/// Handle to the root window
/// </summary>
/// <remarks>
/// We only dirty / draw windows that exist somewhere underneath the root window
/// </remarks>
kernel_handle_t m_root;
// We track these values so methods like print/fill/etc can be aware of context when called by
// tasks and automatically draw within their allocated space.
/// <summary>
/// The X offset of the window we're currently drawing
/// </summary>
int m_draw_x;
/// <summary>
/// The Y offset of the window we're currently drawing
/// </summary>
int m_draw_y;
/// <summary>
/// The width of the window we're currently drawing
/// </summary>
int m_draw_w;
/// <summary>
/// The height of the window we're currently drawing
/// </summary>
int m_draw_h;
/// <summary>
/// Draw a window to screen, including all of its children
/// </summary>
/// <param name="window">window to draw</param>
/// <param name="x">x offset on screen to start at</param>
/// <param name="y">y offset on screen to start at</param>
/// <param name="width">width of the space to draw within</param>
/// <param name="height">height of the space to draw within</param>
void draw_window(UiWindow* window, int x, int y, int width, int height);
void local_to_screen(int* x, int* y);
void get_screen_coordinates(UiWindow* window, int* x, int* y);
public:
Module_UI();
/// <summary>
/// Create a new window
/// </summary>
/// <param name="drawable">drawable object</param>
/// <param name="parent">handle of a window to create this window under, WATCHOS_HANDLE_NULL for a root window</param>
/// <returns>handle of the newly created window</returns>
kernel_handle_t createWindow(IDrawable* drawable, kernel_handle_t parent);
/// <summary>
/// Create a new placeholder window -- one that can have children, but is not drawn itself
/// </summary>
/// <param name="parent">handle of the window to create this window under, WATCHOS_HANDLE_NULL for a root window</param>
/// <returns>handle of the newly created window</returns>
kernel_handle_t createWindow(kernel_handle_t parent);
/// <summary>
/// Get the handle of the current root window
/// </summary>
/// <returns>handle of the current root window</returns>
kernel_handle_t getRoot();
/// <summary>
/// Set the current root window
/// </summary>
/// <remarks>
/// Triggers a full screen redraw
/// </remarks>
/// <param name="handle">handle of the window that should be the root window</param>
void setRoot(kernel_handle_t handle);
void initialize();
void start();
void tick();
/// <summary>
/// Trigger a draw
/// </summary>
/// <remarks>
/// Will either perform the draw synchronously (if multicore is disabled) or spawns a task
/// on the other core to run the draw asynchronously.
/// </remarks>
void draw();
/// <summary>
/// Internal draw call, either called by draw() or indirectly if spawned on another core
/// </summary>
void do_draw();
/// <summary>
/// Set the layout mode of a window
/// </summary>
/// <param name="window_handle">handle of the window to set the layout mode of</param>
/// <param name="layout_mode">one of the layout mode enum</param>
void setLayoutMode(kernel_handle_t window_handle, ui_layout_mode layout_mode);
/// <summary>
/// Set the absolute position of an absolutely positioned window relative to parent
/// </summary>
/// <remarks>
/// This only has any effect if the parent window is set to absolute layout
/// </remarks>
/// <param name="window_handle">handle to set the location of</param>
/// <param name="x">x offset from parent</param>
/// <param name="y">y offset from parent</param>
/// <param name="w">width of window</param>
/// <param name="h">height of window</param>
void setAbsolutePosition(kernel_handle_t window_handle, byte x, byte y, byte w, byte h);
/// <summary>
/// Mark the window as dirty, requiring a draw
/// </summary>
/// <param name="window_handle">handle of window to mark dirty</param>
void setDirty(kernel_handle_t window_handle);
/// <summary>
/// Get the width of the current window
/// </summary>
/// <returns>width in pixels</returns>
int getWidth();
/// <summary>
/// Get the height of the current window
/// </summary>
/// <returns>height in pixels</returns>
int getHeight();
/// <summary>
/// Fill the current window with the given colour
/// </summary>
/// <param name="colour">colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
void fill(uint16_t colour = COLOUR_SECONDARY);
/// <summary>
/// Fill a portion of the current window with the given colour
/// </summary>
/// <param name="x">x offset within window</param>
/// <param name="y">y offset within window</param>
/// <param name="width">width of area to fill</param>
/// <param name="height">height of area to fill</param>
/// <param name="colour">colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
void fill(int x, int y, int width, int height, uint16_t colour = COLOUR_SECONDARY);
/// <summary>
/// Print text to the screen
/// </summary>
/// <param name="x">x offset within window to place bottom left corner of text</param>
/// <param name="y">y offset within window to place bottom left corner of text</param>
/// <param name="str">text to write</param>
/// <param name="colour">colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
/// <param name="font">pointer to GFXfont to use for text</param>
void print(int x, int y, char* str, uint16_t colour, void* font);
/// <summary>
/// Print text to the screen
/// </summary>
/// <param name="x">x offset within window to place bottom left corner of text</param>
/// <param name="y">y offset within window to place bottom left corner of text</param>
/// <param name="str">text to write</param>
/// <param name="colour">colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
/// <param name="font_id">id of a registered font</param>
void print(int x, int y, char* str, uint16_t colour = COLOUR_PRIMARY, byte font_id = 0);
void rectangle(int x, int y, int w, int h, uint16_t colour = COLOUR_PRIMARY);
void fillRectangle(int x, int y, int w, int h, uint16_t colour = COLOUR_PRIMARY);
void line(int x0, int y0, int x1, int y1, uint16_t colour = COLOUR_PRIMARY);
void horizontalLine(int y, uint16_t colour = COLOUR_PRIMARY);
void verticalLine(int x, uint16_t colour = COLOUR_PRIMARY);
/// <summary>
/// Register a font with an id
/// </summary>
/// <remarks>
/// This can be passed to print(), avoids having to pull in the entire GFX library in order to
/// print text.
/// </remarks>
/// <param name="id">id to register the font with</param>
/// <param name="font">a pointer to a GFXfont</param>
void registerFont(byte id, const void* font);
};
#endif