diff --git a/lib/tailor/Collection.rb b/lib/tailor/Collection.rb index 03ca49c..e52a17f 100644 --- a/lib/tailor/Collection.rb +++ b/lib/tailor/Collection.rb @@ -1,16 +1,20 @@ require 'singleton' module Tailor - module Collection + class Collection include Singleton attr_accessor :library def initialize + clear + end + + def clear @metadata = { - "author": "", - "name": "", - "license": "" + "author" => "", + "name" => "", + "license" => "" } self.library = Tailor::Library.new @pages = {} @@ -18,23 +22,23 @@ module Tailor def to_json js = { - "metadata" => metadata, + "metadata" => @metadata, "library" => {}, "pages" => {} } @library.each do |tileset| - js['library'][tileset['name']] = tileset.get_filename + js['library'][tileset.tileset_name] = tileset.get_filename end @pages.each do |page| js['pages'][page.page_name] = page.to_json end + js end def save_json(filename) dname = File.dirname(filename) Dir.mkdir(dname) unless Dir.exist?(dname) - File.open(filename, "w") do |file| file.write(JSON.pretty_generate(to_json)) end @@ -51,12 +55,11 @@ module Tailor end def load_json(filename) - dname = File.dirname(filename) - Dir.mkdir(dname) unless Dir.exist?(dname) + clear File.open(filename, "r") do |file| js = JSON.parse(file.read) + from_json(js) end - from_json(js) end end diff --git a/lib/tailor/GUI.rb b/lib/tailor/GUI.rb index 85ed590..37fa5ca 100644 --- a/lib/tailor/GUI.rb +++ b/lib/tailor/GUI.rb @@ -1,5 +1,6 @@ require 'wx' require 'tailor/GUI/MainWindow' +require 'tailor/Library' module Tailor module GUI diff --git a/lib/tailor/GUI/GridDisplay.rb b/lib/tailor/GUI/GridDisplay.rb index 137efd4..0c1c331 100644 --- a/lib/tailor/GUI/GridDisplay.rb +++ b/lib/tailor/GUI/GridDisplay.rb @@ -19,7 +19,7 @@ module Tailor end - class GridDisplay < Tailor::GUI::BitmapDisplay + class GridDisplay < Wx::ScrolledWindow attr_accessor :tileset @@ -36,9 +36,16 @@ module Tailor evt_left_up() { |event| on_gridClicked(event) } end + def set_tileset(tileset) + self.tileset = tileset + @bitmap = tileset.image.convert_to_bitmap + refresh_grid + end + def set_image(image) self.tileset.image = image @bitmap = image.convert_to_bitmap + set_scrollbars(1, 1, @bitmap.width, @bitmap.height) refresh_grid end diff --git a/lib/tailor/GUI/LibraryManager.rb b/lib/tailor/GUI/LibraryManager.rb index 9458266..4db5333 100644 --- a/lib/tailor/GUI/LibraryManager.rb +++ b/lib/tailor/GUI/LibraryManager.rb @@ -1,12 +1,13 @@ require 'wx' -require 'Tailor/Library' +require 'Tailor/Collection' module Tailor module GUI - class LibraryManager + class LibraryManager < Wx::Frame def initialize(*args) super(*args) - @library = Tailor::Library.instance + @library = Tailor::Collection.instance.library + @tileset = nil @panel = Wx::Panel.new(self) @sizer = Wx::BoxSizer.new(Wx::VERTICAL) @@ -19,12 +20,86 @@ module Tailor @library.tileset_names, Wx::LB_SORT | Wx::LB_SINGLE) vertSizer.add(@tilesetList, 0, flags = Wx::EXPAND|Wx::ALL) + @addBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Add Tileset") + evt_button(@addBtn.get_id()) { |event| on_AddClicked(event) } + @removeBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Remove Tileset") + evt_button(@removeBtn.get_id()) { |event| on_RemoveClicked(event) } + vertSizer.add(@addBtn, 1, flags = Wx::GROW) + vertSizer.add(@removeBtn, 1, flags = Wx::GROW) horizSizer.add(vertSizer, 0, flags = Wx::EXPAND|Wx::ALL) + @previewPane = Tailor::GUI::GridDisplay.new(@panel, + Wx::ID_ANY, + Wx::DEFAULT_POSITION, + Wx::DEFAULT_SIZE, + Wx::VSCROLL | Wx::HSCROLL | Wx::ALWAYS_SHOW_SB) + @previewPane.set_min_size(Wx::Size.new(320, 240)) + horizSizer.add(@previewPane, 1, flags=Wx::EXPAND|Wx::ALL) @sizer.add(horizSizer, 1, flag=Wx::EXPAND|Wx::ALL) @panel.set_sizer(@sizer) @sizer.set_size_hints(self) + + evt_listbox(@tilesetList) { |event| on_ListClicked(event) } + evt_listbox_dclick(@tilesetList) { |event| on_ListDoubleClicked(event) } + if @tilesetList.get_count > 0 + @tilesetList.set_selection(0) + on_ListClicked(nil) + end show() end + + def on_AddClicked(event) + wildcards = "*.json" + fd = Wx::FileDialog.new(self, "Select tileset to load", + :wildcard => wildcards, + :style => Wx::FD_FILE_MUST_EXIST) + if fd.show_modal == Wx::ID_OK + @tileset = @library.load_tileset(fd.get_path) + idx = @tilesetList.append(@tileset.tileset_name) + @tilesetList.set_selection(idx) + refresh_image + end + refresh + end + + def on_RemoveClicked(event) + idx = @tilesetList.get_selection + name = @tilesetList.get_string_selection + @library.delete(name) + @tilesetList.deselect(idx) + @tilesetList.delete(idx) + if @tilesetList.get_count > 0 + @tilesetList.set_selection(0) + on_ListClicked(nil) + else + @previewPane.hide + end + end + + def on_ListDoubleClicked(event) + @tileset = @library.by_name(@tilesetList.get_string_selection) + editor = Tailor::GUI::TilesetEditor.new(self, Wx::ID_ANY) + editor.set_tileset(@tileset) + editor.disable_load + editor.disable_import + editor.show + if (editor.has_saved and (not editor.has_loaded)) + on_RemoveClicked + idx = @tilesetList.append(@tileset.tileset_name) + @tilesetList.set_selection(idx) + on_ListClicked + end + end + + def on_ListClicked(event) + @tileset = @library.by_name(@tilesetList.get_string_selection) + refresh_image + end + + def refresh_image + @previewPane.set_tileset(@tileset) + @previewPane.show + end + end end end diff --git a/lib/tailor/GUI/MainWindow.rb b/lib/tailor/GUI/MainWindow.rb index 693c469..372acc8 100644 --- a/lib/tailor/GUI/MainWindow.rb +++ b/lib/tailor/GUI/MainWindow.rb @@ -1,48 +1,62 @@ require 'wx' require 'Tailor/version' +require 'Tailor/Collection' require 'Tailor/GUI/TilesetProperties' require 'tailor/GUI/TilesetEditor' +require 'tailor/GUI/LibraryManager' module Tailor module GUI class MainWindow < Wx::Frame def initialize() super(nil, -1, 'Tailor') + @collection = Tailor::Collection.instance + @mainPanel = nil init_menubar() show() end def init_menubar @menuBar = Wx::MenuBar.new - menuFile = Wx::Menu.new - menuFileNew = menuFile.append(Wx::ID_ANY, "&New\tAlt-N", "New Compilation") - menuFileOpen = menuFile.append(Wx::ID_ANY, "&Open\tAlt-O", "Open Compilation") - menuFileSave = menuFile.append(Wx::ID_ANY, "&Save\tAlt-S", "Save Compilation") - menuFileExport = menuFile.append(Wx::ID_ANY, "&Export\tAlt-E", "Export Compilation") - menuFile.append_separator - menuFileClose = menuFile.append(Wx::ID_ANY, "&Close\tAlt-W", "Close Compilation") - menuFile.append_separator - menuFileExit = menuFile.append(Wx::ID_EXIT, "E&xit\tAlt-X", "Exit Tailor") - @menuBar.append(menuFile, "&File") - menuEdit = Wx::Menu.new - menuEditTileset = menuEdit.append(Wx::ID_ANY, "&Tileset\tAlt-T", "Edit Tileset") - @menuBar.append(menuEdit, "&Edit") + @menuFile = Wx::Menu.new + menuFileNew = @menuFile.append(Wx::ID_ANY, "&New\tAlt-N", "New Compilation") + menuFileOpen = @menuFile.append(Wx::ID_ANY, "&Open\tAlt-O", "Open Compilation") + @menuFileSave = @menuFile.append(Wx::ID_ANY, "&Save\tAlt-S", "Save Compilation") + @menuFileSaveAs = @menuFile.append(Wx::ID_ANY, "Save As", "Save Compilation As...") + @menuFileExport = @menuFile.append(Wx::ID_ANY, "&Export\tAlt-E", "Export Compilation") + @menuFile.append_separator + @menuFileClose = @menuFile.append(Wx::ID_ANY, "&Close\tAlt-W", "Close Compilation") + @menuFile.append_separator + menuFileExit = @menuFile.append(Wx::ID_EXIT, "E&xit\tAlt-X", "Exit Tailor") + @menuBar.append(@menuFile, "&File") + @menuLibrary = Wx::Menu.new + @menuLibraryManager = @menuLibrary.append(Wx::ID_ANY, "&Manage\tAlt-M", "Library Manager") + menuLibraryTileset = @menuLibrary.append(Wx::ID_ANY, "&Tileset Editor\tAlt-T", "Tileset Editor") + @menuBar.append(@menuLibrary, "&Library") menuHelp = Wx::Menu.new menuHelpAbout = menuHelp.append(Wx::ID_ABOUT, "&About...\tF1", "About Tailor") @menuBar.append(menuHelp, "&Help") self.menu_bar = @menuBar evt_menu(menuFileNew, :on_file_new) - evt_menu(menuEditTileset, :on_edit_tileset) + evt_menu(menuLibraryTileset, :on_library_tileset) + evt_menu(@menuLibraryManager, :on_library_manager) evt_menu(menuFileOpen, :on_file_open) - evt_menu(menuFileSave, :on_file_save) - evt_menu(menuFileExport, :on_file_export) - evt_menu(menuFileClose, :on_file_close) + evt_menu(@menuFileSave, :on_file_save) + evt_menu(@menuFileSaveAs, :on_file_saveas) + evt_menu(@menuFileExport, :on_file_export) + evt_menu(@menuFileClose, :on_file_close) evt_menu(Wx::ID_EXIT, :on_file_exit) evt_menu(Wx::ID_ABOUT, :on_help_about) + + @menuFile.enable(@menuFileSave.get_id, false) + @menuFile.enable(@menuFileSaveAs.get_id, false) + @menuFile.enable(@menuFileExport.get_id, false) + @menuFile.enable(@menuFileClose.get_id, false) + @menuLibrary.enable(@menuLibraryManager.get_id, false) end - def on_file_new + def refresh_project @mainPanel = Wx::Panel.new(self) @mainPanelSizer = Wx::BoxSizer.new(Wx::HORIZONTAL) @mainPanel.set_sizer(@mainPanelSizer) @@ -53,27 +67,84 @@ module Tailor @mainPanelSizer.add(button, 0, Wx::EXPAND|Wx::ALL, 2) @mainPanelSizer.set_size_hints(@mainPanel) @mainPanelSizer.set_size_hints(self) + + @menuFile.enable(@menuFileSave.get_id, true) + @menuFile.enable(@menuFileSaveAs.get_id, true) + @menuFile.enable(@menuFileExport.get_id, true) + @menuFile.enable(@menuFileClose.get_id, true) + @menuLibrary.enable(@menuLibraryManager.get_id, true) + end + + + def on_file_new + wildcards = "*.json" + fd = Wx::FileDialog.new(self, "Select location for new collection", + :wildcard => wildcards, + :style => Wx::FD_OVERWRITE_PROMPT | Wx::FD_SAVE) + if fd.show_modal == Wx::ID_OK + @filename = fd.get_path + on_file_close + @collection.clear + refresh_project + end end def on_clickme(event) puts "I don't do anything" end - def on_edit_tileset - Tailor::GUI::TilesetEditor.new(self, Wx::ID_ANY, 'Tailor') + def on_library_tileset + Tailor::GUI::TilesetEditor.new(self, Wx::ID_ANY, 'Tailor :: Tileset').show + end + + def on_library_manager + Tailor::GUI::LibraryManager.new(self, + Wx::ID_ANY, + 'Tailor :: Library :: Manager').show end def on_file_open + wildcards = "*.json" + fd = Wx::FileDialog.new(self, "Select collection to load", + :wildcard => wildcards, + :style => Wx::FD_FILE_MUST_EXIST | Wx::FD_OPEN) + if fd.show_modal == Wx::ID_OK + on_file_close + @filename = fd.get_path + @collection.load_json(@filename) + refresh_project + end + end + + def on_file_saveas + wildcards = "*.json" + fd = Wx::FileDialog.new(self, "Select save location", + :wildcard => wildcards) + if fd.show_modal == Wx::ID_OK + @filename = fd.get_path + on_file_save + end end def on_file_save + @collection.save_json(@filename) + @has_saved = true end def on_file_export end def on_file_close - @mainPanel.destroy + @collection.clear + @menuFile.enable(@menuFileSave.get_id, false) + @menuFile.enable(@menuFileSaveAs.get_id, false) + @menuFile.enable(@menuFileExport.get_id, false) + @menuFile.enable(@menuFileClose.get_id, false) + @menuLibrary.enable(@menuLibraryManager.get_id, false) + if not @mainPanel.nil? + @mainPanel.destroy + @mainPanel = nil + end end def on_help_about @@ -81,6 +152,9 @@ module Tailor :version => Tailor::VERSION, :description => ( "Tailor : A program for stitching tilesets\n" + + "(C) 2014 Andrew Kesterson \n" + + "" + + "Released under the MIT License\n" + "Please file bugs at https://github.com/akesterson/tailor" ) ) diff --git a/lib/tailor/GUI/TilesetEditor.rb b/lib/tailor/GUI/TilesetEditor.rb index 31060e6..67abcb7 100644 --- a/lib/tailor/GUI/TilesetEditor.rb +++ b/lib/tailor/GUI/TilesetEditor.rb @@ -7,8 +7,14 @@ require 'Tailor/Tileset' module Tailor module GUI class TilesetEditor < Wx::Frame + + attr_accessor :has_saved + attr_accessor :has_loaded + def initialize(*args) super(*args) + self.has_saved = false + self.has_loaded = false @tilesetFilename = "" @tilesetImage = nil @tilesetNames = [] @@ -49,7 +55,11 @@ module Tailor tmpversizer.add(@cancelBtn, 0, flag=Wx::EXPAND) rowsizer.add(tmpversizer, 0, flag = Wx::EXPAND|Wx::ALL) - @tilesetSlicer = Tailor::GUI::GridDisplay.new(@panel, Wx::ID_ANY) + @tilesetSlicer = Tailor::GUI::GridDisplay.new(@panel, + Wx::ID_ANY, + Wx::DEFAULT_POSITION, + Wx::DEFAULT_SIZE, + Wx::VSCROLL | Wx::HSCROLL | Wx::ALWAYS_SHOW_SB) @tilesetSlicer.set_min_size(Wx::Size.new(320, 240)) rowsizer.add(@tilesetSlicer, 1, flag = Wx::EXPAND|Wx::ALL) evt_griddisplay_selected(@tilesetSlicer) { |event| on_gridSelected(event) } @@ -92,7 +102,14 @@ module Tailor @panel.set_sizer(@sizer) @sizer.set_size_hints(self) - show() + end + + def disable_load + @loadBtn.disable + end + + def disable_import + @importBtn.disable end def on_CancelClicked(event) @@ -115,6 +132,8 @@ module Tailor @tilesetNames << "Tile #{i}" end @tileNameCtrl.disable + self.has_saved = false + self.has_loaded = true end end @@ -153,6 +172,29 @@ module Tailor end + def set_tileset(tileset) + @tileset = tileset + tileset_changed + self.has_loaded = false + self.has_saved = false + end + + def tileset_changed + # FIXME : This is redundant. + @tileset.tiles.each do |tile| + @tilesetNames << tile['name'] + end + @tilesetImage = @tileset.image + @tilesetNameCtrl.set_value(@tileset.tileset_name) + @tilesetNotesCtrl.set_value(@tileset.notes) + @tilesetLicenseCtrl.set_value(@tileset.license) + @tileNameCtrl.disable + @tilesetSlicer.tileset = @tileset + @tilesetProperties.set_tileset(@tileset) + refresh_image + set_label("Tailor :: Tileset :: #{@tileset.tileset_name}") + end + def on_LoadClicked(event) @tileset = Tailor::Tileset.new wildcards = "*.json" @@ -160,17 +202,11 @@ module Tailor :wildcard => wildcards, :style => Wx::FD_FILE_MUST_EXIST) if fd.show_modal == Wx::ID_OK - @tileset.from_json(JSON.parse(File.read(fd.get_path))) - # FIXME : This is redundant. - @tileset.tiles.each do |tile| - @tilesetNames << tile['name'] - end - @tilesetImage = @tileset.image - @tileNameCtrl.disable - @tilesetSlicer.tileset = @tileset - @tilesetProperties.set_tileset(@tileset) - refresh_image + @tileset.from_file(fd.get_path) + tileset_changed end + self.has_saved = false + self.has_loaded = true end def on_SaveClicked(event) @@ -197,11 +233,13 @@ module Tailor File.open(filename, "w") do |file| @tileset.write(file, callback) end + self.has_saved = true end end def on_tilepropsChanged(event) return if @tilesetSlicer.get_size < 1 + self.has_saved = false ret = Wx::MessageDialog.new(self, "Changing tile properties will reset all tile names. Continue?", "WARNING").show_modal @@ -217,28 +255,28 @@ module Tailor def on_tilesetNameChanged(event) @tileset.tileset_name = @tilesetNameCtrl.get_value + self.has_saved = false end def on_tilesetLicenseChanged(event) @tileset.license = @tilesetLicenseCtrl.get_value + self.has_saved = false end def on_tilesetNotesChanged(event) @tileset.notes = @tilesetNotesCtrl.get_value + self.has_saved = false end def on_tileNameChanged(event) @tilesetNames[@tilesetSlicer.get_selected_index] = @tileNameCtrl.get_value + self.has_saved = false end def refresh_image begin if @tilesetImage.is_ok @tilesetSlicer.set_image @tilesetImage - # The + 20 here is a hack to make scroll bars go away where we don't want them - x = ( @tilesetImage.get_width > 600 ? 600 : @tilesetImage.get_width + 20) - y = ( @tilesetImage.get_height > 400 ? 400 : @tilesetImage.get_height + 20) - @tilesetSlicer.set_min_size(Wx::Size.new(x, y)) @sizer.set_size_hints(self) end rescue Exception => e diff --git a/lib/tailor/Library.rb b/lib/tailor/Library.rb index 3ccf1db..d22d515 100644 --- a/lib/tailor/Library.rb +++ b/lib/tailor/Library.rb @@ -1,33 +1,54 @@ require 'Tailor/Tileset' -require 'singleton' module Tailor class Library - include Singleton - attr_accessor :tilesets - def initialize - self.tilesets = [] + @tilesets = [] end def tileset_names x = [] idx = 0 - self.tilesets.each do |tileset| - x << [idx, tileset.tileset_name] + @tilesets.each do |tileset| + x << tileset.tileset_name idx += 1 end + x + end + + def each + @tilesets.each do |tileset| + yield tileset + end end - def unload_tileset(tileset) - self.tilesets.delete(tileset) + def delete(tileset) + if tileset.instance_of?(String) + @tilesets.delete(by_name(tileset)) + end + @tilesets.delete(tileset) end - def add_tileset(path) + def add_tileset(tileset) + @tilesets << tileset if tileset.instance_of?(Tailor::Tileset) + end + + def load_tileset(path) ts = Tailor::Tileset.new - ts.from_json(JSON.parse(File.read(path))) - self.tilesets << ts + ts.from_file(path) + @tilesets << ts + return ts + end + + def by_name(name) + @tilesets.each do |ts| + return ts if ts.tileset_name == name + end + end + + def by_index(idx) + return @tilesets[idx] end end diff --git a/lib/tailor/Tileset.rb b/lib/tailor/Tileset.rb index ccdf7b8..c13adef 100644 --- a/lib/tailor/Tileset.rb +++ b/lib/tailor/Tileset.rb @@ -18,6 +18,7 @@ module Tailor attr_accessor :tiles def initialize + @filename = '' self.image = nil self.license = '' self.notes = '' @@ -55,6 +56,11 @@ module Tailor end end + def get_filename + puts @filename + return @filename + end + def write(io_obj, callback = nil) obj = self.to_json(callback) if not callback.nil? @@ -63,6 +69,20 @@ module Tailor io_obj.write(JSON.pretty_generate(obj)) end + def to_file(filename) + @filename = filename + File.open(filename, "w") do |file| + write(file) + end + end + + def from_file(filename) + @filename = filename + File.open(filename, "r") do |file| + from_json(JSON.parse(file.read)) + end + end + def from_json(js) self.tileset_name = js['name'] self.license = js['license']