NODE(SolidifiedFunction) \
NODE(PolyProc) \
\
+ NODE(Note) \
+ \
NODE(Package)
#define NODE(name) typedef struct Ast ## name Ast ## name;
Ast_Kind_Directive_Operator,
Ast_Kind_Directive_Export,
+ Ast_Kind_Note,
+
Ast_Kind_Count
} AstKind;
AstTyped* export;
};
+struct AstNote {
+ AstNode_base;
+};
+
typedef enum EntityState {
Entity_State_Error,
Entity_Type_Unknown,
Entity_Type_Error,
+ Entity_Type_Note,
Entity_Type_Load_Path,
Entity_Type_Load_File,
Entity_Type_Binding,
bh_allocator allocator;
CompileAction action;
- u32 verbose_output : 28;
+ u32 verbose_output : 2;
b32 fun_output : 1;
b32 print_function_mappings : 1;
b32 print_static_if_results : 1;
+ b32 print_notes : 1;
b32 use_post_mvp_features : 1;
-
+@Rename
package immediate_mode
#load "./immediate_renderer"
--- /dev/null
+package ui
+
+// UI Flow
+
+Flow :: struct {
+ split_vertical :: proc {
+ (r: Rectangle, left_percent: f32) -> (left: Rectangle, right: Rectangle) {
+ return split_vertical(r, left_width=left_percent * Rectangle.width(r));
+ },
+
+ (r: Rectangle, right_percent: f32) -> (left: Rectangle, right: Rectangle) {
+ return split_vertical(r, right_width=right_percent * Rectangle.width(r));
+ },
+
+ (r: Rectangle, left_width: f32) -> (left: Rectangle, right: Rectangle) {
+ x0, y0 := Rectangle.top_left(r);
+ x1, y1 := Rectangle.bottom_right(r);
+
+ return .{ x0=x0, x1=x0+left_width, y0=y0, y1=y1 },
+ .{ x0=x0+left_width, x1=x1, y0=y0, y1=y1 };
+ },
+
+ (r: Rectangle, right_width: f32) -> (left: Rectangle, right: Rectangle) {
+ x0, y0 := Rectangle.top_left(r);
+ x1, y1 := Rectangle.bottom_right(r);
+
+ return .{ x0=x0, x1=x1-right_width, y0=y0, y1=y1 },
+ .{ x0=x1-right_width, x1=x1, y0=y0, y1=y1 };
+ }
+ }
+
+
+ split_horizontal :: proc {
+ (r: Rectangle, top_percent: f32) -> (top: Rectangle, bottom: Rectangle) {
+ return split_horizontal(r, top_height=top_percent * Rectangle.height(r));
+ },
+
+ (r: Rectangle, bottom_percent: f32) -> (top: Rectangle, bottom: Rectangle) {
+ return split_horizontal(r, bottom_height=bottom_percent * Rectangle.height(r));
+ },
+
+ (r: Rectangle, top_height: f32) -> (top: Rectangle, bottom: Rectangle) {
+ x0, y0 := Rectangle.top_left(r);
+ x1, y1 := Rectangle.bottom_right(r);
+
+ return .{ x0=x0, x1=x1, y0=y0, y1=y0+top_height },
+ .{ x0=x0, x1=x1, y0=y0+top_height, y1=y1 };
+ },
+
+ (r: Rectangle, bottom_height: f32) -> (top: Rectangle, bottom: Rectangle) {
+ x0, y0 := Rectangle.top_left(r);
+ x1, y1 := Rectangle.bottom_right(r);
+
+ return .{ x0=x0, x1=x1, y0=y0, y1=y1-bottom_height },
+ .{ x0=x0, x1=x1, y0=y1-bottom_height, y1=y1 };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+@Todo // document this module better
+
+
+/*
+
+The goal of this module is to provide a low-friction method of producing simple
+user interfaces in WebGL (and OpenGL when Onyx compiles to C).
+
+*/
+
+
+
+
+
+
+
+@Rename
+package ui
+
+use package immediate_mode // The immediate_mode module needs to be accessible
+
+#load "./ui"
+#load "./flow"
\ No newline at end of file
--- /dev/null
+package ui
+
+#private_file gfx :: package immediate_mode
+#private_file bitmap_font :: package bitmap_font
+#private_file gl :: package gl
+#private_file math :: package core.math
+
+#private font : bitmap_font.Bitmap_Font;
+#private font_texture : gl.GLTexture;
+
+@Temporary
+DEFAULT_TEXT_SIZE :: 32.0f
+
+
+
+init_ui :: () {
+ init_font();
+}
+
+#private init_font :: () {
+ font_data := #file_contents "./resources/font_2.data";
+
+ bft := bitmap_font.Bitmap_Font_Texture.{
+ data = font_data,
+ width = 256,
+ height = 256,
+ };
+
+ font = bitmap_font.bitmap_font_create(bft, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 \xff:");
+
+ font_texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, font_texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 256, 0, gl.RGBA, gl.UNSIGNED_BYTE, font_data);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.bindTexture(gl.TEXTURE_2D, -1);
+}
+
+get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
+ return font->get_width(text, size);
+}
+
+// 'x' and 'y' are the top-left coordinates of the text. 'y' is NOT the baseline.
+draw_text :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := gfx.Color4.{1,1,1}) {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, font_texture);
+ gfx.set_texture(0);
+
+ for char: text {
+ glyph := font->get_glyph(char);
+
+ if glyph == null {
+ glyph = font->get_glyph(255);
+ assert(glyph != null, "NO NULL GLYPH");
+ }
+
+ // Round to the nearest pixel
+ tx, ty := math.floor(x + .5), math.floor(y + .5);
+ w := math.floor(glyph.w * size * font.em + .5);
+ h := math.floor(glyph.h * size * font.em + .5);
+
+ gfx.textured_rect(
+ .{ tx, ty },
+ .{ w, h },
+ .{ glyph.x0, glyph.y0 },
+ .{ glyph.x1 - glyph.x0, glyph.y1 - glyph.y0 },
+ color = color);
+
+ x += glyph.w * size * font.em;
+ }
+
+ gfx.flush();
+ gl.bindTexture(gl.TEXTURE_2D, -1);
+}
+
+draw_rect :: proc {
+ (use r: Rectangle, color := gfx.Color4.{1,1,1}) {
+ gfx.set_texture();
+
+ width, height := Rectangle.dimensions(r);
+ gfx.rect(.{ x0, y0 }, .{ width, height }, color);
+ },
+
+ (x: f32, y: f32, w: f32, h: f32, color := gfx.Color4.{1,1,1}) {
+ gfx.set_texture();
+ gfx.rect(.{ x, y }, .{ w, h }, color);
+ }
+}
+
+@Themeing
+draw_button :: (use r: Rectangle, text: str) -> bool {
+ gfx.set_texture();
+
+ padding :: 10.0f @RawPixels
+
+ width, height := Rectangle.dimensions(r);
+
+ gfx.rect(.{ x0, y0 }, .{ width, height }, .{ 0.2, 0.2, 0.2 });
+ gfx.rect(.{ x0 + padding, y0 + padding }, .{ width - padding * 2, height - padding * 2 }, .{ 0.1, 0.1, 0.1 });
+
+ font_size := height * .4;
+ text_width := font->get_width(text, font_size);
+ text_height := font->get_height(text, font_size);
+
+ draw_text(text, x0 + (width - text_width) / 2, y0 + (height - text_height) / 2, font_size, .{ 1, 1, 1 });
+ return false;
+}
+
+
+
+Rectangle :: struct {
+ //
+ // x0,y0 ------------+
+ // | |
+ // | |
+ // +------------ x1, y1
+ //
+
+ x0: f32 = 0;
+ y0: f32 = 0;
+ x1: f32 = 0;
+ y1: f32 = 0;
+
+ width :: (use r: Rectangle) -> f32 do return math.abs(x1 - x0);
+ height :: (use r: Rectangle) -> f32 do return math.abs(y1 - y0);
+
+ dimensions :: (use r: Rectangle) -> (width: f32, height: f32) {
+ return math.abs(x1 - x0), math.abs(y1 - y0);
+ }
+
+ top_left :: (use r: Rectangle) -> (x: f32, y: f32) do return math.min(x0, x1), math.min(y0, y1);
+ bottom_right :: (use r: Rectangle) -> (x: f32, y: f32) do return math.max(x0, x1), math.max(y0, y1);
+}
+
+
+
"Developer flags:\n"
"\t--print-function-mappings Prints a mapping from WASM function index to source location.\n"
"\t--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.\n"
+ "\t--print-notes Prints the location of notes throughout the loaded code.\n"
"\n";
else if (!strcmp(argv[i], "--print-static-if-results")) {
options.print_static_if_results = 1;
}
+ else if (!strcmp(argv[i], "--print-notes")) {
+ options.print_notes = 1;
+ }
else if (!strcmp(argv[i], "--use-post-mvp-features")) {
options.use_post_mvp_features = 1;
}
"OPERATOR OVERLOAD",
"EXPORT",
+ "NOTE",
+
"AST_NODE_KIND_COUNT",
};
const char* entity_type_strings[Entity_Type_Count] = {
"Unknown",
"Error",
+ "Note",
"Add to Load Path",
"Load File",
"Binding (Declaration)",
} else {
if (expected_return_type->Basic.size > 0) {
onyx_report_error(retnode->token->pos,
- "Returning from non-void function without value. Expected a value of type '%s'.",
+ "Returning from non-void function without a value. Expected a value of type '%s'.",
type_get_name(expected_return_type));
return Check_Error;
}
ENTITY_INSERT(ent);
break;
}
+
+ case Ast_Kind_Note: {
+ ent.type = Entity_Type_Note;
+ ent.expr = (AstTyped *) node;
+ ent.state = Entity_State_Code_Gen;
+ ENTITY_INSERT(ent);
+ break;
+ }
default: {
ent.type = Entity_Type_Expression;
parser->prev = parser->curr;
// :LinearTokenDependent
parser->curr++;
- while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note)
+ while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note) {
+ if (parser->curr->type == Token_Type_Note) {
+ AstNote* note = make_node(AstNode, Ast_Kind_Note);
+ note->token = parser->curr;
+ ENTITY_SUBMIT(note);
+ }
+
parser->curr++;
+ }
}
static OnyxToken* find_matching_paren(OnyxToken* paren) {
void onyx_parse(OnyxParser *parser) {
// NOTE: Skip comments at the beginning of the file
- while (consume_token_if_next(parser, Token_Type_Comment));
+ while (consume_token_if_next(parser, Token_Type_Comment) || consume_token_if_next(parser, Token_Type_Note));
parser->package = parse_file_package(parser);
parser->file_scope = scope_create(parser->allocator, parser->package->private_scope, parser->tokenizer->tokens[0].pos);
case Entity_Type_Function: emit_function(module, ent->function); break;
case Entity_Type_Global: emit_global(module, ent->global); break;
+ // Cleanup: Maybe these should be printed elsewhere?
+ case Entity_Type_Note: {
+ if (!context.options->print_notes) break;
+
+ AstNote* note = (AstNote *) ent->expr;
+ OnyxFilePos pos = note->token->pos;
+
+ bh_printf("Note: %b %s:%d:%d\n", note->token->text, note->token->length, pos.filename, pos.line, pos.column);
+
+ break;
+ }
+
default: break;
}