DEV Community

Andy Maleh
Andy Maleh

Posted on

Glimmer Wordle 1.1.5 Windows Support

Glimmer Wordle 1.1.5 (open-source desktop game) has just been released with official support for Windows. Although the game worked on Windows before, thanks to the platform-independent Glimmer DSL for SWT GUI library it was built with, styling was not tweaked for Windows till now in version 1.1.5.

Windows Wordle

To give you an idea of how the game flows, here is an animated Gif with it running on the Mac:

Mac Wordle Video

Here is the Glimmer code of the GUI View:

class Wordle module View class AppView include Glimmer::UI::CustomShell COLOR_TO_BACKGROUND_COLOR_MAP = { green: rgb(106, 170, 100), yellow: rgb(201, 180, 88), gray: rgb(120, 124, 126), } COLOR_TO_TEXT_COLOR_MAP = { green: :white, yellow: :white, gray: :white, } ALPHABET_LAYOUTS = { alphabetical: [ %w[A B C D E F G H I J], %w[K L M N O P Q R S], %w[T U V W X Y Z], ], qwerty: [ %w[Q W E R T Y U I O P], %w[A S D F G H J K L], %w[Z X C V B N M], ], } CONFIG_FILE = File.join(Dir.home, '.glimmer_wordle') before_body do @display = display { on_about { display_about_dialog } on_preferences { display_about_dialog } on_swt_keydown do |key_event| if key_event.keyCode == 8 do_backspace elsif key_event.keyCode == swt(:arrow_left) do_left elsif key_event.keyCode == swt(:arrow_right) do_right elsif key_event.keyCode == swt(:cr) if @five_letter_word.status == :in_progress do_guess else do_restart end elsif valid_character?((key_event.keyCode.chr rescue '')) do_type(key_event.keyCode.chr) end end } @five_letter_word = Model::FiveLetterWord.new config = load_config @alphabet_layout = config[:alphabet_layout] || :alphabetical end ## Add widget content inside custom shell body ## Top-most widget must be a shell or another custom shell # body { shell(:no_resize) { grid_layout { margin_width 10 margin_height 10 vertical_spacing 0 } # Replace example content below with custom shell content minimum_size 420, 540 image File.join(APP_ROOT, 'icons', 'windows', "Glimmer Wordle.ico") if OS.windows? image File.join(APP_ROOT, 'icons', 'linux', "Glimmer Wordle.png") unless OS.windows? text "Glimmer Wordle" background :white app_menu_bar alphabet_container label { layout_data :center, :center, true, false text 'You have 6 tries to guess a 5-letter word' background :transparent if OS.windows? } word_guesser guess_button } } def app_menu_bar menu_bar { menu { text '&Game' menu_item { text '&Restart' on_widget_selected { do_restart } } menu_item { text 'E&xit' on_widget_selected { exit(0) } } } menu { text '&View' menu { text 'Alphabet &Layout' menu_item(:radio) { text '&Alphabetical' selection @alphabet_layout == :alphabetical on_widget_selected { self.alphabet_layout = :alphabetical rebuild_alphabet_container } } menu_item(:radio) { text '&Qwerty' selection @alphabet_layout == :qwerty on_widget_selected { self.alphabet_layout = :qwerty rebuild_alphabet_container } } } } menu { text '&Help' menu_item { text '&Instructions' on_widget_selected { display_instructions_dialog } } menu_item { text '&About' on_widget_selected { display_about_dialog } } } } end def display_instructions_dialog message_box(body_root) { text 'Instructions' message <<~MULTI_LINE_STRING Make 6 guesses for a 5-letter word. If you enter a letter that is part of the word, and at the right location, it becomes green, If you enter a letter that is part of the word, but at the wrong location, it becomes yellow. If you enter a letter that is not part of the word, it becomes red.  MULTI_LINE_STRING }.open end def display_about_dialog message_box(body_root) { text 'About' message "Glimmer Wordle #{VERSION}\n\n#{LICENSE}" }.open end def alphabet_container @alphabet_container = composite { layout_data(:center, :center, true, false) grid_layout { margin_width 0 margin_height 0 vertical_spacing 0 } background :white alphabets } end def alphabets alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][0]) { layout_data(:center, :center, true, false) { width_hint 318 height_hint 50 } } alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][1]) { layout_data(:center, :center, true, false) { width_hint 288 height_hint 50 } } alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][2]) { layout_data(:center, :center, true, false) { width_hint 222 height_hint 50 } } end def alphabet_row(alphabet_characters, &block) canvas { block.call background :white @alphabet_rectangles ||= [] @alphabet_borders ||= [] @alphabet_letters ||= [] alphabet_characters.each_with_index do |alphabet_character, i| @alphabet_rectangles << rectangle(1 + i*32, @alphabet_row_offset_y, 28, 28) { background :transparent @alphabet_borders << rectangle { foreground :gray line_width 2 } @alphabet_letters << text(alphabet_character, :default, [:default, OS.linux? ? 5 : (OS.windows? ? 1 : 0)]) { font alphabet_font } } end } end def alphabet_layout_alphabets ALPHABET_LAYOUTS[@alphabet_layout].reduce(:+) end def word_guesser @canvasses ||= [] margin_x = 5 margin_y = 5 @canvasses << canvas { layout_data(:center, :center, true, false) { width_hint 230 height_hint 50 } background :white focus true @rectangles = [] @borders = [] @letters = [] 5.times do |i| @rectangles << rectangle(margin_x + i*45, margin_y, 40, 40) { background :transparent @borders << rectangle { foreground i == 0 ? :title_background : :gray line_width 2 } @letters << text('', :default, [:default, OS.linux? ? 6 : (OS.windows? ? 1 : 0)]) { font letter_font } } end } end def guess_button @guess_button = button { layout_data :center, :center, true, false text 'Guess' on_widget_selected do do_guess end } end def do_backspace @letter = @letters[highlighted_letter_index] if @letter.string != '' index_to_delete = highlighted_letter_index else index_to_delete = [highlighted_letter_index - 1, 0].max end @letter = @letters[index_to_delete] @letter.string = '' @borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret @borders[index_to_delete].foreground = :title_background end def do_guess return if !word_filled_up? word = @letters.map(&:string).join if invalid_word?(word) message_box { text 'Invalid Word' message "The word you entered is not an allowed guess!\n\nPlease try another word!" }.open return end guess_result = @five_letter_word.guess(word) update_guess_word_background_colors(guess_result) update_alphabet_background_colors if @five_letter_word.status == :in_progress @guess_button.dispose body_root.content { word_guesser guess_button } else @guess_button.dispose body_root.content { @restart_button = button { layout_data :center, :center, true, false text 'Restart' focus true on_widget_selected do do_restart end } } display_share_text_dialog end body_root.layout(true, true) body_root.pack(true) end def highlighted_letter_index @borders.each_with_index.find {|border, i| border.foreground.first == color(:title_background).swt_color }.last end def do_type(character) index = highlighted_letter_index @letter = @letters[index] @letter.string = character.upcase if @letters.any? {|letter| letter.string == ''} @borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret @borders[index == 4 ? 4 : index + 1].foreground = :title_background end end def do_left index = [highlighted_letter_index - 1, 0].max @borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret @borders[index].foreground = :title_background end def do_right index = [highlighted_letter_index + 1, @letters.count - 1].min @borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret @borders[index].foreground = :title_background end def do_restart @share_text_dialog&.close alphabet_layout_alphabets.each_with_index do |alphabet_character, i| @alphabet_borders[i].foreground = :gray @alphabet_rectangles[i].background = :white @alphabet_letters[i].foreground = :black end @restart_button&.dispose @canvasses.dup.each(&:dispose) @canvasses.clear @guess_button&.dispose body_root.content { word_guesser guess_button } body_root.layout(true, true) body_root.pack(true) @five_letter_word.refresh end def update_guess_word_background_colors(guess_result) guess_result.each_with_index do |result_color, i| background_color = COLOR_TO_BACKGROUND_COLOR_MAP[result_color] @borders[i].foreground = background_color @rectangles[i].background = background_color @letters[i].foreground = COLOR_TO_TEXT_COLOR_MAP[result_color] async_exec { @canvasses.last.redraw } end end def update_alphabet_background_colors alphabet_layout_alphabets.each_with_index do |alphabet_character, i| result_color = @five_letter_word.colored_alphabets[alphabet_character.downcase] if result_color background_color = COLOR_TO_BACKGROUND_COLOR_MAP[result_color] @alphabet_borders[i].foreground = background_color @alphabet_rectangles[i].background = background_color @alphabet_letters[i].foreground = COLOR_TO_TEXT_COLOR_MAP[result_color] end end end def word_filled_up? @letters.find {|letter| letter.string == ''}.nil? end def invalid_word?(word) !Model::FiveLetterWord::WORLD_ALLOWED_GUESSES.include?(word.downcase) end def valid_character?(character) ((65..90).to_a + (97..122).to_a).map {|n| n.chr}.include?(character) end def display_share_text_dialog result = "#{@five_letter_word.answer.upcase}\n\n#{emoji_result}" Clipboard.copy(result) @share_text_dialog = dialog(body_root) { grid_layout text 'Share Result' label { layout_data :center, :center, true, false text 'Result is copied to clipboard!' } styled_text { layout_data :fill, :fill, true, true editable false caret nil alignment :center font name: 'Segoe UI Emoji' if OS.windows? text result } } @share_text_dialog.open end def alphabet_font the_font = {style: :bold, height: 28} the_font.merge!(name: 'Helvetica', height: 21) if OS.linux? the_font.merge!(name: 'Arial', height: 25) if OS.windows? the_font end def letter_font the_font = {style: :bold, height: 40} the_font.merge!(name: 'Helvetica', height: 30) if OS.linux? the_font.merge!(name: 'Arial', height: 32) if OS.windows? the_font end def dispose_alphabet_container_children @alphabet_rectangles.clear @alphabet_borders.clear @alphabet_letters.clear @alphabet_container.children.each(&:dispose) end def rebuild_alphabet_container dispose_alphabet_container_children @alphabet_container.content { alphabets } @alphabet_container.layout(true, true) @alphabet_container.pack(true) update_alphabet_background_colors end def alphabet_layout=(value) @alphabet_layout = value save_config end def new_config { alphabet_layout: @alphabet_layout } end def save_config File.write(CONFIG_FILE, YAML.dump(new_config)) rescue => e puts e.full_message end def load_config File.exist?(CONFIG_FILE) ? YAML.load(File.read(CONFIG_FILE)) : {} rescue => e puts e.full_message {} end def emoji_result result = '' @five_letter_word.guess_results.each do |row| row.each do |result_color| case result_color when :green result << "🟩" when :yellow result << "🟨" when :gray result << "⬜" end end result << "\n" end result end end end end 
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/AndyObtiva/glimmer_wordle

RubyGem: https://rubygems.org/gems/glimmer_wordle

Blog Post Announcement: https://andymaleh.blogspot.com/2023/10/glimmer-wordle-115.html

Top comments (0)