From 15a26a1a42bcd39f7773496fcbad0740cd6391bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=83zvan=20C=2E=20R=C4=83dulescu?= Date: Tue, 8 Mar 2022 14:04:15 +0200 Subject: [PATCH] improvement: redesign modulo examples to be more intuitive (#437) fixes #430 --- .../visuals/ExampleModulo.tscn | 23 +- course/lesson-15-modulo/visuals/Modulo.gd | 87 +++---- course/lesson-15-modulo/visuals/Modulo.tscn | 241 +++++++++++------- .../visuals/Modulo/normal_stylebox.tres | 10 + .../visuals/Modulo/remainder_stylebox.tres | 10 + ui/components/RunnableCodeExample.gd | 13 +- 6 files changed, 223 insertions(+), 161 deletions(-) create mode 100644 course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres create mode 100644 course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres diff --git a/course/lesson-15-modulo/visuals/ExampleModulo.tscn b/course/lesson-15-modulo/visuals/ExampleModulo.tscn index 217b4c71..57391639 100644 --- a/course/lesson-15-modulo/visuals/ExampleModulo.tscn +++ b/course/lesson-15-modulo/visuals/ExampleModulo.tscn @@ -7,27 +7,16 @@ script/source = "tool extends RunnableCodeExample + func _ready(): yield(self, \"ready\") - var number_slider = create_slider_for(\"number\", 1, 7, 1) - var modulo_slider = create_slider_for(\"modulo\", 1, 7, 1) - _scene_instance.connect(\"number_changed\", self, \"_on_number_changed\", [number_slider]) - _scene_instance.connect(\"modulo_changed\", self, \"_on_modulo_changed\", [modulo_slider]) + var number_slider = create_slider_for(\"number\", 1, 7, 1, _scene_instance.number_color) + var modulo_slider = create_slider_for(\"modulo\", 1, 7, 1, _scene_instance.modulo_color) func _set_instance_value(value: float, property_name: String, value_label: Label) -> void: ._set_instance_value(value, property_name, value_label) _gdscript_text_edit.text = gdscript_code.replace(property_name, \"%s [=%s]\"%[property_name, value]) - - -func _on_number_changed(number: int, slider: HSlider) -> void: - if slider.value != number: - slider.value = number - - -func _on_modulo_changed(modulo: int, slider: HSlider) -> void: - if slider.value != modulo: - slider.value = modulo " [node name="ExampleModulo" type="PanelContainer"] @@ -37,15 +26,15 @@ margin_bottom = 14.0 [node name="RunnableCodeExample" parent="." instance=ExtResource( 1 )] margin_left = 7.0 margin_top = 7.0 -margin_right = 307.0 +margin_right = 387.0 margin_bottom = 387.0 script = SubResource( 1 ) scene = ExtResource( 2 ) [node name="Frame" parent="RunnableCodeExample" index="0"] -margin_right = 300.0 +margin_right = 380.0 margin_bottom = 380.0 -rect_min_size = Vector2( 300, 380 ) +rect_min_size = Vector2( 380, 380 ) [node name="ResetButton" parent="RunnableCodeExample/Frame/HBoxContainer" index="0"] visible = false diff --git a/course/lesson-15-modulo/visuals/Modulo.gd b/course/lesson-15-modulo/visuals/Modulo.gd index 84bca30c..0fea3194 100644 --- a/course/lesson-15-modulo/visuals/Modulo.gd +++ b/course/lesson-15-modulo/visuals/Modulo.gd @@ -1,77 +1,60 @@ tool extends Node2D -const color_green := Color("3dff6e") -const color_red := Color("928fb8") -export var _offset := Vector2.ZERO -export (int, 1, 7) var number := 5 setget _set_number, get_number -export (int, 1, 7) var modulo := 3 setget _set_modulo, get_modulo +const NORMAL_STYLEBOX := preload("res://course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres") +const REMAINDER_STYLEBOX := preload("res://course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres") +const MODULO_SIZE_X := 48 +const MODULO_GAP := 4 -onready var _blocks := $Blocks -onready var _string := $String -onready var _remainder := $Remainder +export (int, 1, 7) var number := 5 setget _set_number +export (int, 1, 7) var modulo := 3 setget _set_modulo +var number_color := Color.black +var modulo_color := Color.black + +onready var _result := $Result as Control +onready var _result_blocks := $Result/HBoxContainerBlocks as HBoxContainer +onready var _result_modulo := $Result/HBoxContainerModulo as HBoxContainer +onready var _string := $String as RichTextLabel +onready var _remainder := $Remainder as Label func _ready() -> void: - _align_blocks() + number_color = NORMAL_STYLEBOX.border_color + modulo_color = _result_modulo.get_child(0).get_stylebox("panel").border_color _update_visual() -func _align_blocks() -> void: - for c in _blocks.get_children(): - c.modulate.a = 0.0 - - var row := 0 - var column := 0 - for i in range(max(number, modulo)): - _blocks.get_child(i).position = Vector2(40 * column, row * 40) + _offset - row += 1 - if row % modulo == 0 and modulo < number: - column += 1 - row = 0 - - func _update_visual() -> void: - if number >= modulo: - for i in range(number): - _blocks.get_child(i).modulate.a = 0 - if i < number - (number % modulo): - _blocks.get_child(i).modulate = color_red - else: - _blocks.get_child(i).modulate = color_green - else: - for i in range(modulo): - _blocks.get_child(i).modulate = color_green - - if i >= number: - _blocks.get_child(i).modulate = color_red - - _string.text = "%s %% %s" % [number, modulo] + var remainder := number % modulo + var cap := max(number, remainder) + for i in range(_result_blocks.get_child_count()): + var block: Panel = _result_blocks.get_child(i) + block.visible = i < cap + block.add_stylebox_override("panel", NORMAL_STYLEBOX if i < number - remainder else REMAINDER_STYLEBOX) + + var rest := int(number / modulo) + for i in range(_result_modulo.get_child_count()): + var modulo_highlight: Panel = _result_modulo.get_child(i) + modulo_highlight.visible = i < rest + modulo_highlight.rect_min_size.x = MODULO_SIZE_X * modulo + MODULO_GAP * (modulo - 1) + + _string.bbcode_text = ( + "[center][color=#%s]%s[/color] %% [color=#%s]%s[/color][/center]" % + [number_color.to_html(), number, modulo_color.to_html(), modulo] + ) _remainder.text = "Remainder: %s" % [number % modulo] func _set_number(value: int) -> void: number = value - emit_signal("number_changed", value) - if not _blocks: + if not _result: return - _align_blocks() _update_visual() func _set_modulo(value: int) -> void: modulo = value - emit_signal("modulo_changed", value) - if not _blocks: + if not _result: return - _align_blocks() _update_visual() - - -func get_number() -> int: - return number - - -func get_modulo() -> int: - return modulo diff --git a/course/lesson-15-modulo/visuals/Modulo.tscn b/course/lesson-15-modulo/visuals/Modulo.tscn index b676fe77..e48d1faf 100644 --- a/course/lesson-15-modulo/visuals/Modulo.tscn +++ b/course/lesson-15-modulo/visuals/Modulo.tscn @@ -1,106 +1,165 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://course/lesson-15-modulo/visuals/Modulo.gd" type="Script" id=1] -[ext_resource path="res://course/lesson-15-modulo/visuals/square.png" type="Texture" id=2] [ext_resource path="res://ui/theme/fonts/font_title.tres" type="DynamicFont" id=3] +[ext_resource path="res://course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres" type="StyleBox" id=4] +[ext_resource path="res://course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres" type="StyleBox" id=5] + +[sub_resource type="StyleBoxFlat" id=3] +draw_center = false +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color( 0.14902, 0.776471, 0.968627, 1 ) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 [node name="Modulo" type="Node2D"] script = ExtResource( 1 ) -_offset = Vector2( -120, -90 ) - -[node name="Blocks" type="Node2D" parent="."] -position = Vector2( 0, 11 ) - -[node name="Block" type="Sprite" parent="Blocks"] -modulate = Color( 0.572549, 0.560784, 0.721569, 1 ) -position = Vector2( -120, -90 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block2" type="Sprite" parent="Blocks"] -modulate = Color( 0.572549, 0.560784, 0.721569, 1 ) -position = Vector2( -120, -50 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block2"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block3" type="Sprite" parent="Blocks"] -modulate = Color( 0.572549, 0.560784, 0.721569, 1 ) -position = Vector2( -120, -10 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block3"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block4" type="Sprite" parent="Blocks"] -modulate = Color( 0.239216, 1, 0.431373, 1 ) -position = Vector2( -80, -90 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block4"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block5" type="Sprite" parent="Blocks"] -modulate = Color( 0.239216, 1, 0.431373, 1 ) -position = Vector2( -80, -50 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block5"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block6" type="Sprite" parent="Blocks"] -modulate = Color( 1, 0, 0, 0 ) -position = Vector2( 40, 40 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block6"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="Block7" type="Sprite" parent="Blocks"] -modulate = Color( 1, 0, 0, 0 ) -position = Vector2( 40, 80 ) -texture = ExtResource( 2 ) - -[node name="Shadow" type="Sprite" parent="Blocks/Block7"] -modulate = Color( 0.0901961, 0.0941176, 0.188235, 1 ) -show_behind_parent = true -position = Vector2( 0, 4 ) -texture = ExtResource( 2 ) - -[node name="String" type="Label" parent="."] + +[node name="Result" type="Control" parent="."] +margin_left = -172.0 +margin_top = -90.0 +margin_right = -172.0 +margin_bottom = -90.0 + +[node name="HBoxContainerBlocks" type="HBoxContainer" parent="Result"] +margin_right = 344.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 344, 0 ) +custom_constants/separation = 20 + +[node name="Block" type="Panel" parent="Result/HBoxContainerBlocks"] +margin_right = 32.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 5 ) + +[node name="Block2" type="Panel" parent="Result/HBoxContainerBlocks"] +margin_left = 52.0 +margin_right = 84.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 5 ) + +[node name="Block3" type="Panel" parent="Result/HBoxContainerBlocks"] +margin_left = 104.0 +margin_right = 136.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 5 ) + +[node name="Block4" type="Panel" parent="Result/HBoxContainerBlocks"] +margin_left = 156.0 +margin_right = 188.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 4 ) + +[node name="Block5" type="Panel" parent="Result/HBoxContainerBlocks"] +margin_left = 208.0 +margin_right = 240.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 4 ) + +[node name="Block6" type="Panel" parent="Result/HBoxContainerBlocks"] +visible = false +margin_left = 260.0 +margin_right = 292.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 5 ) + +[node name="Block7" type="Panel" parent="Result/HBoxContainerBlocks"] +visible = false +margin_left = 312.0 +margin_right = 344.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 32, 36 ) +custom_styles/panel = ExtResource( 5 ) + +[node name="HBoxContainerModulo" type="HBoxContainer" parent="Result"] +margin_left = -8.0 +margin_top = -8.0 +margin_right = 352.0 +margin_bottom = 44.0 +rect_min_size = Vector2( 360, 0 ) +custom_constants/separation = 4 + +[node name="ModuloHighlight" type="Panel" parent="Result/HBoxContainerModulo"] +margin_right = 152.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 152, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight2" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight3" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight4" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight5" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight6" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="ModuloHighlight7" type="Panel" parent="Result/HBoxContainerModulo"] +visible = false +margin_left = 156.0 +margin_right = 156.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 0, 52 ) +custom_styles/panel = SubResource( 3 ) + +[node name="String" type="RichTextLabel" parent="."] margin_left = -84.0 -margin_top = 97.0 +margin_top = 100.0 margin_right = 84.0 -margin_bottom = 128.0 -custom_fonts/font = ExtResource( 3 ) +margin_bottom = 131.0 +custom_fonts/normal_font = ExtResource( 3 ) +bbcode_enabled = true +bbcode_text = "[center][color=#fff5fafa]5[/color] % [color=#ff26c6f7]3[/color][/center]" text = "5 % 3" -align = 1 +scroll_active = false [node name="Remainder" type="Label" parent="."] margin_left = -84.0 -margin_top = 131.0 +margin_top = 140.0 margin_right = 84.0 -margin_bottom = 162.0 +margin_bottom = 171.0 +custom_colors/font_color = Color( 0.239216, 1, 0.431373, 1 ) custom_fonts/font = ExtResource( 3 ) text = "Remainder: 2" diff --git a/course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres b/course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres new file mode 100644 index 00000000..f3b7584c --- /dev/null +++ b/course/lesson-15-modulo/visuals/Modulo/normal_stylebox.tres @@ -0,0 +1,10 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +bg_color = Color( 0.572549, 0.560784, 0.721569, 1 ) +border_width_bottom = 4 +border_color = Color( 0.960784, 0.980392, 0.980392, 1 ) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 diff --git a/course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres b/course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres new file mode 100644 index 00000000..ef33ddbc --- /dev/null +++ b/course/lesson-15-modulo/visuals/Modulo/remainder_stylebox.tres @@ -0,0 +1,10 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +bg_color = Color( 0.239216, 1, 0.431373, 1 ) +border_width_bottom = 4 +border_color = Color( 0.960784, 0.980392, 0.980392, 1 ) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 diff --git a/ui/components/RunnableCodeExample.gd b/ui/components/RunnableCodeExample.gd index 6dc1166a..f79c9184 100644 --- a/ui/components/RunnableCodeExample.gd +++ b/ui/components/RunnableCodeExample.gd @@ -7,6 +7,7 @@ extends HBoxContainer signal scene_instance_set const ERROR_NO_RUN_FUNCTION := "Scene %s doesn't have a run() function. The Run button won't work." +const HSLIDER_GRABBER_HIGHLIGHT := preload("res://ui/theme/hslider_grabber_highlight.tres") export var scene: PackedScene setget set_scene export(String, MULTILINE) var gdscript_code := "" setget set_code @@ -112,7 +113,7 @@ func set_run_button_label(new_text: String) -> void: _run_button.text = run_button_label -func create_slider_for(property_name, min_value := 0.0, max_value := 100.0, step := 1.0) -> HSlider: +func create_slider_for(property_name, min_value := 0.0, max_value := 100.0, step := 1.0, color := Color.black) -> HSlider: if not _scene_instance: yield(self, "scene_instance_set") var box := HBoxContainer.new() @@ -134,6 +135,16 @@ func create_slider_for(property_name, min_value := 0.0, max_value := 100.0, step slider.rect_min_size.x = 100.0 slider.connect("value_changed", self, "_set_instance_value", [property_name, value_label]) _set_instance_value(property_value, property_name, value_label) + + if color != Color.black: + var hslider_grabber_highlight := HSLIDER_GRABBER_HIGHLIGHT.duplicate() + hslider_grabber_highlight.bg_color = color + + label.add_color_override("font_color", color) + value_label.add_color_override("font_color", color) + slider.add_stylebox_override("grabber_area", hslider_grabber_highlight) + slider.add_stylebox_override("grabber_area_highlight", hslider_grabber_highlight) + return slider