Reveal the flashcard answer by tapping the card, not a separate button
Matches the mockup: the "Show Answer" button is replaced by an in-card "Tap card to reveal answer" hint, and clicking anywhere on the card reveals it (guarded the same way Space already was - only while at the live card and not yet revealed).
This commit is contained in:
@@ -55,7 +55,7 @@ var back_label: Label
|
||||
var extra_caption: Label
|
||||
var extra_label: Label
|
||||
var card_position_label: Label
|
||||
var reveal_button: Button
|
||||
var tap_hint_label: Label
|
||||
var grade_row: HBoxContainer
|
||||
var grade_buttons: Dictionary = {}
|
||||
var undo_button: Button
|
||||
@@ -184,6 +184,8 @@ func _build() -> void:
|
||||
card_panel = PanelContainer.new()
|
||||
card_panel.custom_minimum_size = Vector2(720, 420)
|
||||
card_panel.add_theme_stylebox_override("panel", Brand.content_card_box(UIThemeService.is_dark_mode(), true, Brand.ROYAL_BLUE))
|
||||
card_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
card_panel.gui_input.connect(_on_card_panel_input)
|
||||
center.add_child(card_panel)
|
||||
var card_content := VBoxContainer.new()
|
||||
card_content.add_theme_constant_override("separation", 20)
|
||||
@@ -206,6 +208,13 @@ func _build() -> void:
|
||||
divider.visible = false
|
||||
card_content.add_child(divider)
|
||||
|
||||
var tap_hint_center := CenterContainer.new()
|
||||
card_content.add_child(tap_hint_center)
|
||||
tap_hint_label = Label.new()
|
||||
tap_hint_label.text = "Tap card to reveal answer"
|
||||
tap_hint_label.visible = false
|
||||
tap_hint_center.add_child(tap_hint_label)
|
||||
|
||||
var answer_block := VBoxContainer.new()
|
||||
answer_block.add_theme_constant_override("separation", 4)
|
||||
card_content.add_child(answer_block)
|
||||
@@ -240,13 +249,6 @@ func _build() -> void:
|
||||
card_position_label.visible = false
|
||||
position_row.add_child(card_position_label)
|
||||
|
||||
reveal_button = Button.new()
|
||||
reveal_button.text = "Show Answer (Space)"
|
||||
reveal_button.custom_minimum_size = Vector2(0, 52)
|
||||
_style_gradient_button(reveal_button)
|
||||
reveal_button.pressed.connect(_reveal)
|
||||
center_column.add_child(reveal_button)
|
||||
|
||||
grade_row = HBoxContainer.new()
|
||||
grade_row.add_theme_constant_override("separation", 10)
|
||||
grade_row.visible = false
|
||||
@@ -309,14 +311,6 @@ func _caption_label(text: String) -> Label:
|
||||
return label
|
||||
|
||||
|
||||
func _style_gradient_button(button: Button) -> void:
|
||||
button.add_theme_stylebox_override("normal", Brand.gradient_button_box())
|
||||
button.add_theme_stylebox_override("hover", Brand.gradient_button_box())
|
||||
button.add_theme_stylebox_override("pressed", Brand.gradient_button_box())
|
||||
button.add_theme_color_override("font_color", Color.WHITE)
|
||||
button.add_theme_color_override("font_hover_color", Color.WHITE)
|
||||
|
||||
|
||||
func _style_ghost_button(button: Button, dark: bool) -> void:
|
||||
var empty := StyleBoxEmpty.new()
|
||||
button.add_theme_stylebox_override("normal", empty)
|
||||
@@ -565,6 +559,7 @@ func refresh_theme_controls() -> void:
|
||||
extra_caption.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
extra_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
card_position_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
tap_hint_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||
_style_ghost_button(undo_button, dark)
|
||||
previous_button.add_theme_stylebox_override("normal", Brand.secondary_button_box(dark))
|
||||
previous_button.add_theme_stylebox_override("hover", Brand.secondary_button_box(dark, true))
|
||||
@@ -706,7 +701,7 @@ func _render_history_entry(index: int) -> void:
|
||||
_render_card()
|
||||
_reveal()
|
||||
grade_row.visible = false
|
||||
reveal_button.visible = false
|
||||
tap_hint_label.visible = false
|
||||
var is_graded_entry: bool = entry.get("action", "") == "graded"
|
||||
var is_last_graded_card: bool = str(entry.get("card_id", "")) == last_card_id
|
||||
var has_undoable_review: bool = not last_client_review_id.is_empty()
|
||||
@@ -811,7 +806,7 @@ func _render_loading() -> void:
|
||||
front_label.visible = true
|
||||
front_label.text = "Loading..."
|
||||
_hide_answer()
|
||||
reveal_button.visible = false
|
||||
tap_hint_label.visible = false
|
||||
grade_row.visible = false
|
||||
|
||||
|
||||
@@ -819,7 +814,7 @@ func _render_empty(card: Dictionary) -> void:
|
||||
empty_label.visible = true
|
||||
front_label.visible = false
|
||||
_hide_answer()
|
||||
reveal_button.visible = false
|
||||
tap_hint_label.visible = false
|
||||
grade_row.visible = false
|
||||
progress_label.text = "0 due"
|
||||
if typeof(card) == TYPE_DICTIONARY:
|
||||
@@ -844,7 +839,8 @@ func _render_card() -> void:
|
||||
front_label.text = str(current_note.get("prompt", ""))
|
||||
front_label.visible = true
|
||||
_hide_answer()
|
||||
reveal_button.visible = true
|
||||
divider.visible = true
|
||||
tap_hint_label.visible = true
|
||||
grade_row.visible = false
|
||||
progress_label.text = "%d due, %d new available" % [due_remaining, new_remaining]
|
||||
|
||||
@@ -866,13 +862,20 @@ func _reveal() -> void:
|
||||
extra_caption.visible = not extra.is_empty()
|
||||
extra_label.visible = not extra.is_empty()
|
||||
extra_label.text = extra
|
||||
reveal_button.visible = false
|
||||
tap_hint_label.visible = false
|
||||
grade_row.visible = true
|
||||
for rating in grade_buttons:
|
||||
var button: Button = grade_buttons[rating]
|
||||
button.text = "%s\n%s" % [rating.capitalize(), _interval_label_for(rating)]
|
||||
|
||||
|
||||
func _on_card_panel_input(event: InputEvent) -> void:
|
||||
if history_cursor != session_history.size() or revealed:
|
||||
return
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_reveal()
|
||||
|
||||
|
||||
func _interval_label_for(rating: String) -> String:
|
||||
for option in rating_options:
|
||||
if str(option.get("rating", "")) == rating:
|
||||
|
||||
@@ -135,8 +135,8 @@ func _run() -> void:
|
||||
screen._go_next()
|
||||
_assert(screen.front_label.text == "P3(live)" and screen.history_cursor == screen.session_history.size(),
|
||||
"stepping past the last history entry should return to the live card")
|
||||
_assert(screen.reveal_button.visible and not screen.grade_row.visible,
|
||||
"returning to a live, unrevealed card should restore the reveal prompt")
|
||||
_assert(screen.tap_hint_label.visible and not screen.grade_row.visible,
|
||||
"returning to a live, unrevealed card should restore the tap-to-reveal hint")
|
||||
|
||||
screen._go_next()
|
||||
_assert(screen.session_history.size() == 3 and screen.session_history[2]["action"] == "skipped" and screen.session_history[2]["card_id"] == "card-3",
|
||||
|
||||
@@ -29,7 +29,7 @@ func _initialize() -> void:
|
||||
]
|
||||
screen._render_card()
|
||||
_assert(screen.front_label.text == "2 + 2 = ?", "the front should show only the prompt before reveal")
|
||||
_assert(screen.reveal_button.visible, "the reveal button should be visible before the answer is shown")
|
||||
_assert(screen.tap_hint_label.visible, "the tap-to-reveal hint should be visible before the answer is shown")
|
||||
_assert(not screen.grade_row.visible, "grading buttons should be hidden before reveal")
|
||||
_assert(not screen.back_label.visible, "the back should stay hidden before reveal")
|
||||
|
||||
@@ -38,7 +38,7 @@ func _initialize() -> void:
|
||||
_assert(screen.extra_label.text == "Basic arithmetic" and screen.extra_label.visible, "the extra takeaway should render in its own labeled section")
|
||||
_assert(screen.answer_caption.visible and screen.extra_caption.visible, "revealing a card with an extra should show both the answer and takeaway captions")
|
||||
_assert(screen.grade_row.visible, "grading buttons should appear after reveal")
|
||||
_assert(not screen.reveal_button.visible, "the reveal button should hide once the card is revealed")
|
||||
_assert(not screen.tap_hint_label.visible, "the tap-to-reveal hint should hide once the card is revealed")
|
||||
_assert(screen.grade_buttons["good"].text.contains("1d"), "each grade button should show its computed next interval")
|
||||
|
||||
# Cloze card: only the card's own variant is hidden on the front; back reveals everything.
|
||||
@@ -60,6 +60,23 @@ func _initialize() -> void:
|
||||
screen._reveal()
|
||||
_assert(not screen.extra_label.visible and not screen.extra_caption.visible, "a card with no extra takeaway should hide that section instead of showing an empty caption")
|
||||
|
||||
# Tapping the card panel reveals the answer, same as pressing Space.
|
||||
screen.current_note = {"type": "basic", "prompt": "Tap test", "answer": "Tapped", "extra": ""}
|
||||
screen.current_card = {"id": "card-4", "variant": 0, "revision": 1}
|
||||
screen.revealed = false
|
||||
screen._render_card()
|
||||
var click := InputEventMouseButton.new()
|
||||
click.button_index = MOUSE_BUTTON_LEFT
|
||||
click.pressed = true
|
||||
screen._on_card_panel_input(click)
|
||||
_assert(screen.revealed and screen.back_label.text == "Tapped", "clicking the card panel should reveal the answer")
|
||||
|
||||
# A second tap after reveal (or while browsing history) is a no-op - it must not re-trigger
|
||||
# any reveal side effects (grading is guarded elsewhere; this only checks idempotence).
|
||||
screen.back_label.text = "Tapped"
|
||||
screen._on_card_panel_input(click)
|
||||
_assert(screen.back_label.text == "Tapped", "tapping an already-revealed card should be a no-op")
|
||||
|
||||
screen.free()
|
||||
quit(1 if failures > 0 else 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user