Files
FFCardGame/scripts/ui/CardFilterBar.gd

433 lines
13 KiB
GDScript

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