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