Better markdown rendering. WIP 3d control

This commit is contained in:
Scott Wadden 2022-05-12 21:51:06 -03:00
parent 7afa4e7235
commit 1a43e71a54
5 changed files with 282 additions and 52 deletions

View File

@ -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__ = {

View File

@ -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

View File

@ -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.
<div style=\"page-break-after: always;\"></div>
## 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
```
<div style=\"page-break-after: always;\"></div>
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.
<div style=\"page-break-after: always;\"></div>
## 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"]

View File

@ -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

View File

@ -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)