From 4e9d3c075943a3f06579fce7d85fab017c717e6e Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Sat, 30 Jan 2021 21:27:16 -0600 Subject: [PATCH] Initial implementation of search, updated PacketHandler to behave more more like android app --- lib/backend.rb | 24 +++ lib/pages/search.rb | 305 ++++++++++++++++++++++++++++- lib/states/boot.rb | 2 +- lib/tacnet/packet_handler.rb | 68 ++++--- timecrafters_configuration_tool.rb | 4 +- 5 files changed, 366 insertions(+), 37 deletions(-) diff --git a/lib/backend.rb b/lib/backend.rb index 0992d04..83d9701 100644 --- a/lib/backend.rb +++ b/lib/backend.rb @@ -41,6 +41,30 @@ module TAC @config_changed = false end + def move_config(old_name, new_name) + if not File.exists?("#{TAC::CONFIGS_PATH}/#{old_name}.json") or + File.directory?("#{TAC::CONFIGS_PATH}/#{old_name}.json") + # move_config: Can not move config file "#{old_name}" does not exist! + return false + end + + if File.exists?("#{TAC::CONFIGS_PATH}/#{new_name}.json") && + !File.directory?("#{TAC::CONFIGS_PATH}/#{old_name}.json") + # move_config: Config file "#{new_name}" already exist! + return false + end + + return FileUtils.mv( + "#{TAC::CONFIGS_PATH}/#{old_name}.json", + "#{TAC::CONFIGS_PATH}/#{new_name}.json" + ) + end + + def delete_config(config_name) + FileUtils.rm("#{TAC::CONFIGS_PATH}/#{config_name}.json") if File.exists?("#{TAC::CONFIGS_PATH}/#{config_name}.json") + end + + def upload_config(config_name) if @tacnet.connected? json = Config.new(config_name).to_json diff --git a/lib/pages/search.rb b/lib/pages/search.rb index ff93faf..43a5d25 100644 --- a/lib/pages/search.rb +++ b/lib/pages/search.rb @@ -7,14 +7,311 @@ module TAC menu_bar.clear do search = edit_line "", width: 0.9, height: 1.0 button get_image("#{TAC::ROOT_PATH}/media/icons/zoom.png"), image_height: 1.0 do - # do search - body.clear do - label "Search results for: #{search.value.strip}" - label "TODO: Search Results." + unless search.value.strip.empty? + search_results = search_config(search.value.downcase.strip) + + body.clear do + flow(width: 1.0, height: 1.0) do + stack(width: 0.495, height: 1.0) do + shared_index = 0 + if search_results.results.size.zero? + subtitle "No results for: \"#{search.value.strip}\"" + else + subtitle "Search results for: \"#{search.value.strip}\"" + end + + if search_results.groups.size.positive? + title "Groups" + + search_results.groups.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button result.highlight(result.group.name), width: 1.0 + end + + shared_index += 1 + end + end + + if search_results.actions.size.positive? + title "Actions" + + search_results.actions.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button result.highlight(result.action.name), width: 1.0 + + if result.from_comment? + para result.highlight(result.action.comment), width: 1.0 + end + end + + shared_index += 1 + end + end + + if search_results.variables.size.positive? + title "Variables" + + search_results.variables.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button "#{result.highlight(result.variable.name)} [#{result.highlight(result.variable.value)}]", width: 1.0 + end + + shared_index += 1 + end + end + end + + stack(width: 0.495, height: 1.0) do + if search_results.group_presets.size.positive? + title "Group Presets" + + search_results.group_presets.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button result.highlight(result.group.name), width: 1.0 + end + + shared_index += 1 + end + end + + + if search_results.action_presets.size.positive? + title "Action Presets" + + search_results.action_presets.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button result.highlight(result.action.name), width: 1.0 + + if result.from_comment? + para result.highlight(result.action.comment), width: 1.0 + end + end + + shared_index += 1 + end + end + + if search_results.variables_from_presets.size.positive? + title "Variables from Presets" + + search_results.variables_from_presets.each do |result| + stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do + background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR + button "#{result.highlight(result.variable.name)} [#{result.highlight(result.variable.value)}]", width: 1.0 + end + + shared_index += 1 + end + end + end + end + end end end end end + + def search_config(query) + search_results = SearchResults.new + + search_groups(query, search_results) + search_actions(query, search_results) + search_variables(query, search_results) + search_presets(query, search_results) + + return search_results + end + + def search_groups(query, search_results) + window.backend.config.groups.each do |group| + if group.name.downcase.include?(query) + result = SearchResult.new(group: group, query: query, is_group: true, is_from_name: true) + search_results.results << result + end + end + end + + def search_actions(query, search_results) + window.backend.config.groups.each do |group| + group.actions.each do |action| + if action.name.downcase.include?(query) + result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_name: true) + search_results.results << result + end + + if action.comment.downcase.include?(query) + result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_comment: true) + search_results.results << result + end + end + end + end + + def search_variables(query, search_results) + window.backend.config.groups.each do |group| + group.actions.each do |action| + action.variables.each do |variable| + if variable.name.downcase.include?(query) + result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_name: true) + search_results.results << result + end + + if variable.value.downcase.include?(query) + result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_value: true) + search_results.results << result + end + end + end + end + end + + def search_presets(query, search_results) + window.backend.config.presets.groups.each do |group| + if group.name.downcase.include?(query) + result = SearchResult.new(group: group, query: query, is_group: true, is_from_name: true, is_preset: true) + search_results.results << result + end + + group.actions.each do |action| + if action.name.downcase.include?(query) + result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_name: true, is_preset: true) + search_results.results << result + end + + if action.comment.downcase.include?(query) + result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_comment: true, is_preset: true) + search_results.results << result + end + + action.variables.each do |variable| + if variable.name.downcase.include?(query) + result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_name: true, is_preset: true) + search_results.results << result + end + + if variable.value.downcase.include?(query) + result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_value: true, is_preset: true) + search_results.results << result + end + end + end + end + + window.backend.config.presets.actions.each do |action| + if action.name.downcase.include?(query) + result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_name: true, is_preset: true) + search_results.results << result + end + + if action.comment.downcase.include?(query) + result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_comment: true, is_preset: true) + search_results.results << result + end + + action.variables.each do |variable| + if variable.name.downcase.include?(query) + result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_name: true, is_preset: true) + search_results.results << result + end + + if variable.value.downcase.include?(query) + result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_value: true, is_preset: true) + search_results.results << result + end + end + end + end + + class SearchResults + attr_reader :results + + def initialize + @results = [] + end + + def groups + @results.select { |result| result.group? && !result.preset? } + end + + def actions + @results.select { |result| result.action? && !result.preset? } + end + + def variables + @results.select { |result| result.variable? && !result.preset? } + end + + def group_presets + @results.select { |result| result.group? && result.preset? } + end + + def action_presets + @results.select { |result| result.action? && result.preset? } + end + + def variables_from_presets + @results.select { |result| result.variable? && result.preset? } + end + end + + class SearchResult + attr_reader :group, :action, :variable, :query + + def initialize(query:, group:, action: nil, variable: nil, + is_group: false, is_action: false, is_variable: false, + is_from_name: false, is_from_value: false, is_from_comment: false, is_preset: false) + @group = group + @action = action + @variable = variable + @query = query + + @is_group = is_group + @is_action = is_action + @is_variable = is_variable + + @is_from_name = is_from_name + @is_from_value = is_from_value + @is_from_comment = is_from_comment + @is_preset = is_preset + end + + def group? + @is_group + end + + def action? + @is_action + end + + def variable? + @is_variable + end + + def from_name? + @is_from_name + end + + def from_value? + @is_from_value + end + + def from_comment? + @is_from_comment + end + + def preset? + @is_preset + end + + def highlight(string) + string.gsub(/#{@query}/i, "#{@query}") + end + end end end end \ No newline at end of file diff --git a/lib/states/boot.rb b/lib/states/boot.rb index 0630579..a16be7f 100644 --- a/lib/states/boot.rb +++ b/lib/states/boot.rb @@ -12,7 +12,7 @@ module TAC @animator = CyberarmEngine::Animator.new(start_time: 0, duration: 3_000, from: 0, to: 255) @transition_color = Gosu::Color.new(0x00_000000) - @next_state = ARGV.include?("--redesign") ? NewEditor : Editor + @next_state = USE_REDESIGN ? NewEditor : Editor end def draw diff --git a/lib/tacnet/packet_handler.rb b/lib/tacnet/packet_handler.rb index d44a62d..210485d 100644 --- a/lib/tacnet/packet_handler.rb +++ b/lib/tacnet/packet_handler.rb @@ -68,40 +68,16 @@ module TAC config_name, json = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2) data = JSON.parse(json, symbolize_names: true) - if @host_is_a_connection - if data.is_a?(Array) - # OLDEST CONFIG, upgrade? - $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config to old.") - - elsif data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION - File.open("#{TAC::CONFIGS_PATH}/#{config_name}.json", "w") { |f| f.write json } - - if $window.backend.config.name == config_name - $window.backend.load_config(config_name) - - $window.instance_variable_get(:"@states").each do |state| - state.populate_groups_list if state.is_a?(TAC::States::Editor) - end - end - - elsif data.is_a?(Hash) && data.dig(:config, :spec_version) < TAC::CONFIG_SPEC_VERSION - # OLD CONFIG, Upgrade? - $window.push_state(TAC::Dialog::ConfirmDialog, title: "Upgrade Config", message: "Remote config is an older\nspec version.\nTry to upgrade?", callback_method: proc {}) - - elsif data.is_a?(Hash) && data.dig(:config, :spec_version) > TAC::CONFIG_SPEC_VERSION - # NEWER CONFIG, Error Out - $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Client outdated, check for\nupdates.\nSupported config spec:\nv#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}") + if data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION + File.open("#{TAC::CONFIGS_PATH}/#{config_name}.json", "w") { |f| f.write json } + if $window.backend.config&.name == config_name + $window.backend.load_config(config_name) else - # CONFIG is unknown - $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config is not supported.") - end - - else - if data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION - File.open("#{TAC::CONFIGS_PATH}/#{config_name}.json", "w") { |f| f.write json } - end + $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Supported config spec: v#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}") + end end + rescue JSON::ParserError => e log.e(TAG, "JSON parsing error: #{e}") end @@ -164,15 +140,45 @@ module TAC end def handle_select_config(packet) + config_name = packet.body + + $window.backend.settings.config = config_name + $window.backend.save_settings + $window.backend.load_config(config_name) end def handle_add_config(packet) + config_name = packet.body + + if $window.backend.configs_list.include?(config_name) + unless @host_is_a_connection + if $server.active_client&.connected? + $server.active_client.puts(PacketHandler.packet_error("Config already exists!", "A config with the name #{config_name} already exists over here.")) + end + end + else + $window.backend.write_new_config(config_name) + end end def handle_update_config(packet) + old_config_name, new_config_name = packet.body.split(PROTOCOL_SEPERATOR, 2) + + if $window.backend.configs_list.include?(config_name) + unless @host_is_a_connection + if $server.active_client&.connected? + $server.active_client.puts(PacketHandler.packet_error("Config already exists!", "A config with the name #{config_name} already exists over here.")) + end + end + else + $window.backend.move_config(old_config_name, new_config_name) + end end def handle_delete_config(packet) + config_name = packet.body + + $window.backend.delete_config(config_name) end def self.packet_handshake(client_uuid) diff --git a/timecrafters_configuration_tool.rb b/timecrafters_configuration_tool.rb index f6df60c..50c40f9 100644 --- a/timecrafters_configuration_tool.rb +++ b/timecrafters_configuration_tool.rb @@ -47,6 +47,8 @@ require_relative "lib/tacnet/server" # Thread.abort_on_exception = true +USE_REDESIGN = ARGV.include?("--redesign") + if not defined?(Ocra) - TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true, borderless: true).show + TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true, borderless: USE_REDESIGN).show end