From 29b11560b52a87f56cd1c6391d3e4012059597f7 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 5 Oct 2026 22:33:58 +0200 Subject: [PATCH] Show where each item starts in the inspiration and sync lists Items in the inspiration list and the sync result columns now each have their own background box, alternating between two shades, with a small gap between them. When a long name wraps over several lines it is still clear which lines belong together. The sync columns keep their alternating white and orange text. --- MainWindow.gd | 14 ++----- list_row.gd | 57 +++++++++++++++++++++++++++ list_row.gd.uid | 1 + sync_window.gd | 29 ++++++-------- tests/integration/test_main_window.gd | 9 +++++ tests/integration/test_sync_window.gd | 2 +- tests/unit/test_list_row.gd | 26 ++++++++++++ tests/unit/test_list_row.gd.uid | 1 + 8 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 list_row.gd create mode 100644 list_row.gd.uid create mode 100644 tests/unit/test_list_row.gd create mode 100644 tests/unit/test_list_row.gd.uid diff --git a/MainWindow.gd b/MainWindow.gd index 2eae59a..e5c690e 100644 --- a/MainWindow.gd +++ b/MainWindow.gd @@ -446,12 +446,8 @@ func get_suggestion_list() -> void: soundtracks = [] Settings.delete_children(inspiration_list) soundtracks.append_array(array) - for soundtrack: String in soundtracks: - var inspiration_label: Label = Label.new() - inspiration_label.add_theme_font_size_override("font_size", 20) - inspiration_label.text = soundtrack - inspiration_label.autowrap_mode = TextServer.AUTOWRAP_WORD - inspiration_list.add_child(inspiration_label) + for i: int in soundtracks.size(): + inspiration_list.add_child(ListRow.create(soundtracks[i], i, 20)) else: print("Unexpected data") MusicServerClient.fetch_all_soundtracks(populate_list) @@ -572,7 +568,5 @@ func dir_contents(path: String) -> void: else: print("An error occurred when trying to access the path.") - for soundtrack: String in soundtracks: - var local_inspiration_label: Label = Label.new() - local_inspiration_label.text = soundtrack - inspiration_list.add_child(local_inspiration_label) + for i: int in soundtracks.size(): + inspiration_list.add_child(ListRow.create(soundtracks[i], i, 20)) diff --git a/list_row.gd b/list_row.gd new file mode 100644 index 0000000..c9fd8eb --- /dev/null +++ b/list_row.gd @@ -0,0 +1,57 @@ +class_name ListRow +extends PanelContainer +## One item in a list of names, such as the inspiration list or a sync result column. +## +## Every row has its own background, alternating between two shades, so it is clear +## where an item starts even when its text wraps over several lines. + +const EVEN_BACKGROUND: Color = Color(1, 1, 1, 0.05) +const ODD_BACKGROUND: Color = Color(1, 1, 1, 0.11) + +static var _even_style: StyleBoxFlat +static var _odd_style: StyleBoxFlat + +## The label showing the item's text. +var label: Label + +## The item's text. +var text: String: + get: + return label.text + + +## Creates the row for item number [param row] (counting from 0) in a list. +static func create( + item_text: String, + row: int, + font_size: int, + alignment: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT, +) -> ListRow: + var list_row: ListRow = ListRow.new() + list_row.add_theme_stylebox_override("panel", _style(row % 2 != 0)) + list_row.label = Label.new() + list_row.label.add_theme_font_size_override("font_size", font_size) + list_row.label.text = item_text + list_row.label.horizontal_alignment = alignment + list_row.label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + list_row.add_child(list_row.label) + return list_row + + +# Rows share two styles instead of each creating its own +static func _style(odd: bool) -> StyleBoxFlat: + if _even_style == null: + _even_style = _make_style(EVEN_BACKGROUND) + _odd_style = _make_style(ODD_BACKGROUND) + return _odd_style if odd else _even_style + + +static func _make_style(background: Color) -> StyleBoxFlat: + var style: StyleBoxFlat = StyleBoxFlat.new() + style.bg_color = background + style.set_corner_radius_all(4) + style.content_margin_left = 8 + style.content_margin_right = 8 + style.content_margin_top = 2 + style.content_margin_bottom = 2 + return style diff --git a/list_row.gd.uid b/list_row.gd.uid new file mode 100644 index 0000000..c92d518 --- /dev/null +++ b/list_row.gd.uid @@ -0,0 +1 @@ +uid://c2o2qunso5n8l diff --git a/sync_window.gd b/sync_window.gd index 352e768..34869cb 100644 --- a/sync_window.gd +++ b/sync_window.gd @@ -126,10 +126,9 @@ func on_sync_finished(data_received: Dictionary) -> void: make_header_label(soundtracks_changed_title_vbox, "Changed Title") var row: int = 0 for old_soundtrack_title: String in data_received.soundtracks_changed_title: - var label: Label = make_soundtrack_label(old_soundtrack_title + "\nchanged title to\n" + data_received.soundtracks_changed_title[old_soundtrack_title]) - if row % 2 != 0: - label.add_theme_color_override("font_color", Color(1, 0.5, 0)) - soundtracks_changed_title_vbox.add_child(label) + var new_soundtrack_title: String = data_received.soundtracks_changed_title[old_soundtrack_title] + soundtracks_changed_title_vbox.add_child(make_soundtrack_row( + old_soundtrack_title + "\nchanged title to\n" + new_soundtrack_title, row)) row += 1 soundtracks_changed_title_vbox.visible = true @@ -194,13 +193,12 @@ func make_header_label(vbox: VBoxContainer, text: String) -> void: label.text = text vbox.add_child(label) -func make_soundtrack_label(text: String) -> Label: - var label: Label = Label.new() - label.add_theme_font_size_override("font_size", 18) - label.text = text - label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - return label +# Items alternate between white and orange text as well as background shade +func make_soundtrack_row(text: String, row: int) -> ListRow: + var list_row: ListRow = ListRow.create(text, row, 18, HORIZONTAL_ALIGNMENT_CENTER) + if row % 2 != 0: + list_row.label.add_theme_color_override("font_color", Color(1, 0.5, 0)) + return list_row func reset_sync_result() -> void: sync_progress_bar.value = 0 @@ -222,13 +220,8 @@ func reset_sync_result() -> void: func make_list(list: Array, vbox: VBoxContainer, text: String) -> void: Settings.delete_children(vbox) make_header_label(vbox, text) - var row: int = 0 - for soundtrack: String in list: - var label: Label = make_soundtrack_label(soundtrack) - if row % 2 != 0: - label.add_theme_color_override("font_color", Color(1, 0.5, 0)) - vbox.add_child(label) - row += 1 + for row: int in list.size(): + vbox.add_child(make_soundtrack_row(list[row], row)) func show_sync_result() -> void: sync_title_label.text = "Sync Result" diff --git a/tests/integration/test_main_window.gd b/tests/integration/test_main_window.gd index 0b29042..390d221 100644 --- a/tests/integration/test_main_window.gd +++ b/tests/integration/test_main_window.gd @@ -240,3 +240,12 @@ func test_log_adds_one_row_per_point_with_padded_time() -> void: var time_pattern: RegEx = RegEx.create_from_string("^\\d\\d:\\d\\d:\\d\\d Crono got a point$") assert_object(time_pattern.search((log_box.get_child(0) as Label).text)).is_not_null() assert_str((log_box.get_child(1) as Label).text).ends_with("Crono lost a point") + + +func test_inspiration_list_alternates_row_backgrounds() -> void: + var rows: Array[Node] = _window.get_node("ScrollContainer/VBoxContainer").get_children() + + assert_int(rows.size()).is_equal(2) + assert_str((rows[0] as ListRow).text).is_equal("Chrono Trigger") + assert_that((rows[0] as ListRow).get_theme_stylebox("panel")) \ + .is_not_equal((rows[1] as ListRow).get_theme_stylebox("panel")) diff --git a/tests/integration/test_sync_window.gd b/tests/integration/test_sync_window.gd index b64371a..60e3dfa 100644 --- a/tests/integration/test_sync_window.gd +++ b/tests/integration/test_sync_window.gd @@ -113,7 +113,7 @@ func test_changed_titles_list_old_and_new_title() -> void: var vbox: Control = _node("SoundtracksChangedTitleVBox") assert_bool(vbox.visible).is_true() - assert_str((vbox.get_child(1) as Label).text).is_equal( + assert_str((vbox.get_child(1) as ListRow).text).is_equal( "FF7\nchanged title to\nFinal Fantasy VII") diff --git a/tests/unit/test_list_row.gd b/tests/unit/test_list_row.gd new file mode 100644 index 0000000..d25bb06 --- /dev/null +++ b/tests/unit/test_list_row.gd @@ -0,0 +1,26 @@ +extends GdUnitTestSuite + + +func _background(list_row: ListRow) -> Color: + return (list_row.get_theme_stylebox("panel") as StyleBoxFlat).bg_color + + +func test_row_shows_text() -> void: + var list_row: ListRow = auto_free(ListRow.create("Chrono Trigger", 0, 20)) + + assert_str(list_row.text).is_equal("Chrono Trigger") + + +func test_neighbouring_rows_have_different_backgrounds() -> void: + var first: ListRow = auto_free(ListRow.create("Chrono Trigger", 0, 20)) + var second: ListRow = auto_free(ListRow.create("Chrono Cross", 1, 20)) + var third: ListRow = auto_free(ListRow.create("Xenogears", 2, 20)) + + assert_that(_background(first)).is_not_equal(_background(second)) + assert_that(_background(first)).is_equal(_background(third)) + + +func test_long_text_wraps() -> void: + var list_row: ListRow = auto_free(ListRow.create("Castlevania: Symphony of the Night", 0, 20)) + + assert_int(list_row.label.autowrap_mode).is_equal(TextServer.AUTOWRAP_WORD_SMART) diff --git a/tests/unit/test_list_row.gd.uid b/tests/unit/test_list_row.gd.uid new file mode 100644 index 0000000..70b4210 --- /dev/null +++ b/tests/unit/test_list_row.gd.uid @@ -0,0 +1 @@ +uid://bouligcywd5se