new features, play menu, deck builder, deck selection
This commit is contained in:
432
scripts/ui/CardFilterBar.gd
Normal file
432
scripts/ui/CardFilterBar.gd
Normal file
@@ -0,0 +1,432 @@
|
||||
class_name CardFilterBar
|
||||
extends Control
|
||||
|
||||
## CardFilterBar - Filter controls for the deck builder card grid
|
||||
|
||||
signal filters_changed(filters: Dictionary)
|
||||
|
||||
const FILTER_BAR_HEIGHT: float = 120.0
|
||||
const EXPANDED_HEIGHT: float = 200.0
|
||||
|
||||
var current_filters: Dictionary = {}
|
||||
var is_expanded: bool = false
|
||||
|
||||
# UI elements
|
||||
var search_field: LineEdit
|
||||
var element_buttons: Dictionary = {} # Enums.Element -> Button
|
||||
var type_dropdown: OptionButton
|
||||
var cost_slider: HSlider
|
||||
var cost_label: Label
|
||||
var expand_button: Button
|
||||
var expanded_container: Control
|
||||
|
||||
# Expanded filter elements
|
||||
var job_dropdown: OptionButton
|
||||
var category_dropdown: OptionButton
|
||||
var power_min_spin: SpinBox
|
||||
var power_max_spin: SpinBox
|
||||
var ex_burst_check: CheckBox
|
||||
var set_dropdown: OptionButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
custom_minimum_size = Vector2(0, FILTER_BAR_HEIGHT)
|
||||
_create_ui()
|
||||
_populate_dropdowns()
|
||||
|
||||
|
||||
func _create_ui() -> void:
|
||||
var panel = PanelContainer.new()
|
||||
panel.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
panel.add_theme_stylebox_override("panel", _create_panel_style())
|
||||
add_child(panel)
|
||||
|
||||
var main_vbox = VBoxContainer.new()
|
||||
main_vbox.add_theme_constant_override("separation", 8)
|
||||
panel.add_child(main_vbox)
|
||||
|
||||
# Row 1: Search and type
|
||||
var row1 = HBoxContainer.new()
|
||||
row1.add_theme_constant_override("separation", 12)
|
||||
main_vbox.add_child(row1)
|
||||
|
||||
# Search field
|
||||
var search_label = Label.new()
|
||||
search_label.text = "Search:"
|
||||
search_label.add_theme_font_size_override("font_size", 12)
|
||||
row1.add_child(search_label)
|
||||
|
||||
search_field = LineEdit.new()
|
||||
search_field.placeholder_text = "Card name..."
|
||||
search_field.custom_minimum_size = Vector2(180, 0)
|
||||
search_field.text_changed.connect(_on_search_changed)
|
||||
row1.add_child(search_field)
|
||||
|
||||
# Type dropdown
|
||||
var type_label = Label.new()
|
||||
type_label.text = "Type:"
|
||||
type_label.add_theme_font_size_override("font_size", 12)
|
||||
row1.add_child(type_label)
|
||||
|
||||
type_dropdown = OptionButton.new()
|
||||
type_dropdown.custom_minimum_size = Vector2(100, 0)
|
||||
type_dropdown.add_item("All", -1)
|
||||
type_dropdown.add_item("Forward", Enums.CardType.FORWARD)
|
||||
type_dropdown.add_item("Backup", Enums.CardType.BACKUP)
|
||||
type_dropdown.add_item("Summon", Enums.CardType.SUMMON)
|
||||
type_dropdown.add_item("Monster", Enums.CardType.MONSTER)
|
||||
type_dropdown.item_selected.connect(_on_type_selected)
|
||||
row1.add_child(type_dropdown)
|
||||
|
||||
# Cost slider
|
||||
var cost_container = HBoxContainer.new()
|
||||
cost_container.add_theme_constant_override("separation", 8)
|
||||
row1.add_child(cost_container)
|
||||
|
||||
var cost_title = Label.new()
|
||||
cost_title.text = "Max Cost:"
|
||||
cost_title.add_theme_font_size_override("font_size", 12)
|
||||
cost_container.add_child(cost_title)
|
||||
|
||||
cost_slider = HSlider.new()
|
||||
cost_slider.min_value = 1
|
||||
cost_slider.max_value = 14
|
||||
cost_slider.value = 14
|
||||
cost_slider.step = 1
|
||||
cost_slider.custom_minimum_size = Vector2(100, 0)
|
||||
cost_slider.value_changed.connect(_on_cost_changed)
|
||||
cost_container.add_child(cost_slider)
|
||||
|
||||
cost_label = Label.new()
|
||||
cost_label.text = "14"
|
||||
cost_label.custom_minimum_size = Vector2(24, 0)
|
||||
cost_label.add_theme_font_size_override("font_size", 12)
|
||||
cost_container.add_child(cost_label)
|
||||
|
||||
# Expand/collapse button
|
||||
expand_button = Button.new()
|
||||
expand_button.text = "More Filters"
|
||||
expand_button.custom_minimum_size = Vector2(100, 0)
|
||||
expand_button.pressed.connect(_toggle_expanded)
|
||||
_apply_button_style(expand_button)
|
||||
row1.add_child(expand_button)
|
||||
|
||||
# Clear filters button
|
||||
var clear_btn = Button.new()
|
||||
clear_btn.text = "Clear"
|
||||
clear_btn.custom_minimum_size = Vector2(60, 0)
|
||||
clear_btn.pressed.connect(_clear_filters)
|
||||
_apply_button_style(clear_btn)
|
||||
row1.add_child(clear_btn)
|
||||
|
||||
# Row 2: Element buttons
|
||||
var row2 = HBoxContainer.new()
|
||||
row2.add_theme_constant_override("separation", 6)
|
||||
main_vbox.add_child(row2)
|
||||
|
||||
var elem_label = Label.new()
|
||||
elem_label.text = "Elements:"
|
||||
elem_label.add_theme_font_size_override("font_size", 12)
|
||||
row2.add_child(elem_label)
|
||||
|
||||
for element in Enums.Element.values():
|
||||
var btn = Button.new()
|
||||
btn.text = Enums.element_to_string(element).substr(0, 3).to_upper()
|
||||
btn.custom_minimum_size = Vector2(44, 28)
|
||||
btn.toggle_mode = true
|
||||
btn.button_pressed = false
|
||||
btn.pressed.connect(_on_element_toggled.bind(element))
|
||||
_apply_element_button_style(btn, element)
|
||||
row2.add_child(btn)
|
||||
element_buttons[element] = btn
|
||||
|
||||
# Expanded filters container (hidden by default)
|
||||
expanded_container = VBoxContainer.new()
|
||||
expanded_container.visible = false
|
||||
expanded_container.add_theme_constant_override("separation", 8)
|
||||
main_vbox.add_child(expanded_container)
|
||||
|
||||
var expanded_row = HBoxContainer.new()
|
||||
expanded_row.add_theme_constant_override("separation", 16)
|
||||
expanded_container.add_child(expanded_row)
|
||||
|
||||
# Job dropdown
|
||||
var job_container = HBoxContainer.new()
|
||||
job_container.add_theme_constant_override("separation", 4)
|
||||
expanded_row.add_child(job_container)
|
||||
|
||||
var job_label_el = Label.new()
|
||||
job_label_el.text = "Job:"
|
||||
job_label_el.add_theme_font_size_override("font_size", 12)
|
||||
job_container.add_child(job_label_el)
|
||||
|
||||
job_dropdown = OptionButton.new()
|
||||
job_dropdown.custom_minimum_size = Vector2(120, 0)
|
||||
job_dropdown.item_selected.connect(_on_job_selected)
|
||||
job_container.add_child(job_dropdown)
|
||||
|
||||
# Category dropdown
|
||||
var cat_container = HBoxContainer.new()
|
||||
cat_container.add_theme_constant_override("separation", 4)
|
||||
expanded_row.add_child(cat_container)
|
||||
|
||||
var cat_label = Label.new()
|
||||
cat_label.text = "Category:"
|
||||
cat_label.add_theme_font_size_override("font_size", 12)
|
||||
cat_container.add_child(cat_label)
|
||||
|
||||
category_dropdown = OptionButton.new()
|
||||
category_dropdown.custom_minimum_size = Vector2(80, 0)
|
||||
category_dropdown.item_selected.connect(_on_category_selected)
|
||||
cat_container.add_child(category_dropdown)
|
||||
|
||||
# Power range
|
||||
var power_container = HBoxContainer.new()
|
||||
power_container.add_theme_constant_override("separation", 4)
|
||||
expanded_row.add_child(power_container)
|
||||
|
||||
var power_label_el = Label.new()
|
||||
power_label_el.text = "Power:"
|
||||
power_label_el.add_theme_font_size_override("font_size", 12)
|
||||
power_container.add_child(power_label_el)
|
||||
|
||||
power_min_spin = SpinBox.new()
|
||||
power_min_spin.min_value = 0
|
||||
power_min_spin.max_value = 20000
|
||||
power_min_spin.step = 1000
|
||||
power_min_spin.value = 0
|
||||
power_min_spin.custom_minimum_size = Vector2(70, 0)
|
||||
power_min_spin.value_changed.connect(_on_power_min_changed)
|
||||
power_container.add_child(power_min_spin)
|
||||
|
||||
var dash = Label.new()
|
||||
dash.text = "-"
|
||||
power_container.add_child(dash)
|
||||
|
||||
power_max_spin = SpinBox.new()
|
||||
power_max_spin.min_value = 0
|
||||
power_max_spin.max_value = 20000
|
||||
power_max_spin.step = 1000
|
||||
power_max_spin.value = 20000
|
||||
power_max_spin.custom_minimum_size = Vector2(70, 0)
|
||||
power_max_spin.value_changed.connect(_on_power_max_changed)
|
||||
power_container.add_child(power_max_spin)
|
||||
|
||||
# EX Burst checkbox
|
||||
ex_burst_check = CheckBox.new()
|
||||
ex_burst_check.text = "EX Burst only"
|
||||
ex_burst_check.add_theme_font_size_override("font_size", 12)
|
||||
ex_burst_check.toggled.connect(_on_ex_burst_toggled)
|
||||
expanded_row.add_child(ex_burst_check)
|
||||
|
||||
# Set/Opus dropdown
|
||||
var set_container = HBoxContainer.new()
|
||||
set_container.add_theme_constant_override("separation", 4)
|
||||
expanded_row.add_child(set_container)
|
||||
|
||||
var set_label = Label.new()
|
||||
set_label.text = "Set:"
|
||||
set_label.add_theme_font_size_override("font_size", 12)
|
||||
set_container.add_child(set_label)
|
||||
|
||||
set_dropdown = OptionButton.new()
|
||||
set_dropdown.custom_minimum_size = Vector2(70, 0)
|
||||
set_dropdown.item_selected.connect(_on_set_selected)
|
||||
set_container.add_child(set_dropdown)
|
||||
|
||||
|
||||
func _populate_dropdowns() -> void:
|
||||
# Populate job dropdown
|
||||
job_dropdown.add_item("All", -1)
|
||||
for job in CardDatabase.get_unique_jobs():
|
||||
job_dropdown.add_item(job)
|
||||
|
||||
# Populate category dropdown
|
||||
category_dropdown.add_item("All", -1)
|
||||
for category in CardDatabase.get_unique_categories():
|
||||
category_dropdown.add_item(category)
|
||||
|
||||
# Populate set dropdown
|
||||
set_dropdown.add_item("All", -1)
|
||||
for set_id in CardDatabase.get_card_sets():
|
||||
set_dropdown.add_item("Opus " + set_id, set_dropdown.item_count)
|
||||
|
||||
|
||||
func _create_panel_style() -> StyleBoxFlat:
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.1, 0.1, 0.14, 0.95)
|
||||
style.border_color = Color(0.4, 0.35, 0.25)
|
||||
style.set_border_width_all(1)
|
||||
style.set_corner_radius_all(4)
|
||||
style.content_margin_left = 12
|
||||
style.content_margin_right = 12
|
||||
style.content_margin_top = 8
|
||||
style.content_margin_bottom = 8
|
||||
return style
|
||||
|
||||
|
||||
func _apply_button_style(button: Button) -> void:
|
||||
var style_normal = StyleBoxFlat.new()
|
||||
style_normal.bg_color = Color(0.2, 0.2, 0.25)
|
||||
style_normal.border_color = Color(0.4, 0.4, 0.5)
|
||||
style_normal.set_border_width_all(1)
|
||||
style_normal.set_corner_radius_all(3)
|
||||
button.add_theme_stylebox_override("normal", style_normal)
|
||||
|
||||
var style_hover = StyleBoxFlat.new()
|
||||
style_hover.bg_color = Color(0.28, 0.28, 0.35)
|
||||
style_hover.border_color = Color(0.5, 0.45, 0.3)
|
||||
style_hover.set_border_width_all(1)
|
||||
style_hover.set_corner_radius_all(3)
|
||||
button.add_theme_stylebox_override("hover", style_hover)
|
||||
|
||||
button.add_theme_font_size_override("font_size", 12)
|
||||
|
||||
|
||||
func _apply_element_button_style(button: Button, element: Enums.Element) -> void:
|
||||
var element_color = Enums.element_to_color(element)
|
||||
|
||||
var style_normal = StyleBoxFlat.new()
|
||||
style_normal.bg_color = element_color.darkened(0.6)
|
||||
style_normal.border_color = element_color.darkened(0.3)
|
||||
style_normal.set_border_width_all(1)
|
||||
style_normal.set_corner_radius_all(3)
|
||||
button.add_theme_stylebox_override("normal", style_normal)
|
||||
|
||||
var style_hover = StyleBoxFlat.new()
|
||||
style_hover.bg_color = element_color.darkened(0.4)
|
||||
style_hover.border_color = element_color
|
||||
style_hover.set_border_width_all(2)
|
||||
style_hover.set_corner_radius_all(3)
|
||||
button.add_theme_stylebox_override("hover", style_hover)
|
||||
|
||||
var style_pressed = StyleBoxFlat.new()
|
||||
style_pressed.bg_color = element_color.darkened(0.2)
|
||||
style_pressed.border_color = Color.WHITE
|
||||
style_pressed.set_border_width_all(2)
|
||||
style_pressed.set_corner_radius_all(3)
|
||||
button.add_theme_stylebox_override("pressed", style_pressed)
|
||||
|
||||
button.add_theme_font_size_override("font_size", 10)
|
||||
|
||||
|
||||
func _toggle_expanded() -> void:
|
||||
is_expanded = not is_expanded
|
||||
expanded_container.visible = is_expanded
|
||||
expand_button.text = "Less Filters" if is_expanded else "More Filters"
|
||||
custom_minimum_size.y = EXPANDED_HEIGHT if is_expanded else FILTER_BAR_HEIGHT
|
||||
|
||||
|
||||
func _clear_filters() -> void:
|
||||
search_field.text = ""
|
||||
type_dropdown.select(0)
|
||||
cost_slider.value = 14
|
||||
for btn in element_buttons.values():
|
||||
btn.button_pressed = false
|
||||
job_dropdown.select(0)
|
||||
category_dropdown.select(0)
|
||||
power_min_spin.value = 0
|
||||
power_max_spin.value = 20000
|
||||
ex_burst_check.button_pressed = false
|
||||
set_dropdown.select(0)
|
||||
current_filters.clear()
|
||||
filters_changed.emit(current_filters)
|
||||
|
||||
|
||||
func _emit_filters() -> void:
|
||||
filters_changed.emit(current_filters)
|
||||
|
||||
|
||||
func _on_search_changed(text: String) -> void:
|
||||
if text.is_empty():
|
||||
current_filters.erase("name")
|
||||
else:
|
||||
current_filters["name"] = text
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_type_selected(index: int) -> void:
|
||||
var type_id = type_dropdown.get_item_id(index)
|
||||
if type_id == -1:
|
||||
current_filters.erase("type")
|
||||
else:
|
||||
current_filters["type"] = type_id
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_cost_changed(value: float) -> void:
|
||||
cost_label.text = str(int(value))
|
||||
if value >= 14:
|
||||
current_filters.erase("cost_max")
|
||||
else:
|
||||
current_filters["cost_max"] = int(value)
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_element_toggled(element: Enums.Element) -> void:
|
||||
var selected_elements: Array[Enums.Element] = []
|
||||
for elem in element_buttons:
|
||||
if element_buttons[elem].button_pressed:
|
||||
selected_elements.append(elem)
|
||||
|
||||
if selected_elements.is_empty():
|
||||
current_filters.erase("elements")
|
||||
else:
|
||||
current_filters["elements"] = selected_elements
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_job_selected(index: int) -> void:
|
||||
if index == 0:
|
||||
current_filters.erase("job")
|
||||
else:
|
||||
current_filters["job"] = job_dropdown.get_item_text(index)
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_category_selected(index: int) -> void:
|
||||
if index == 0:
|
||||
current_filters.erase("category")
|
||||
else:
|
||||
current_filters["category"] = category_dropdown.get_item_text(index)
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_power_min_changed(value: float) -> void:
|
||||
if value <= 0:
|
||||
current_filters.erase("power_min")
|
||||
else:
|
||||
current_filters["power_min"] = int(value)
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_power_max_changed(value: float) -> void:
|
||||
if value >= 20000:
|
||||
current_filters.erase("power_max")
|
||||
else:
|
||||
current_filters["power_max"] = int(value)
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_ex_burst_toggled(pressed: bool) -> void:
|
||||
if pressed:
|
||||
current_filters["ex_burst_only"] = true
|
||||
else:
|
||||
current_filters.erase("ex_burst_only")
|
||||
_emit_filters()
|
||||
|
||||
|
||||
func _on_set_selected(index: int) -> void:
|
||||
if index == 0:
|
||||
current_filters.erase("set")
|
||||
else:
|
||||
# Extract set number from "Opus X" text
|
||||
var text = set_dropdown.get_item_text(index)
|
||||
var set_num = text.replace("Opus ", "")
|
||||
current_filters["set"] = set_num + "-"
|
||||
_emit_filters()
|
||||
|
||||
|
||||
## Get current filters
|
||||
func get_filters() -> Dictionary:
|
||||
return current_filters.duplicate()
|
||||
Reference in New Issue
Block a user