433 lines
13 KiB
GDScript
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()
|