diff --git a/game/file_manager.py b/game/file_manager.py index 0680938..dc60282 100644 --- a/game/file_manager.py +++ b/game/file_manager.py @@ -7,7 +7,7 @@ from arcade.gui.experimental.scroll_area import UIScrollArea, UIScrollBar class FileManager(arcade.gui.UIAnchorLayout): def __init__(self, width, allowed_extensions): - super().__init__(size_hint=(0.95, 0.9), vertical=False) + super().__init__(size_hint=(0.95, 0.875), vertical=False) self.filemanager_width = width @@ -20,11 +20,11 @@ class FileManager(arcade.gui.UIAnchorLayout): self.content_cache = {} self.pre_cache_contents() - self.current_directory_label = self.add(arcade.gui.UILabel(text=self.current_directory, font_name="Roboto", font_size=24), anchor_x="center", anchor_y="top", align_y=-5) + self.current_directory_label = self.add(arcade.gui.UILabel(text=self.current_directory, font_name="Roboto", font_size=22), anchor_x="center", anchor_y="top", align_y=-10) self.scroll_area = UIScrollArea(size_hint=(0.665, 0.7)) # center on screen self.scroll_area.scroll_speed = -50 - self.add(self.scroll_area, anchor_x="center", anchor_y="center", align_y=self.filemanager_width * 0.05) + self.add(self.scroll_area, anchor_x="center", anchor_y="center", align_y=self.filemanager_width * 0.025) self.scrollbar = UIScrollBar(self.scroll_area) self.scrollbar.size_hint = (0.02, 1) @@ -33,21 +33,18 @@ class FileManager(arcade.gui.UIAnchorLayout): self.files_box = arcade.gui.UIBoxLayout(space_between=5) self.scroll_area.add(self.files_box) - self.bottom_box = self.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x="center", anchor_y="bottom", align_y=10) + self.bottom_box = self.add(arcade.gui.UIBoxLayout(space_between=5), anchor_x="center", anchor_y="bottom", align_y=5) - self.filename_label = self.bottom_box.add(arcade.gui.UILabel(text="Filename:", font_name="Roboto", font_size=20)) - self.filename_input = self.bottom_box.add(arcade.gui.UIInputText(width=self.filemanager_width * 0.35, height=self.filemanager_width * 0.025).with_border(color=arcade.color.WHITE)) - self.submit_button = self.bottom_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Submit", style=button_style, width=self.filemanager_width * 0.2, height=self.filemanager_width * 0.05)) + self.filename_label = self.bottom_box.add(arcade.gui.UILabel(text="Filename:", font_name="Roboto", font_size=17)) + self.filename_input = self.bottom_box.add(arcade.gui.UIInputText(width=self.filemanager_width * 0.35, height=self.filemanager_width * 0.02).with_border(color=arcade.color.WHITE)) + + self.submit_button = self.bottom_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Submit", style=button_style, width=self.filemanager_width * 0.5, height=self.filemanager_width * 0.025)) self.submit_button.on_click = lambda event: self.submit(self.current_directory) self.submit_button.visible = False self.filename_label.visible = False self.filename_input.visible = False - self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) - self.back_button.on_click = lambda event: self.exit() - self.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) - self.show_directory() def change_mode(self, mode): @@ -125,10 +122,6 @@ class FileManager(arcade.gui.UIAnchorLayout): def disable(self): self.parent.parent.disable() # The FileManager UIManager. self.parent is the FileManager UIAnchorLayout - def exit(self): - self.disable() - self.submitted_content = "exit" - def change_directory(self, directory): if directory.startswith("//"): # Fix / paths directory = directory[1:] diff --git a/game/play.py b/game/play.py index 74c44cb..50e01b1 100644 --- a/game/play.py +++ b/game/play.py @@ -3,7 +3,7 @@ import arcade, arcade.gui, pyglet, random, json from utils.preload import SPRITE_TEXTURES, button_texture, button_hovered_texture from utils.constants import button_style, dropdown_style, DO_RULES, IF_RULES, SHAPES, ALLOWED_INPUT, menu_background_color -from game.rules import RuleUIBox +from game.rules import RuleUI from game.sprites import BaseShape, Rectangle, Circle, Triangle from game.file_manager import FileManager @@ -19,7 +19,7 @@ class Game(arcade.gui.UIView): self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) - self.rules_box = RuleUIBox(self.window) + self.rules_box = RuleUI(self.window) self.file_manager = FileManager(self.window.width * 0.95, [".json"]).with_border() @@ -165,8 +165,7 @@ class Game(arcade.gui.UIView): def on_show_view(self): super().on_show_view() - self.rules_box.add_rule(None, ["on_left_click", "spawn"]) - self.rules_box.refresh_rules_display() + # self.rules_box.add_rule(None, ["on_left_click", "spawn"]) self.sprites_box.add(arcade.gui.UILabel(text="Sprites", font_size=24, text_color=arcade.color.WHITE), anchor_x="center", anchor_y="top") @@ -330,12 +329,18 @@ class Game(arcade.gui.UIView): self.triggered_events.append(["on_input", {"event_key": chr(symbol)}]) def on_mouse_press(self, x, y, button, modifiers): + if not self.mode == "simulation": + return + if button == arcade.MOUSE_BUTTON_LEFT: self.triggered_events.append(["on_left_click", {}]) elif self.mode == "simulation" and button == arcade.MOUSE_BUTTON_RIGHT: self.triggered_events.append(["on_right_click", {}]) def on_mouse_motion(self, x, y, button, modifiers): + if not self.mode == "simulation": + return + self.triggered_events.append(["on_mouse_move", {}]) def disable_previous(self): @@ -361,7 +366,7 @@ class Game(arcade.gui.UIView): self.mode = "export" self.file_manager.change_mode("export") - self.anchor.add(self.file_manager, anchor_x="center", anchor_y="top") + self.anchor.add(self.file_manager, anchor_x="center", anchor_y="top", align_y=-self.window.height * 0.025) def import_file(self): self.disable_previous() @@ -369,7 +374,7 @@ class Game(arcade.gui.UIView): self.mode = "import" self.file_manager.change_mode("import") - self.anchor.add(self.file_manager, anchor_x="center", anchor_y="top") + self.anchor.add(self.file_manager, anchor_x="center", anchor_y="top", align_y=-self.window.height * 0.025) def sprites(self): self.disable_previous() diff --git a/game/rules.py b/game/rules.py index c93a188..807e820 100644 --- a/game/rules.py +++ b/game/rules.py @@ -1,4 +1,17 @@ -from utils.constants import DO_RULES, IF_RULES, LOGICAL_OPERATORS, NON_COMPATIBLE_WHEN, NON_COMPATIBLE_DO_WHEN, VAR_NAMES, VAR_DEFAULT, VAR_OPTIONS, dropdown_style, slider_style +from utils.constants import ( + DO_RULES, + IF_RULES, + LOGICAL_OPERATORS, + NON_COMPATIBLE_WHEN, + NON_COMPATIBLE_DO_WHEN, + VAR_NAMES, + VAR_DEFAULT, + VAR_OPTIONS, + dropdown_style, + slider_style, + button_style, +) +from utils.preload import button_texture, button_hovered_texture import arcade, arcade.gui, random IF_KEYS = tuple(IF_RULES.keys()) @@ -7,249 +20,369 @@ DO_KEYS = tuple(DO_RULES.keys()) BAD_WHEN = {tuple(sorted(pair)) for pair in NON_COMPATIBLE_WHEN} BAD_DO_WHEN = {tuple(pair) for pair in NON_COMPATIBLE_DO_WHEN} -def generate_ruleset(ruleset_type): - when_a = random.choice(IF_KEYS) +def generate_if_rule(): + return random.choice(IF_KEYS) - if ruleset_type == "advanced": - valid_b = [ - b for b in IF_KEYS - if b != when_a and tuple(sorted((when_a, b))) not in BAD_WHEN - ] +def generate_do_rule(): + return random.choice(DO_KEYS) - if not valid_b: - return [when_a, random.choice(DO_KEYS)] +def generate_comparison(): + return random.choice(LOGICAL_OPERATORS) - when_b = random.choice(valid_b) - logical = random.choice(LOGICAL_OPERATORS) - else: - when_b = None - logical = None +def per_widget_height(height, widget_count): + return height // widget_count - if when_b: - valid_do = [ - d for d in DO_KEYS - if (when_a, d) not in BAD_DO_WHEN - and (when_b, d) not in BAD_DO_WHEN - and (d, when_a) not in BAD_DO_WHEN - and (d, when_b) not in BAD_DO_WHEN - ] - else: - valid_do = [ - d for d in DO_KEYS - if (when_a, d) not in BAD_DO_WHEN - and (d, when_a) not in BAD_DO_WHEN - ] +def cubic_bezier_point(p0, p1, p2, p3, t): + u = 1 - t + x = (u ** 3) * p0[0] + 3 * (u ** 2) * t * p1[0] + 3 * u * (t ** 2) * p2[0] + (t ** 3) * p3[0] + y = (u ** 3) * p0[1] + 3 * (u ** 2) * t * p1[1] + 3 * u * (t ** 2) * p2[1] + (t ** 3) * p3[1] + return x, y - do = random.choice(valid_do) +def cubic_bezier_points(p0, p1, p2, p3, segments=40): + return [cubic_bezier_point(p0, p1, p2, p3, i / segments) for i in range(segments + 1)] - if logical: - return [when_a, logical, when_b, do] - else: - return [when_a, do] - -class RuleUIBox(arcade.gui.UIBoxLayout): +def connection_between(p0, p3): + dx = p3[0] - p0[0] + offset = max(60, abs(dx) * 0.45) + c1 = (p0[0] + offset, p0[1]) + c2 = (p3[0] - offset, p3[1]) + + return cubic_bezier_points(p0, c1, c2, p3, segments=100) + +def get_rule_defaults(rule_type): + if rule_type == "if": + return { + rule_key: ( + rule_dict["description"].format_map( + { + VAR_NAMES[n]: VAR_NAMES[n] + for n, variable in enumerate(rule_dict["user_vars"]) + } + ), + { + VAR_NAMES[n]: VAR_DEFAULT[variable] + for n, variable in enumerate(rule_dict["user_vars"]) + }, + ) + for rule_key, rule_dict in IF_RULES.items() + } + + elif rule_type == "do": + return { + rule_key: ( + rule_dict["description"].format_map( + { + VAR_NAMES[n]: VAR_NAMES[n] + for n, variable in enumerate(rule_dict["user_vars"]) + } + ), + { + VAR_NAMES[n]: VAR_DEFAULT[variable] + for n, variable in enumerate(rule_dict["user_vars"]) + }, + ) + for rule_key, rule_dict in DO_RULES.items() + } + + +class ComparisonBox(arcade.gui.UITextureButton): + def __init__(self, x, y, comparison, rule_num): + super().__init__( + x=x, + y=y, + text=comparison, + style=button_style, + texture=button_texture, + texture_hovered=button_hovered_texture, + ) + + self.rule_num = rule_num + +class RuleBox(arcade.gui.UIBoxLayout): + def __init__(self, x, y, width, height, rule_num, rule_type, rule): + super().__init__(space_between=10, x=x, y=y, width=width, height=height) + + self.rule = rule + self.rule_num = rule_num + self.rule_type = rule_type + self.rule_dict = ( + IF_RULES[self.rule] if self.rule_type == "if" else DO_RULES[self.rule] + ) + self.defaults = get_rule_defaults(self.rule_type) + self.rule_values = {} + self.var_labels = {} + self.var_changers = {} + + self.per_widget_height = per_widget_height( + self.height, 2 + 2 * len(self.rule_dict["user_vars"]) + ) + + self.init_ui() + + def init_ui(self): + dropdown_options = [desc for desc, _ in self.defaults.values()] + self.desc_label = self.add( + arcade.gui.UIDropdown( + default=self.defaults[self.rule][0], + options=dropdown_options, + font_size=13, + active_style=dropdown_style, + primary_style=dropdown_style, + dropdown_style=dropdown_style, + width=self.width, + ) + ) + # self.desc_label.on_change = lambda event, rule_type=self.rule_type, rule_num=self.rule_num: self.change_rule_type(rule_num, rule_type, event.new_value) + + if self.rule_type == "do": + self.add_connection_button() + + for n, variable_type in enumerate(self.rule_dict["user_vars"]): + key = f"{variable_type}_{n}" + + defaults = get_rule_defaults(self.rule_type) + default_values = defaults[self.rule][1] + self.rule_values[key] = default_values[VAR_NAMES[n]] + + self.var_labels[key] = self.add( + arcade.gui.UILabel( + f"{VAR_NAMES[n]}: {self.rule_values[key]}", + font_size=11, + text_color=arcade.color.WHITE, + width=self.width, + height=self.per_widget_height, + ) + ) + + if variable_type in ["variable", "size"]: + slider = self.add( + arcade.gui.UISlider( + value=self.rule_values[key], + min_value=VAR_OPTIONS[variable_type][0], + max_value=VAR_OPTIONS[variable_type][1], + step=1, + style=slider_style, + width=self.width, + height=self.per_widget_height, + ) + ) + slider._render_steps = lambda surface: None + slider.on_change = ( + lambda event, + variable_type=variable_type, + n=n: self.change_var_value(variable_type, n, event.new_value) + ) + + self.var_changers[key] = slider + + else: + dropdown = self.add( + arcade.gui.UIDropdown( + default=self.rule_values[key], + options=VAR_OPTIONS[variable_type], + active_style=dropdown_style, + primary_style=dropdown_style, + dropdown_style=dropdown_style, + width=self.width, + height=self.per_widget_height, + ) + ) + dropdown.on_change = ( + lambda event, + variable_type=variable_type, + n=n: self.change_var_value(variable_type, n, event.new_value) + ) + + self.var_changers[key] = dropdown + + if self.rule_type == "if": + self.add_connection_button() + + def add_connection_button(self): + self.connection_button = self.add( + arcade.gui.UITextureButton( + text="+", + width=self.width, + style=button_style, + texture=button_texture, + texture_hovered=button_hovered_texture, + ) + ) + + def change_var_value(self, variable_type, n, value): + key = f"{variable_type}_{n}" + + self.rule_values[key] = value + + values = {} + for i, variable in enumerate(self.rule_dict["user_vars"]): + lookup_key = f"{variable}_{i}" + values[VAR_NAMES[i]] = self.rule_values.get( + lookup_key, VAR_DEFAULT[variable] + ) + + description = self.rule_dict["description"].format_map(values) + + self.desc_label.text = description + self.var_labels[key].text = f"{VAR_NAMES[n]}: {value}" + + +class RuleUI(arcade.gui.UIAnchorLayout): def __init__(self, window): - super().__init__(space_between=10, align="center", size_hint=(0.95, 0.75)) + super().__init__(size_hint=(0.95, 0.875)) + self.window = window - self.current_ruleset_num = 0 - self.current_ruleset_page = 0 - self.rulesets_per_page = 2 - self.rulesets = {} + self.current_rule_num = 0 self.rule_values = {} - self.rule_labels = {} - self.rule_var_changers = {} - self.rule_boxes = {} + self.dragged_rule_ui = None + self.rule_ui: dict[str, RuleBox | ComparisonBox] = {} + self.connections = [] + self.to_connect = [] - self.nav_buttons_box = None + self.rules_label = self.add( + arcade.gui.UILabel( + text="Rules", font_size=20, text_color=arcade.color.WHITE + ), + anchor_x="center", + anchor_y="top", + ) - self.rules_label = self.add(arcade.gui.UILabel(text="Rules", font_size=20, text_color=arcade.color.WHITE)) - self.add(arcade.gui.UISpace(height=self.window.height / 70, width=self.window.width * 0.25)) + self.add( + arcade.gui.UISpace( + height=self.window.height / 70, width=self.window.width * 0.25 + ) + ) - self.add_simple_rule_button = self.add(arcade.gui.UIFlatButton(text="Add Simple rule", width=self.window.width * 0.225, height=self.window.height / 25, style=dropdown_style)) - self.add_simple_rule_button.on_click = lambda event: self.add_rule("simple") + self.add_button_box = self.add( + arcade.gui.UIBoxLayout(space_between=10), + anchor_x="center", + anchor_y="bottom", + ) - self.add(arcade.gui.UISpace(height=self.window.height / 85)) + self.add_if_rule_button = self.add_button_box.add( + arcade.gui.UIFlatButton( + text="Add IF rule", + width=self.window.width * 0.225, + height=self.window.height / 25, + style=dropdown_style, + ) + ) + self.add_if_rule_button.on_click = lambda event: self.add_if_rule() - self.add_advanced_rule_button = self.add(arcade.gui.UIFlatButton(text="Add Advanced rule", width=self.window.width * 0.225, height=self.window.height / 25, style=dropdown_style)) - self.add_advanced_rule_button.on_click = lambda event: self.add_rule("advanced") - - self.add(arcade.gui.UISpace(height=self.window.height / 85)) + self.add_do_rule_button = self.add_button_box.add( + arcade.gui.UIFlatButton( + text="Add DO rule", + width=self.window.width * 0.225, + height=self.window.height / 25, + style=dropdown_style, + ) + ) + self.add_do_rule_button.on_click = lambda event: self.add_do_rule() - self.nav_buttons_box = self.add(arcade.gui.UIBoxLayout(vertical=False, space_between=10)) - - self.prev_button = self.nav_buttons_box.add(arcade.gui.UIFlatButton(text="Previous", width=self.window.width * 0.1, height=self.window.height / 25, style=dropdown_style)) - self.prev_button.on_click = self.prev_page - - self.next_button = self.nav_buttons_box.add(arcade.gui.UIFlatButton(text="Next", width=self.window.width * 0.1, height=self.window.height / 25, style=dropdown_style)) - self.next_button.on_click = self.next_page + self.add_comparison_button = self.add_button_box.add( + arcade.gui.UIFlatButton( + text="Add comparison", + width=self.window.width * 0.225, + height=self.window.height / 25, + style=dropdown_style, + ) + ) + self.add_comparison_button.on_click = lambda event: self.add_comparison() - self.rules_content_box = self.add(arcade.gui.UIBoxLayout(align="center")) + self.rule_space = self.add(arcade.gui.UIWidget(size_hint=(1, 1))) - def get_rule_defaults(self, rule_type): - if rule_type == "if": - return { - rule_key: ( - rule_dict["description"].format_map({VAR_NAMES[n]: VAR_NAMES[n] for n, variable in enumerate(rule_dict["user_vars"])}), - {VAR_NAMES[n]: VAR_DEFAULT[variable] for n, variable in enumerate(rule_dict["user_vars"])} - ) - for rule_key, rule_dict in IF_RULES.items() - } - elif rule_type == "do": - return { - rule_key: ( - rule_dict["description"].format_map({VAR_NAMES[n]: VAR_NAMES[n] for n, variable in enumerate(rule_dict["user_vars"])}), - {VAR_NAMES[n]: VAR_DEFAULT[variable] for n, variable in enumerate(rule_dict["user_vars"])} - ) - for rule_key, rule_dict in DO_RULES.items() - } - - def create_rule_ui(self, rule_box: arcade.gui.UIBoxLayout, rule, rule_type, rule_num=1, is_import=False): - defaults = self.get_rule_defaults(rule_type) - rule_dict = IF_RULES[rule] if rule_type == "if" else DO_RULES[rule] - ruleset_num = self.current_ruleset_num - default_values = defaults[rule][1] + # self.trash_image = self.add(arcade.gui.UIImage(texture=trash_texture), anchor_x="right", anchor_y="bottom") - dropdown_options = [desc for desc, _ in defaults.values()] - desc_label = rule_box.add(arcade.gui.UIDropdown(default=defaults[rule][0], options=dropdown_options, font_size=13, width=self.window.width * 0.225, active_style=dropdown_style, primary_style=dropdown_style, dropdown_style=dropdown_style)) - desc_label.on_change = lambda event, rule_type=rule_type, ruleset_num=ruleset_num, rule_num=rule_num: self.change_rule_type(ruleset_num, rule_num, rule_type, event.new_value) - self.rule_labels[f"{self.current_ruleset_num}_{rule_num}_desc"] = desc_label + def connection(self, rule_ui): + if len(self.to_connect) == 1: + rule_type = self.to_connect[0].rule_type - for n, variable_type in enumerate(rule_dict["user_vars"]): - key = f"{self.current_ruleset_num}_{rule_num}_{variable_type}_{n}" - - if not is_import: - self.rule_values[key] = default_values[VAR_NAMES[n]] - - label = rule_box.add(arcade.gui.UILabel(f'{VAR_NAMES[n]}: {self.rule_values[key]}', font_size=11, width=self.window.width * 0.225, height=self.window.height / 30)) - self.rule_labels[key] = label - - if variable_type in ["variable", "size"]: - slider = rule_box.add(arcade.gui.UISlider(value=self.rule_values[key], min_value=VAR_OPTIONS[variable_type][0], max_value=VAR_OPTIONS[variable_type][1], step=1, style=slider_style, width=self.window.width * 0.225, height=self.window.height / 30)) - slider._render_steps = lambda surface: None - slider.on_change = lambda event, variable_type=variable_type, rule=rule, rule_type=rule_type, ruleset_num=ruleset_num, rule_num=rule_num, n=n: self.change_rule_value(ruleset_num, rule_num, rule, rule_type, variable_type, n, event.new_value) - self.rule_var_changers[key] = slider - - else: - dropdown = rule_box.add(arcade.gui.UIDropdown(default=self.rule_values[key], options=VAR_OPTIONS[variable_type], active_style=dropdown_style, primary_style=dropdown_style, dropdown_style=dropdown_style, width=self.window.width * 0.225, height=self.window.height / 30)) - dropdown.on_change = lambda event, variable_type=variable_type, rule=rule, rule_type=rule_type, ruleset_num=ruleset_num, rule_num=rule_num, n=n: self.change_rule_value(ruleset_num, rule_num, rule, rule_type, variable_type, n, event.new_value) - self.rule_var_changers[key] = dropdown - - def change_rule_type(self, ruleset_num, rule_num, rule_type, new_rule_text): - defaults = self.get_rule_defaults(rule_type) - new_rule_name = next(key for key, default_list in defaults.items() if default_list[0] == new_rule_text) - - ruleset = self.rulesets[ruleset_num] - - if len(ruleset) == 2: - if rule_type == "if": - ruleset[0] = new_rule_name - else: - ruleset[1] = new_rule_name - else: - if rule_type == "if": - if rule_num == 1: - ruleset[0] = new_rule_name - else: - ruleset[2] = new_rule_name - else: - ruleset[3] = new_rule_name - - self.rebuild_ruleset_ui(ruleset_num) - - def rebuild_ruleset_ui(self, ruleset_num): - rule_box = self.rule_boxes[ruleset_num] - - keys_to_remove = [k for k in self.rule_labels.keys() if k.startswith(f"{ruleset_num}_")] - for key in keys_to_remove: - del self.rule_labels[key] - - keys_to_remove = [k for k in self.rule_var_changers.keys() if k.startswith(f"{ruleset_num}_")] - for key in keys_to_remove: - del self.rule_var_changers[key] - - keys_to_remove = [k for k in self.rule_values.keys() if k.startswith(f"{ruleset_num}_")] - for key in keys_to_remove: - del self.rule_values[key] - - rule_box.clear() - - ruleset = self.rulesets[ruleset_num] - old_ruleset_num = self.current_ruleset_num - self.current_ruleset_num = ruleset_num - - if len(ruleset) == 2: - self.create_rule_ui(rule_box, ruleset[0], "if") - self.create_rule_ui(rule_box, ruleset[1], "do", 2) - else: - self.create_rule_ui(rule_box, ruleset[0], "if") - rule_box.add(arcade.gui.UILabel(ruleset[1].upper(), font_size=14, width=self.window.width * 0.25)) - self.create_rule_ui(rule_box, ruleset[2], "if", 2) - self.create_rule_ui(rule_box, ruleset[3], "do", 3) - - self.current_ruleset_num = old_ruleset_num - - def add_ruleset(self, ruleset, is_import=False): - rule_box = arcade.gui.UIBoxLayout(space_between=5, align="left") - self.rule_boxes[self.current_ruleset_num] = rule_box - - if len(ruleset) == 2: - self.rulesets[self.current_ruleset_num] = ruleset + if (rule_type == "if" and rule_ui.rule_type == "if") or (rule_type == "do" and rule_type in ["do", "comparison"]): + return - self.create_rule_ui(rule_box, ruleset[0], "if", 1, is_import) - self.create_rule_ui(rule_box, ruleset[1], "do", 2, is_import) + self.to_connect.append(rule_ui.rule_num) - else: - self.rulesets[self.current_ruleset_num] = ruleset + if len(self.to_connect) == 2: + self.connections.append(self.to_connect) + self.to_connect = [] - self.create_rule_ui(rule_box, ruleset[0], "if", 1, is_import) - rule_box.add(arcade.gui.UILabel(ruleset[1].upper(), font_size=14, width=self.window.width * 0.25)) - self.create_rule_ui(rule_box, ruleset[2], "if", 2, is_import) - self.create_rule_ui(rule_box, ruleset[3], "do", 3, is_import) + @property + def rulesets(self): # dinamically generate them? maybe bad idea? + return {} - def refresh_rules_display(self): - self.rules_content_box.clear() + def generate_pos(self): + return random.randint( + self.window.width * 0.1, int(self.window.width * 0.9) + ), random.randint(self.window.height * 0.1, int(self.window.height * 0.7)) - sorted_keys = sorted(self.rule_boxes.keys()) - start_idx = self.current_ruleset_page * self.rulesets_per_page - end_idx = start_idx + self.rulesets_per_page - visible_keys = sorted_keys[start_idx:end_idx] + def add_if_rule(self): + rule_box = RuleBox( + *self.generate_pos(), + self.window.width * 0.2, + self.window.height * 0.1, + self.current_rule_num, + "if", + generate_if_rule(), + ) - for key in visible_keys: - self.rules_content_box.add(self.rule_boxes[key]) - self.rules_content_box.add(arcade.gui.UISpace(height=self.window.height / 50)) + self.rule_space.add(rule_box) + self.rule_ui[self.current_rule_num] = rule_box + self.current_rule_num += 1 - def next_page(self, event): - sorted_keys = sorted(self.rule_boxes.keys()) - max_page = (len(sorted_keys) - 1) // self.rulesets_per_page - if self.current_ruleset_page < max_page: - self.current_ruleset_page += 1 - self.refresh_rules_display() + def add_do_rule(self): + rule_box = RuleBox( + *self.generate_pos(), + self.window.width * 0.2, + self.window.height * 0.1, + self.current_rule_num, + "do", + generate_do_rule(), + ) - def prev_page(self, event): - if self.current_ruleset_page > 0: - self.current_ruleset_page -= 1 - self.refresh_rules_display() + self.rule_space.add(rule_box) + self.rule_ui[self.current_rule_num] = rule_box + self.current_rule_num += 1 - def change_rule_value(self, ruleset_num, rule_num, rule, rule_type, variable_type, n, value): - rule_dict = IF_RULES[rule] if rule_type == "if" else DO_RULES[rule] - key = f"{ruleset_num}_{rule_num}_{variable_type}_{n}" - - self.rule_values[key] = value - - values = {} - for i, variable in enumerate(rule_dict["user_vars"]): - lookup_key = f"{ruleset_num}_{rule_num}_{variable}_{i}" - values[VAR_NAMES[i]] = self.rule_values.get(lookup_key, VAR_DEFAULT[variable]) - - description = rule_dict["description"].format_map(values) - - self.rule_labels[f"{ruleset_num}_{rule_num}_desc"].text = description - self.rule_labels[key].text = f'{VAR_NAMES[n]}: {value}' + def add_comparison(self): + comparison_box = ComparisonBox( + *self.generate_pos(), generate_comparison(), self.current_rule_num + ) - def add_rule(self, ruleset_type=None, force=None): - self.rulesets[self.current_ruleset_num] = generate_ruleset(ruleset_type) if not force else force - self.add_ruleset(self.rulesets[self.current_ruleset_num]) - self.current_ruleset_num += 1 - if self.rules_content_box: - self.refresh_rules_display() \ No newline at end of file + self.rule_ui[self.current_rule_num] = comparison_box + self.add(comparison_box) + self.current_rule_num += 1 + + def draw(self): + self.bezier_points = [] + + for conn in self.connections: + start_id, end_id = conn + start_rule_ui = self.rule_ui[start_id] + end_rule_ui = self.rule_ui[end_id] + + start_pos = start_rule_ui.top if start_rule_ui.rule_type == "do" else start_rule_ui.bottom + end_pos = end_rule_ui.top if end_rule_ui.rule_type == "do" else end_rule_ui.bottom + + points = self.connection_between(start_pos, end_pos) + self.bezier_points.append(points) + + arcade.draw_line_strip(points, arcade.color.WHITE, 6) + + def on_event(self, event): + super().on_event(event) + + if isinstance(event, arcade.gui.UIMouseDragEvent): + if self.dragged_rule_ui is not None: + self.dragged_rule_ui.center_x += event.dx + self.dragged_rule_ui.center_y += event.dy + elif isinstance(event, arcade.gui.UIMouseReleaseEvent): + self.dragged_rule_ui = None + elif isinstance(event, arcade.gui.UIMousePressEvent): + if event.button == arcade.MOUSE_BUTTON_RIGHT: + ... + + elif event.button == arcade.MOUSE_BUTTON_LEFT: + for rule_ui in self.rule_ui.values(): + if rule_ui.rect.point_in_rect((event.x, event.y)): + self.dragged_rule_ui = rule_ui