converter sample application

This commit is contained in:
nakst 2021-09-24 09:39:49 +01:00
parent 209054f92e
commit ba4ff01e0f
11 changed files with 177 additions and 19 deletions

View File

@ -1,8 +1,8 @@
# **Essence** — An Operating System
![Screenshot of the OS running in an emulator, showing File Manager, and the new tab screen.](https://handmade.network/public/media/image/p-essence-s1.png)
![Screenshot of the OS running in an emulator, showing GCC running under the POSIX subsystem.](https://handmade.network/public/media/image/p-essence-s3.png)
![Screenshot of the OS running in an emulator, showing the shutdown dialog.](https://handmade.network/public/media/image/p-essence-s5.png)
![Screenshot of the operating system running in an emulator, showing File Manager, and the new tab screen.](https://handmade.network/public/media/image/p-essence-s1.png)
![Screenshot of the operating system running in an emulator, showing GCC running under the POSIX subsystem.](https://handmade.network/public/media/image/p-essence-s3.png)
![Screenshot of the operating system running in an emulator, showing the shutdown dialog.](https://handmade.network/public/media/image/p-essence-s5.png)
## Support
@ -90,4 +90,4 @@ If you want your project to target Essence, you need to generate the API header
Currently supported languages are 'c' (also works for C++), 'zig' and 'odin'.
There is currently no documentation for the API; for examples of how to use the API, consult the standard applications in the `apps/` folder of the source tree. A minimal application is demonstrated in `apps/hello.c` and `apps/hello.ini`. By placing your application's `.ini` file in the `apps/` folder, it will be automatically built by the build system.
There is currently no documentation for the API; for examples of how to use the API, consult the standard applications in the `apps/` folder of the source tree. Minimal sample applications are placed in the `apps/samples` folder. By placing your application's `.ini` file in the `apps/` folder, it will be automatically built by the build system.

135
apps/samples/converter.cpp Normal file
View File

@ -0,0 +1,135 @@
// Include the Essence system header.
#include <essence.h>
// Define the metrics for panelStack.
// These values are specified in pixels,
// but will be automatically scaled using the UI scaling factor.
const EsStyle stylePanelStack = {
.metrics = {
.mask = ES_THEME_METRICS_INSETS
| ES_THEME_METRICS_GAP_MAJOR,
.insets = ES_RECT_1(20), // Spacing around the contents.
.gapMajor = 15, // Spacing between items.
},
};
// Define the metrics for panelForm.
const EsStyle stylePanelForm = {
.metrics = {
.mask = ES_THEME_METRICS_GAP_MAJOR
| ES_THEME_METRICS_GAP_MINOR,
.gapMajor = 5, // Spacing between columns.
.gapMinor = 8, // Spacing between rows.
},
};
// Global variables.
EsTextbox *textboxRate;
EsTextbox *textboxAmount;
EsTextDisplay *displayResult;
void ConvertCommand(EsInstance *, EsElement *, EsCommand *) {
// Get the conversion rate and amount to convert from the textboxes.
double rate = EsTextboxGetContentsAsDouble(textboxRate);
double amount = EsTextboxGetContentsAsDouble(textboxAmount);
// Calculate the result, and format it as a string.
char result[64];
size_t resultBytes = EsStringFormat(result, sizeof(result), "Result: $%F", rate * amount);
// Replace the contents of the result textbox.
EsTextDisplaySetContents(displayResult, result, resultBytes);
}
void _start() {
// We're not using the C standard library,
// so we need to initialise global constructors manually.
_init();
while (true) {
// Receive a message from the system.
EsMessage *message = EsMessageReceive();
if (message->type == ES_MSG_INSTANCE_CREATE) {
// The system wants us to create an instance of our application.
// Call EsInstanceCreate with the message and application name.
EsInstance *instance = EsInstanceCreate(message, "Converter");
// Create a layout panel to draw the window background.
EsPanel *panelRoot = EsPanelCreate(
instance->window, // Make the panel the root element of the window.
ES_CELL_FILL // The panel should fill the window.
| ES_PANEL_V_SCROLL_AUTO // Automatically show a vertical scroll bar if needed.
| ES_PANEL_H_SCROLL_AUTO, // Automatically show a horizontal scroll bar if needed.
ES_STYLE_PANEL_WINDOW_BACKGROUND); // Use the window background style.
panelRoot->cName = "panelRoot";
// Create a vertical stack to layout the contents of window.
EsPanel *panelStack = EsPanelCreate(
panelRoot, // Add it to panelRoot.
ES_CELL_H_CENTER // Horizontally center it in panelRoot.
| ES_PANEL_STACK // Use the stack layout.
| ES_PANEL_VERTICAL, // Layout child elements from top to bottom.
&stylePanelStack);
// Add a second layout panel to panelStack to contain the elements of the form.
EsPanel *panelForm = EsPanelCreate(
panelStack, // Add it to panelStack.
ES_PANEL_TABLE // Use table layout.
| ES_PANEL_HORIZONTAL, // Left to right, then top to bottom.
&stylePanelForm);
// Set the number of columns for the panelForm's table layout.
EsPanelSetBands(panelForm, 2);
// Add a text display and textbox for the conversion rate to panelForm.
EsTextDisplayCreate(
panelForm, // Add it to panelForm. It will go in the first column.
ES_CELL_H_RIGHT, // Align it to the right of the column.
ES_STYLE_TEXT_LABEL, // Use the text label style.
"Amount per dollar:"); // The contents of the text display.
textboxRate = EsTextboxCreate(
panelForm, // Add it to panelForm. It will go in the second column.
ES_CELL_H_LEFT); // Align it to the left of the column.
// Set the keyboard focus on the rate textbox.
EsElementFocus(textboxRate);
// Add a text display and textbox for the conversion amount to panelForm.
EsTextDisplayCreate(panelForm, ES_CELL_H_RIGHT, ES_STYLE_TEXT_LABEL, "Value to convert ($):");
textboxAmount = EsTextboxCreate(panelForm, ES_CELL_H_LEFT);
// We want to add a convert button in the second column of next row.
// But the next element we create will go into the first column,
// so we create a spacer element first.
EsSpacerCreate(panelForm);
// Create the convert button.
EsButton *buttonConvert = EsButtonCreate(
panelForm, // Add it to the panelForm. It will go in the second column.
ES_CELL_H_LEFT // Align it to the left of the column.
| ES_BUTTON_DEFAULT, // Set it as the default button. Pressing Enter will invoke it.
0, // Automatically determine the style to use.
"Convert"); // The button's label.
// Set the command callback for the button.
// This is called when the button is invoked.
// That might be from the user clicking it,
// using keyboard input, or an automation API invoking it.
EsButtonOnCommand(buttonConvert, ConvertCommand);
// Add a horizontal line below panelForm.
EsSpacerCreate(
panelStack, // Add it to panelStack.
ES_CELL_H_FILL, // Fill the horizontal width of panelStack.
ES_STYLE_SEPARATOR_HORIZONTAL); // Use the horizontal separator style.
// Add a text display for the conversion result to panelStack.
displayResult = EsTextDisplayCreate(panelStack, ES_CELL_H_LEFT, ES_STYLE_TEXT_LABEL,
"Press \u201CConvert\u201D to update the result.");
// Keep receiving messages in a loop,
// so the system can handle input messages for the window.
}
}
}

View File

@ -0,0 +1,5 @@
[general]
name=Converter
[build]
source=apps/converter.cpp

View File

@ -1629,9 +1629,12 @@ void EsUndoClear(EsUndoManager *manager) {
}
}
void EsUndoPush(EsUndoManager *manager, EsUndoCallback callback, const void *item, size_t itemBytes) {
void EsUndoPush(EsUndoManager *manager, EsUndoCallback callback, const void *item, size_t itemBytes, bool setAsActiveUndoManager) {
EsMessageMutexCheck();
EsInstanceSetActiveUndoManager(manager->instance, manager);
if (setAsActiveUndoManager) {
EsInstanceSetActiveUndoManager(manager->instance, manager);
}
Array<uint8_t> *stack = manager->state == UNDO_MANAGER_STATE_UNDOING ? &manager->redoStack : &manager->undoStack;
@ -1651,8 +1654,10 @@ void EsUndoPush(EsUndoManager *manager, EsUndoCallback callback, const void *ite
manager->state = UNDO_MANAGER_STATE_NORMAL;
}
EsCommandSetDisabled(EsCommandByID(manager->instance, ES_COMMAND_UNDO), !manager->undoStack.Length());
EsCommandSetDisabled(EsCommandByID(manager->instance, ES_COMMAND_REDO), !manager->redoStack.Length());
if (((APIInstance *) manager->instance->_private)->activeUndoManager == manager) {
EsCommandSetDisabled(EsCommandByID(manager->instance, ES_COMMAND_UNDO), !manager->undoStack.Length());
EsCommandSetDisabled(EsCommandByID(manager->instance, ES_COMMAND_REDO), !manager->redoStack.Length());
}
if (manager->instance->undoManager == manager) {
InstanceSetModified(manager->instance, true);

View File

@ -1268,6 +1268,8 @@ EsRectangle UIGetTransitionEffectRectangle(EsRectangle bounds, EsTransitionType
}
void UIDrawTransitionEffect(EsPainter *painter, EsPaintTarget *sourceSurface, EsRectangle bounds, EsTransitionType type, double progress, bool to) {
// TODO Proper blending in the FADE transition.
if ((type == ES_TRANSITION_FADE_OUT && to) || (type == ES_TRANSITION_FADE_IN && !to)) {
return;
}
@ -2278,9 +2280,20 @@ void LayoutStackPrimary(EsPanel *panel, EsMessage *message) {
int available = horizontal ? hSpace : vSpace;
int perPush = LayoutStackDeterminePerPush(panel, available, horizontal ? vSpace : hSpace);
int secondary1 = horizontal ? insets.t : insets.l;
int secondary2 = horizontal ? bounds.b - insets.b : bounds.r - insets.r;
int position = horizontal ? (reverse ? insets.r : insets.l) : (reverse ? insets.b : insets.t);
bool anyNonHiddenChildren = false;
if (message->type == ES_MSG_LAYOUT) {
if (!horizontal && panel->scroll.enabled[0]) {
secondary2 += panel->scroll.limit[0];
} else if (horizontal && panel->scroll.enabled[1]) {
secondary2 += panel->scroll.limit[1];
}
}
for (uintptr_t i = 0; i < childCount; i++) {
EsElement *child = panel->GetChild(i);
if (child->flags & (ES_ELEMENT_HIDDEN | ES_ELEMENT_NON_CLIENT)) continue;
@ -2291,7 +2304,7 @@ void LayoutStackPrimary(EsPanel *panel, EsMessage *message) {
int width = (child->flags & ES_CELL_H_PUSH) ? perPush : child->GetWidth(vSpace);
if (reverse) {
relative = ES_RECT_4(bounds.r - position - width, bounds.r - position, insets.t, bounds.b - insets.b);
relative = ES_RECT_4(bounds.r - position - width, bounds.r - position, secondary1, secondary2);
} else {
relative = ES_RECT_4(position, position + width, insets.t, bounds.b - insets.b);
}
@ -2301,9 +2314,9 @@ void LayoutStackPrimary(EsPanel *panel, EsMessage *message) {
int height = (child->flags & ES_CELL_V_PUSH) ? perPush : child->GetHeight(hSpace);
if (reverse) {
relative = ES_RECT_4(insets.l, bounds.r - insets.r, bounds.b - position - height, bounds.b - position);
relative = ES_RECT_4(secondary1, secondary2, bounds.b - position - height, bounds.b - position);
} else {
relative = ES_RECT_4(insets.l, bounds.r - insets.r, position, position + height);
relative = ES_RECT_4(secondary1, secondary2, position, position + height);
}
position += height + gap;
@ -3392,7 +3405,7 @@ void EsElementSetCellRange(EsElement *element, int xFrom, int yFrom, int xTo, in
element->tableCell = cell;
}
void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount, EsPanelBand *columns, EsPanelBand *rows) {
void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount, const EsPanelBand *columns, const EsPanelBand *rows) {
EsMessageMutexCheck();
EsAssert(panel->flags & ES_PANEL_TABLE); // Cannot set the bands layout for a non-table panel.
EsHeapFree(panel->bands[0]);
@ -3407,11 +3420,11 @@ void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount, EsPane
if (rows && panel->bands[1]) EsMemoryCopy(panel->bands[1], rows, rowCount * sizeof(EsPanelBand));
}
void EsPanelSetBandsAll(EsPanel *panel, EsPanelBand *column, EsPanelBand *row) {
void EsPanelSetBandsAll(EsPanel *panel, const EsPanelBand *column, const EsPanelBand *row) {
EsMessageMutexCheck();
EsAssert(panel->flags & ES_PANEL_TABLE); // Cannot set the bands layout for a non-table panel.
EsPanelBand *templates[2] = { column, row };
const EsPanelBand *templates[2] = { column, row };
for (uintptr_t axis = 0; axis < 2; axis++) {
if (!templates[axis]) continue;
@ -3884,7 +3897,7 @@ EsButton *EsButtonCreate(EsElement *parent, uint64_t flags, const EsStyle *style
}
EsButtonSetCheck(button, (EsCheckState) (flags & 3), false);
button->MaybeRefreshStyle();
return button;
}

View File

@ -2291,7 +2291,7 @@ function void EsUndoEndGroup(EsUndoManager *manager);
function void EsUndoInvokeGroup(EsUndoManager *manager, bool redo);
function bool EsUndoPeek(EsUndoManager *manager, EsUndoCallback *callback, const void **item);
function void EsUndoPop(EsUndoManager *manager);
function void EsUndoPush(EsUndoManager *manager, EsUndoCallback callback, const void *item, size_t itemBytes);
function void EsUndoPush(EsUndoManager *manager, EsUndoCallback callback, const void *item, size_t itemBytes, bool setAsActiveUndoManager = true);
function bool EsUndoInUndo(EsUndoManager *manager);
function bool EsUndoIsEmpty(EsUndoManager *manager, bool redo);
function ES_INSTANCE_TYPE *EsUndoGetInstance(EsUndoManager *manager);
@ -2440,8 +2440,8 @@ function EsElement *EsSpacerCreate(EsElement *parent, uint64_t flags = ES_FLAGS_
function EsSplitter *EsSplitterCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL);
function EsCanvasPane *EsCanvasPaneCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL);
function void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount = 0, EsPanelBand *columns = ES_NULL, EsPanelBand *rows = ES_NULL);
function void EsPanelSetBandsAll(EsPanel *panel, EsPanelBand *column = ES_NULL, EsPanelBand *row = ES_NULL); // Set all the columns/rows to have the same properties. This must be called after the final number of bands has been determined/set!
function void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount = 0, const EsPanelBand *columns = ES_NULL, const EsPanelBand *rows = ES_NULL);
function void EsPanelSetBandsAll(EsPanel *panel, const EsPanelBand *column = ES_NULL, const EsPanelBand *row = ES_NULL); // Set all the columns/rows to have the same properties. This must be called after the final number of bands has been determined/set!
function void EsPanelTableSetChildCells(EsPanel *panel); // Automatically set the child cells for items in a table. This is only necessary if the number of columns/rows is changed after adding items to a table.
function void EsPanelSwitchTo(EsPanel *panel, EsElement *targetChild, EsTransitionType transitionType, uint32_t flags = ES_FLAGS_DEFAULT, float timeMultiplier = 1); // TODO More customization of transitions?

View File

@ -3960,7 +3960,7 @@ void EsTextboxInsert(EsTextbox *textbox, const char *string, ptrdiff_t stringByt
}
undoItem->textbox = textbox;
EsUndoPush(textbox->undo, TextboxUndoItemCallback, undoItem, undoItemBytes);
EsUndoPush(textbox->undo, TextboxUndoItemCallback, undoItem, undoItemBytes, false /* do not set instance's undo manager */);
}
EsHeapFree(undoItem);

Binary file not shown.

Binary file not shown.