diff --git a/app/components/MarkdownLabel.tscn b/app/components/MarkdownLabel.tscn index 8554c007..8dce46bb 100644 --- a/app/components/MarkdownLabel.tscn +++ b/app/components/MarkdownLabel.tscn @@ -31,23 +31,21 @@ content_margin_left = 20.0 content_margin_right = 20.0 content_margin_top = 20.0 content_margin_bottom = 20.0 -bg_color = Color( 0.0784314, 0.0117647, 0.113725, 1 ) +bg_color = Color( 0.0784314, 0.0117647, 0.113725, 0.839216 ) [sub_resource type="DynamicFont" id=13] font_data = ExtResource( 7 ) [sub_resource type="StyleBoxFlat" id=3] -content_margin_top = 10.0 -content_margin_bottom = 0.0 -bg_color = Color( 0, 0, 0, 0.839216 ) +content_margin_top = 20.0 +bg_color = Color( 0, 0, 0, 0.729412 ) border_width_top = 1 border_width_bottom = 1 border_color = Color( 1, 0.45098, 0.992157, 1 ) [node name="MarkdownLabel" type="ScrollContainer"] -margin_right = 1920.0 -margin_bottom = 1080.0 -scroll_horizontal_enabled = false +anchor_right = 1.0 +anchor_bottom = 1.0 script = ExtResource( 1 ) markdown = "" default_font = SubResource( 7 ) @@ -58,21 +56,26 @@ header_font = SubResource( 11 ) mono_font = SubResource( 12 ) [node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_right = 1920.0 +margin_bottom = 1080.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 custom_constants/separation = 0 [node name="RichTextLabel" type="RichTextLabel" parent="VBoxContainer"] visible = false -margin_right = 1920.0 margin_bottom = 69.0 -rect_min_size = Vector2( 1920, 0 ) +rect_clip_content = false focus_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 3 theme = ExtResource( 2 ) custom_colors/default_color = Color( 1, 1, 1, 1 ) custom_colors/selection_color = Color( 0.207843, 0, 0.321569, 1 ) custom_styles/normal = SubResource( 6 ) tab_size = 2 fit_content_height = true +scroll_active = false selection_enabled = true __meta__ = { "_edit_use_anchors_": false @@ -80,22 +83,18 @@ __meta__ = { [node name="TextEdit" type="TextEdit" parent="VBoxContainer"] visible = false -margin_right = 1920.0 -rect_min_size = Vector2( 1920, 0 ) size_flags_vertical = 3 custom_colors/selection_color = Color( 0.207843, 0, 0.321569, 1 ) custom_colors/executing_line_color = Color( 0.0392157, 0, 0.168627, 1 ) custom_colors/font_color_readonly = Color( 0.878431, 0.878431, 0.878431, 1 ) custom_fonts/font = SubResource( 13 ) custom_styles/read_only = SubResource( 3 ) -custom_styles/normal = SubResource( 3 ) indent_using_spaces = true indent_size = 2 readonly = true syntax_highlighting = true show_line_numbers = true smooth_scrolling = true -wrap_enabled = true caret_blink = true caret_moving_by_right_click = false __meta__ = { diff --git a/app/scenes/GUI.tscn b/app/scenes/GUI.tscn index 1892a158..ba536d90 100644 --- a/app/scenes/GUI.tscn +++ b/app/scenes/GUI.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=14 format=2] +[gd_scene load_steps=15 format=2] [ext_resource path="res://components/Console.gdns" type="Script" id=1] [ext_resource path="res://components/TextEdit.gdns" type="Script" id=2] [ext_resource path="res://components/ActionButton.tscn" type="PackedScene" id=3] +[ext_resource path="res://components/MarkdownLabel.tscn" type="PackedScene" id=4] [ext_resource path="res://textures/reticle.png" type="Texture" id=5] [ext_resource path="res://themes/DarkTheme.tres" type="Theme" id=6] [ext_resource path="res://themes/SF-Mono-Powerline-Bold.otf" type="DynamicFontData" id=8] @@ -30,9 +31,6 @@ font_data = ExtResource( 8 ) anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} [node name="Toolbar" type="HBoxContainer" parent="."] anchor_top = 1.0 @@ -104,24 +102,21 @@ group = SubResource( 1 ) icon = null [node name="LeftPanel" type="MarginContainer" parent="."] -anchor_right = 0.4 +anchor_right = 0.5 anchor_bottom = 1.0 margin_right = 192.0 mouse_filter = 2 theme = ExtResource( 6 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="ThemeHolder" type="VBoxContainer" parent="LeftPanel"] -margin_right = 960.0 +margin_right = 1152.0 margin_bottom = 1080.0 mouse_filter = 2 size_flags_horizontal = 3 size_flags_vertical = 3 [node name="MarginContainer" type="MarginContainer" parent="LeftPanel/ThemeHolder"] -margin_right = 960.0 +margin_right = 1152.0 margin_bottom = 1080.0 mouse_filter = 2 size_flags_horizontal = 3 @@ -130,7 +125,7 @@ size_flags_stretch_ratio = 2.0 [node name="Editor" type="TextEdit" parent="LeftPanel/ThemeHolder/MarginContainer"] visible = false -margin_right = 960.0 +margin_right = 1152.0 margin_bottom = 1080.0 size_flags_vertical = 3 custom_colors/selection_color = Color( 0.184314, 0.0117647, 0.686275, 1 ) @@ -163,6 +158,18 @@ scroll_following = true selection_enabled = true script = ExtResource( 1 ) +[node name="RightPanel" type="MarginContainer" parent="."] +visible = false +anchor_left = 0.5 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="MarkdownLabel" parent="RightPanel" instance=ExtResource( 4 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 960.0 +margin_bottom = 1080.0 + [node name="Reticle" type="Control" parent="."] visible = false anchor_left = 0.5 diff --git a/app/scenes/world.tscn b/app/scenes/world.tscn index 35243e9f..a96f7719 100644 --- a/app/scenes/world.tscn +++ b/app/scenes/world.tscn @@ -1,10 +1,24 @@ -[gd_scene load_steps=7 format=2] +[gd_scene load_steps=11 format=2] +[ext_resource path="res://components/MarkdownLabel.tscn" type="PackedScene" id=1] [ext_resource path="res://scenes/env.tres" type="Environment" id=2] [ext_resource path="res://materials/default_ground.tres" type="Material" id=3] [ext_resource path="res://components/GroundNode.gdns" type="Script" id=4] [ext_resource path="res://components/Player.tscn" type="PackedScene" id=6] +[sub_resource type="QuadMesh" id=3] +size = Vector2( 2, 2 ) + +[sub_resource type="ViewportTexture" id=5] +viewport_path = NodePath("Spatial/Viewport") + +[sub_resource type="SpatialMaterial" id=4] +resource_local_to_scene = true +flags_transparent = true +flags_unshaded = true +flags_albedo_tex_force_srgb = true +albedo_texture = SubResource( 5 ) + [sub_resource type="PlaneMesh" id=1] material = ExtResource( 3 ) size = Vector2( 1000, 1000 ) @@ -28,9 +42,195 @@ shadow_enabled = true shadow_color = Color( 0.658824, 0.658824, 0.658824, 1 ) directional_shadow_mode = 0 +[node name="Spatial" type="Spatial" parent="."] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, -1.99 ) + +[node name="Viewport" type="Viewport" parent="Spatial"] +size = Vector2( 1500, 1500 ) +msaa = 4 +fxaa = true +hdr = false +usage = 0 +render_target_v_flip = true +render_target_update_mode = 3 + +[node name="MarkdownLabel" parent="Spatial/Viewport" instance=ExtResource( 1 )] +markdown = "# Parts of an Enu Program + +Enu is programmed with a language called [Nim](https://nim-lang.org). This tutorial teach some of the +basics of Nim, as well as a few special features that are unique to Enu. + +## Comments + +Comments are a way to leave notes to yourself or other programmers. They can be +used for lots of different things, but generally provide more information on how +something works or why it was done a certain way. They can also have more +general information, such as the author of the code and when it was written. +They start with a `#` sign. Everything else on the line is ignored. + +```nim +# Copyright Scott Wadden, 2022 + +var last_row = false # we only want to change colors on the last row +``` + +## Types + +Every piece of data in Enu has a type. These are the most common: + +- `bool` - a `true` or `false` value. Example: `drawing = false` + +- `Number` - a number with a decimal, like `1.0`. Numbers without decimals, like + `1` will usually auto convert, but if something isn't working, try adding a + decimal. Example: `var age = 12.5` + +- `Text` - Any combination of letters, numbers and punctuation, contained inside + double quotes. Example: `var name = \"Sackville Coding Club\"` + +- `Color` - Any one of `blue`, `red`, `green`, `black`, `white`, `brown` or + `eraser`. Someday more colors will be available. Example `color = green` + +- `Position` - The place of something in the world. Example: + `me.position = player.position` + +- `Thing` - Anything that exists in the Enu world. This could be something you + build, a robot, or the player. + +
+ +## Variables + +A variable is used to store data. The value is usually set when it is created, +and can be modified later. A variable must always hold the same type of data. + +```nim +# ok: +var age = 12 +if birthday: + age = age + 1 + +if reset: + age = 0 + +# not ok. Age must always be a number, not text: +var age = 12 +if birthday: + age = \"13 years old\" +``` + +Usually variables are defined with just a value, but sometimes you need to +specify their type as well. This could be because you're not ready to give it a +value, or because you want it to contain more than one kind of `Thing`. + +For example: + +```nim +# this won't work because `enemy` gets automatically set to the type of +# `Player`, so other things won't work: +var enemy = player +enemy = me + +# it will work if we do it like this, since `player` and `me` are both `Thing` +var enemy: Thing +enemy = player +enemy = me +``` + +
+ +Usually each variable starts with `var`, but you can also indent to define +multiple variables with a single `var`. + +```nim +var name = \"Scott\" +var age = 43 +var town = \"Sackville\" + +# this is exactly the same as the above + +var + name = \"Scott\" + age = 43 + town = \"Sackville\" +``` + +## Special Variables + +Enu has some built in variables. + +- `me` - the `Thing` that we're working on in the current script. + +- `speed` - a `Number` to get or set our current speed. `1.0` by default. + +- `color` - the current drawing `Color`. `blue` by default. + +- `scale` - a `Number` to grow or shrink a `Thing`. `1.0` by default. Be careful + about making this too small. You might lose the `Thing` you're working on. + +- `position` - where a `Thing` is. You can use this to move something + immediately. `position = player.position` would teleport `me` to the player. + +- `drawing` - a `bool` that indicates if commands like `forward` or `back` should + draw blocks. `true` by default. `drawing = false` would let you move without + drawing anything, which could be useful for making a hole or a new object. + +- `glow` - how bright a `Thing` is. Can be used to make something flash, or to + highlight it. + +
+ +## Blocks + +Blocks start with a `:`, and are then indented by two spaces. Everything that's +inside the block is controlled by whatever started it. + +- `if` - an `if` block will only run if the statement is `true`. + +```nim +var length = 0 +... +if length == 0: + glow = 1 + echo \"You need to provide a length!\" + # both of the above only happen if `length` is 0. +``` + +- `times` - Make something happen more than once. + +```nim +4.times: + forward 5 + turn right + # the above each happen 4 times (which draws a square!) + +back 20 +# this only happens once, since it isn't in a `times` block. + +8.times(side): + # It's also possible to see how many times we've already performed + # the action by passing `times` an index variable. + # This starts from 0. in this example we're storing the counter in + # a variable called `side`. Because we're doing this 8 times, + # `side` will be set first to 0, then 1, 2, 3, 4, 5, 6, 7. + forward 5 + if side != 4: + turn right + else: + turn left + +echo \"We drew a bow tie!\" +echo \"This will only be printed once because it isn't in a times block\" +``` + +" + +[node name="MeshInstance" type="MeshInstance" parent="Spatial"] +transform = Transform( 0.95, 0, 0, 0, 0.95, 0, 0, 0, 0.95, 0, 0, 0 ) +mesh = SubResource( 3 ) +material/0 = SubResource( 4 ) + [node name="Ground" type="MeshInstance" parent="."] mesh = SubResource( 1 ) -material/0 = null script = ExtResource( 4 ) [node name="StaticBody" type="StaticBody" parent="Ground"] diff --git a/docs/coding.md b/docs/coding.md index 5107cb9b..1f3944f4 100644 --- a/docs/coding.md +++ b/docs/coding.md @@ -150,11 +150,11 @@ back 20 # this only happens once, since it isn't in a `times` block. 8.times(side): - # It's also possible to see how many times we've already performed the action - # by passing `times` an index variable. - # This starts from 0. in this example we're storing the counter in a variable - # called `side`. Because we're doing this 8 times, `side` will be set first to - # 0, then 1, 2, 3, 4, 5, 6, 7. + # It's also possible to see how many times we've already performed + # the action by passing `times` an index variable. + # This starts from 0. in this example we're storing the counter in + # a variable called `side`. Because we're doing this 8 times, + # `side` will be set first to 0, then 1, 2, 3, 4, 5, 6, 7. forward 5 if side != 4: turn right diff --git a/src/ui/markdown_label.nim b/src/ui/markdown_label.nim index efda1010..3228c534 100644 --- a/src/ui/markdown_label.nim +++ b/src/ui/markdown_label.nim @@ -1,4 +1,4 @@ -import std / lists +import std / [lists, algorithm] import pkg / [godot, markdown, hmatching, print, model_citizen] import godotapi / [rich_text_label, scroll_container, text_edit, theme, dynamic_font, dynamic_font_data, style_box] @@ -16,6 +16,7 @@ gdobj MarkdownLabel of ScrollContainer: bold_italic_font* {.gd_export.}: DynamicFont header_font* {.gd_export.}: DynamicFont mono_font* {.gd_export.}: DynamicFont + mono_width* {.gd_export.} = 0 current_label: RichTextLabel container: Node og_text_edit: TextEdit @@ -28,22 +29,38 @@ gdobj MarkdownLabel of ScrollContainer: self.current_label.visible = true proc set_font_sizes = - let config = GameState.active.config - self.default_font.size = config.font_size.value - self.italic_font.size = config.font_size.value - self.bold_font.size = config.font_size.value - self.bold_italic_font.size = config.font_size.value - self.mono_font.size = config.font_size.value - self.header_font.size = config.font_size.value * 2 + var size = 3 + if self.mono_width > 0: + let size_str = " ".repeat(self.mono_width + 2) + self.mono_font.size = size + while self.mono_font.get_string_size(size_str).x < self.rect_size.x: + inc size + self.mono_font.size = size + dec size + else: + size = GameState.active().config.font_size.value + + self.default_font.size = size + self.italic_font.size = size + self.bold_font.size = size + self.bold_italic_font.size = size + self.mono_font.size = size + self.header_font.size = size * 2 var first = true for child in self.container.get_children: var child = child.as_object(Node) if child of TextEdit: var child = TextEdit(child) - var height = child.get_line_count * child.get_line_height + 18 + var height = child.get_line_count * child.get_line_height + 24 + let lines = dup child.text.split_lines.sorted_by_it(it.len) + var size = child.rect_min_size size.y = float height + if lines.len > 0: + let str_size = + self.mono_font.get_string_size(" ".repeat(lines.len + 2)) + size.x = str_size.x child.rect_min_size = size child.rect_size = size elif child of RichTextLabel: @@ -53,7 +70,7 @@ gdobj MarkdownLabel of ScrollContainer: if not first: stylebox = self.current_label.get_stylebox("normal") - stylebox.content_margin_top = float(config.font_size.value + 4) + stylebox.content_margin_top = float(size + 4) self.current_label.add_stylebox_override("normal", stylebox) else: first = false @@ -64,6 +81,7 @@ gdobj MarkdownLabel of ScrollContainer: result.add_color_region("#[", "]#", comment_color, false) method ready() = + self.bind_signals(self, "resized") self.container = self.get_node("VBoxContainer") self.og_text_edit = self.container.get_node("TextEdit") as TextEdit self.og_label = self.container.get_node("RichTextLabel") as RichTextLabel @@ -77,6 +95,9 @@ gdobj MarkdownLabel of ScrollContainer: GameState.active.config.font_size.changes: if added: self.set_font_sizes() + + method on_resized = + self.set_font_sizes() proc render_markdown(token: Token, list_position = 0, inline_blocks = false) = var list_position = list_position @@ -91,6 +112,16 @@ gdobj MarkdownLabel of ScrollContainer: self.render_markdown t label.with(pop, pop, newline) self.needs_margin = true + + of of Em(): + label.push_font self.italic_font + self.render_markdown t + label.pop + + of of Strong(): + label.push_font self.bold_font + self.render_markdown t + label.pop of of CodeSpan(): label.with(push_font self.mono_font, push_color ir_black[number], @@ -114,34 +145,27 @@ gdobj MarkdownLabel of ScrollContainer: self.needs_margin = true of of OL(): - label.push_indent 2 self.render_markdown(t, 1) - label.pop of of UL(): - label.push_indent 2 self.render_markdown(t) - label.pop of of LI(): - label.push_font self.mono_font + label.with(push_table 2, push_cell, push_font self.mono_font) if list_position > 0: label.add_text LI(t).marker & ". " inc list_position else: label.add_text "• " - label.pop + label.with(pop, pop, push_cell) self.render_markdown(t, inline_blocks = true) - label.with(newline) - self.needs_margin = true + label.with(pop, pop, newline) of of Text(): label.add_text t.doc of of SoftBreak(): - label.newline - if inline_blocks: - label.with(push_font self.mono_font, add_text " ", pop) + label.add_text " " else: self.render_markdown(t)