Compare commits
10 Commits
ea0cfdcd3d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c824d5e30e | |||
| 48edd2fa92 | |||
| e2b5ba5808 | |||
| ba5b671730 | |||
| 63875903eb | |||
| afd809d1e9 | |||
| a4a4092008 | |||
| 905da9d681 | |||
| 1b9c873dca | |||
| 3c192092d6 |
18
README.md
18
README.md
@@ -3,3 +3,21 @@ tailor
|
|||||||
==========
|
==========
|
||||||
|
|
||||||
tailor is a tool for stitching small individual images (or regions thereof) together into tilemaps
|
tailor is a tool for stitching small individual images (or regions thereof) together into tilemaps
|
||||||
|
|
||||||
|
Status
|
||||||
|
======
|
||||||
|
|
||||||
|
Tailor is currently in pre-alpha. It currently performs some of its functions (tileset import, slicing, image exporting, library and project management) but the actual combining of individual tiles into new tilesets does not yet work.
|
||||||
|
|
||||||
|
I expect to release 1.0 in a few weeks.
|
||||||
|
|
||||||
|
Installing & Running
|
||||||
|
====================
|
||||||
|
|
||||||
|
This depends on [rvpacker](https://github.com/akesterson/rvpacker) and [wxruby](http://wxruby.rubyforge.org/wiki/wiki.pl), and is built with rake/bundler.
|
||||||
|
|
||||||
|
cd tailor
|
||||||
|
bundle install
|
||||||
|
rake build
|
||||||
|
gem install ./pkg/tailor*gem
|
||||||
|
tailor
|
||||||
|
|||||||
66
lib/tailor/Collection.rb
Normal file
66
lib/tailor/Collection.rb
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
require 'singleton'
|
||||||
|
|
||||||
|
module Tailor
|
||||||
|
class Collection
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
attr_accessor :library
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
clear
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
@metadata = {
|
||||||
|
"author" => "",
|
||||||
|
"name" => "",
|
||||||
|
"license" => ""
|
||||||
|
}
|
||||||
|
self.library = Tailor::Library.new
|
||||||
|
@pages = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_json
|
||||||
|
js = {
|
||||||
|
"metadata" => @metadata,
|
||||||
|
"library" => {},
|
||||||
|
"pages" => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@library.each do |tileset|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_json(js)
|
||||||
|
js['library'].each_pair do |name, filename|
|
||||||
|
@library.load_tileset(filename)
|
||||||
|
end
|
||||||
|
js['pages'].each_pair do |name, data|
|
||||||
|
@pages[name] = Tailor::Page.new
|
||||||
|
@pages[name].from_json(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_json(filename)
|
||||||
|
clear
|
||||||
|
File.open(filename, "r") do |file|
|
||||||
|
js = JSON.parse(file.read)
|
||||||
|
from_json(js)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
require 'wx'
|
require 'wx'
|
||||||
require 'tailor/GUI/MainWindow'
|
require 'tailor/GUI/MainWindow'
|
||||||
require 'tailor/GUI/TilesetEditor'
|
require 'tailor/Library'
|
||||||
|
|
||||||
module Tailor
|
module Tailor
|
||||||
module GUI
|
module GUI
|
||||||
class Application < Wx::App
|
class Application < Wx::App
|
||||||
def on_init
|
def on_init
|
||||||
#Tailor::GUI::MainWindow.new
|
Tailor::GUI::MainWindow.new
|
||||||
Tailor::GUI::TilesetEditor.new(nil, -1, 'Tailor')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,23 +5,25 @@ module Tailor
|
|||||||
|
|
||||||
# Ganked from http://rubyforscientificresearch.blogspot.com/2009/05/displaying-images-in-wxruby.html
|
# Ganked from http://rubyforscientificresearch.blogspot.com/2009/05/displaying-images-in-wxruby.html
|
||||||
|
|
||||||
class ImageDisplay < Wx::ScrolledWindow
|
class BitmapDisplay < Wx::ScrolledWindow
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super(*args)
|
super(*args)
|
||||||
@image = nil
|
@bitmap = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_image image
|
def set_bitmap bmp
|
||||||
@image = image
|
puts "Setting bitmap to #{bmp}"
|
||||||
set_scrollbars(1, 1, @image.width, @image.height)
|
@bitmap = bmp
|
||||||
|
set_scrollbars(1, 1, @bitmap.width, @bitmap.height)
|
||||||
refresh
|
refresh
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_draw dc
|
def on_draw dc
|
||||||
|
puts "Drawing #{@bitmap}"
|
||||||
dc.set_background Wx::WHITE_BRUSH
|
dc.set_background Wx::WHITE_BRUSH
|
||||||
dc.clear
|
dc.clear
|
||||||
return if @image == nil
|
return if @bitmap == nil
|
||||||
dc.draw_bitmap(@image, 0, 0, true)
|
dc.draw_bitmap(@bitmap, 0, 0, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
require 'wx'
|
require 'wx'
|
||||||
require 'Tailor/GUI/ImageDisplay'
|
require 'Tailor/GUI/BitmapDisplay'
|
||||||
|
|
||||||
module Tailor
|
module Tailor
|
||||||
module GUI
|
module GUI
|
||||||
@@ -19,80 +19,92 @@ module Tailor
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class GridDisplay < Tailor::GUI::ImageDisplay
|
class GridDisplay < Wx::ScrolledWindow
|
||||||
|
|
||||||
|
attr_accessor :tileset
|
||||||
|
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super(*args)
|
super(*args)
|
||||||
@imageGrid = nil
|
self.tileset = nil
|
||||||
|
@bitmapGrid = nil
|
||||||
@darkenCell = nil
|
@darkenCell = nil
|
||||||
@pristineImage = nil
|
|
||||||
@darken_x = 0
|
@darken_x = 0
|
||||||
@darken_y = 0
|
@darken_y = 0
|
||||||
@rectlist = []
|
@rectlist = []
|
||||||
@selected = nil
|
@selected = nil
|
||||||
|
|
||||||
|
evt_left_up() { |event| on_gridClicked(event) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_tileset(tileset)
|
||||||
|
self.tileset = tileset
|
||||||
|
@bitmap = tileset.image.convert_to_bitmap
|
||||||
|
refresh_grid
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_image(image)
|
def set_image(image)
|
||||||
@pristineImage = image
|
self.tileset.image = image
|
||||||
set_grid(0, 0, 0, 0, 32, 32)
|
@bitmap = image.convert_to_bitmap
|
||||||
|
set_scrollbars(1, 1, @bitmap.width, @bitmap.height)
|
||||||
|
refresh_grid
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_grid(padX, padY, pitchX, pitchY, gridX, gridY)
|
def refresh_grid
|
||||||
@rectlist = []
|
@rectlist = []
|
||||||
@selected = nil
|
@selected = nil
|
||||||
|
|
||||||
@imageGrid = Wx::Bitmap.new(@pristineImage.get_width(),
|
@bitmapGrid = Wx::Bitmap.new(@bitmap.get_width(),
|
||||||
@pristineImage.get_height(),
|
@bitmap.get_height(),
|
||||||
@pristineImage.get_depth()
|
@bitmap.get_depth()
|
||||||
)
|
)
|
||||||
tmpImage = Wx::Image.new(gridX, gridY)
|
tmpImage = Wx::Image.new(self.tileset.tile_x, self.tileset.tile_y)
|
||||||
tmpImage.init_alpha
|
tmpImage.init_alpha
|
||||||
(0..gridX).each do |x|
|
(0..self.tileset.tile_x).each do |x|
|
||||||
(0..gridY).each do |y|
|
(0..self.tileset.tile_y).each do |y|
|
||||||
tmpImage.set_rgb(x, y, 0, 0, 0)
|
tmpImage.set_rgb(x, y, 0, 0, 0)
|
||||||
tmpImage.set_alpha(x, y, 150)
|
tmpImage.set_alpha(x, y, 150)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@darkenCell = tmpImage.to_bitmap
|
@darkenCell = tmpImage.to_bitmap
|
||||||
@darken_x = -(gridX)
|
@darken_x = -(self.tileset.tile_x)
|
||||||
@darken_y = -(gridY)
|
@darken_y = -(self.tileset.tile_y)
|
||||||
|
|
||||||
evt_left_up() { |event| on_gridClicked(event) }
|
stepx = self.tileset.tile_x + self.tileset.space_x
|
||||||
@imageGrid.draw() { |dc|
|
stepx = 1 unless stepx != 0
|
||||||
|
stepy = self.tileset.tile_y + self.tileset.space_y
|
||||||
|
stepy = 1 unless stepy != 0
|
||||||
|
rows = ( (@bitmap.height - self.tileset.pad_y) / stepy )
|
||||||
|
columns = ( (@bitmap.width - self.tileset.pad_x) / stepx )
|
||||||
|
curX = self.tileset.pad_x
|
||||||
|
curY = self.tileset.pad_y
|
||||||
|
|
||||||
|
@bitmapGrid.draw() { |dc|
|
||||||
dc.clear
|
dc.clear
|
||||||
dc.draw_bitmap(@pristineImage, 0, 0, true)
|
dc.draw_bitmap(@bitmap, 0, 0, true)
|
||||||
stepx = gridX + pitchX
|
|
||||||
stepx = 1 unless stepx != 0
|
|
||||||
stepy = gridY + pitchY
|
|
||||||
stepy = 1 unless stepy != 0
|
|
||||||
rows = ( (@pristineImage.height - padY) / stepy )
|
|
||||||
columns = ( (@pristineImage.width - padX) / stepx )
|
|
||||||
curX = padX
|
|
||||||
curY = padY
|
|
||||||
|
|
||||||
dc.set_brush(Wx::TRANSPARENT_BRUSH)
|
dc.set_brush(Wx::TRANSPARENT_BRUSH)
|
||||||
dc.set_pen(Wx::RED_PEN)
|
dc.set_pen(Wx::RED_PEN)
|
||||||
|
|
||||||
for row in 0..rows
|
for row in 0..rows
|
||||||
for column in 0..columns
|
for column in 0..columns
|
||||||
next if ((curX + stepx + padX) > @imageGrid.get_width)
|
next if ((curX + stepx + self.tileset.pad_x) > @bitmapGrid.get_width)
|
||||||
next if ((curY + stepy + padY) > @imageGrid.get_height)
|
next if ((curY + stepy + self.tileset.pad_y) > @bitmapGrid.get_height)
|
||||||
dc.draw_rectangle(curX, curY, gridX, gridY)
|
dc.draw_rectangle(curX, curY, self.tileset.tile_x, self.tileset.tile_y)
|
||||||
@rectlist << Wx::Rect.new(curX, curY, gridX, gridY)
|
@rectlist << Wx::Rect.new(curX, curY, self.tileset.tile_x, self.tileset.tile_y)
|
||||||
curX += stepx
|
curX += stepx
|
||||||
end
|
end
|
||||||
curX = padX
|
curX = self.tileset.pad_x
|
||||||
curY += stepy
|
curY += stepy
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
_super.set_image(@imageGrid)
|
refresh
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_draw(dc)
|
def on_draw(dc)
|
||||||
dc.set_background Wx::WHITE_BRUSH
|
dc.set_background Wx::WHITE_BRUSH
|
||||||
dc.clear
|
dc.clear
|
||||||
return if @imageGrid == nil
|
return if @bitmapGrid == nil
|
||||||
dc.draw_bitmap(@imageGrid, 0, 0, true)
|
dc.draw_bitmap(@bitmapGrid, 0, 0, true)
|
||||||
return if @darkenCell == nil
|
return if @darkenCell == nil
|
||||||
dc.draw_bitmap(@darkenCell, @darken_x, @darken_y, true)
|
dc.draw_bitmap(@darkenCell, @darken_x, @darken_y, true)
|
||||||
end
|
end
|
||||||
@@ -113,17 +125,21 @@ module Tailor
|
|||||||
@rectlist.length
|
@rectlist.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_tiles
|
def get_bitmap
|
||||||
|
@bitmap
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_tile_bitmaps
|
||||||
ret = []
|
ret = []
|
||||||
@rectlist.each do |rect|
|
@rectlist.each do |rect|
|
||||||
img = @pristineImage.sub_bitmap(rect)
|
img = @bitmap.sub_bitmap(rect)
|
||||||
ret << img
|
ret << img
|
||||||
end
|
end
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_selected_tile
|
def get_selected_tile
|
||||||
@pristineImage.get_sub_bitmap(@selected)
|
@bitmap.get_sub_bitmap(@selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_selected_index
|
def get_selected_index
|
||||||
|
|||||||
119
lib/tailor/GUI/LibraryManager.rb
Normal file
119
lib/tailor/GUI/LibraryManager.rb
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
require 'wx'
|
||||||
|
require 'Tailor/Collection'
|
||||||
|
|
||||||
|
module Tailor
|
||||||
|
module GUI
|
||||||
|
class LibraryChangedEvent < Wx::CommandEvent
|
||||||
|
EVT_LIBRARY_CHANGED = Wx::EvtHandler.register_class(self,
|
||||||
|
nil,
|
||||||
|
"evt_library_changed",
|
||||||
|
1)
|
||||||
|
def initialize(source)
|
||||||
|
super(EVT_LIBRARY_CHANGED)
|
||||||
|
self.id = source.get_id
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class LibraryManager < Wx::Frame
|
||||||
|
def initialize(*args)
|
||||||
|
super(*args)
|
||||||
|
@library = Tailor::Collection.instance.library
|
||||||
|
@tileset = nil
|
||||||
|
@panel = Wx::Panel.new(self)
|
||||||
|
@sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
||||||
|
|
||||||
|
horizSizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
||||||
|
vertSizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
||||||
|
@tilesetList = Wx::ListBox.new(@panel,
|
||||||
|
Wx::ID_ANY,
|
||||||
|
Wx::DEFAULT_POSITION,
|
||||||
|
Wx::DEFAULT_SIZE,
|
||||||
|
@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
|
||||||
|
event_handler.process_event(LibraryChangedEvent.new(self))
|
||||||
|
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
|
||||||
|
event_handler.process_event(LibraryChangedEvent.new(self))
|
||||||
|
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
|
||||||
@@ -1,75 +1,219 @@
|
|||||||
require 'wx'
|
require 'wx'
|
||||||
require 'Tailor/version'
|
require 'Tailor/version'
|
||||||
|
require 'Tailor/Collection'
|
||||||
require 'Tailor/GUI/TilesetProperties'
|
require 'Tailor/GUI/TilesetProperties'
|
||||||
|
require 'tailor/GUI/TilesetEditor'
|
||||||
|
require 'tailor/GUI/LibraryManager'
|
||||||
|
require 'tailor/GUI/ImageDisplay'
|
||||||
|
|
||||||
module Tailor
|
module Tailor
|
||||||
module GUI
|
module GUI
|
||||||
class MainWindow < Wx::Frame
|
class MainWindow < Wx::Frame
|
||||||
def initialize()
|
def initialize()
|
||||||
super(nil, -1, 'Tailor')
|
super(nil, -1, 'Tailor')
|
||||||
|
@collection = Tailor::Collection.instance
|
||||||
|
@mainPanel = nil
|
||||||
init_menubar()
|
init_menubar()
|
||||||
show()
|
show()
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_menubar
|
def init_menubar
|
||||||
@menuBar = Wx::MenuBar.new
|
@menuBar = Wx::MenuBar.new
|
||||||
menuFile = Wx::Menu.new
|
@menuFile = Wx::Menu.new
|
||||||
menuFileNew = menuFile.append(Wx::ID_ANY, "&New\tAlt-N", "New Compilation")
|
menuFileNew = @menuFile.append(Wx::ID_ANY, "&New\tAlt-N", "New Compilation")
|
||||||
menuFileOpen = menuFile.append(Wx::ID_ANY, "&Open\tAlt-O", "Open Compilation")
|
menuFileOpen = @menuFile.append(Wx::ID_ANY, "&Open\tAlt-O", "Open Compilation")
|
||||||
menuFileSave = menuFile.append(Wx::ID_ANY, "&Save\tAlt-S", "Save Compilation")
|
@menuFileSave = @menuFile.append(Wx::ID_ANY, "&Save\tAlt-S", "Save Compilation")
|
||||||
menuFileExport = menuFile.append(Wx::ID_ANY, "&Export\tAlt-E", "Export Compilation")
|
@menuFileSaveAs = @menuFile.append(Wx::ID_ANY, "Save As", "Save Compilation As...")
|
||||||
menuFile.append_separator
|
@menuFileExport = @menuFile.append(Wx::ID_ANY, "&Export\tAlt-E", "Export Compilation")
|
||||||
menuFileClose = menuFile.append(Wx::ID_ANY, "&Close\tAlt-W", "Close Compilation")
|
@menuFile.append_separator
|
||||||
menuFile.append_separator
|
@menuFileClose = @menuFile.append(Wx::ID_ANY, "&Close\tAlt-W", "Close Compilation")
|
||||||
menuFileExit = menuFile.append(Wx::ID_EXIT, "E&xit\tAlt-X", "Exit Tailor")
|
@menuFile.append_separator
|
||||||
@menuBar.append(menuFile, "&File")
|
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
|
menuHelp = Wx::Menu.new
|
||||||
menuHelpAbout = menuHelp.append(Wx::ID_ABOUT, "&About...\tF1", "About Tailor")
|
menuHelpAbout = menuHelp.append(Wx::ID_ABOUT, "&About...\tF1", "About Tailor")
|
||||||
@menuBar.append(menuHelp, "&Help")
|
@menuBar.append(menuHelp, "&Help")
|
||||||
self.menu_bar = @menuBar
|
self.menu_bar = @menuBar
|
||||||
|
|
||||||
evt_menu(menuFileNew, :on_file_new)
|
evt_menu(menuFileNew, :on_file_new)
|
||||||
|
evt_menu(menuLibraryTileset, :on_library_tileset)
|
||||||
|
evt_menu(@menuLibraryManager, :on_library_manager)
|
||||||
evt_menu(menuFileOpen, :on_file_open)
|
evt_menu(menuFileOpen, :on_file_open)
|
||||||
evt_menu(menuFileSave, :on_file_save)
|
evt_menu(@menuFileSave, :on_file_save)
|
||||||
evt_menu(menuFileExport, :on_file_export)
|
evt_menu(@menuFileSaveAs, :on_file_saveas)
|
||||||
evt_menu(menuFileClose, :on_file_close)
|
evt_menu(@menuFileExport, :on_file_export)
|
||||||
|
evt_menu(@menuFileClose, :on_file_close)
|
||||||
evt_menu(Wx::ID_EXIT, :on_file_exit)
|
evt_menu(Wx::ID_EXIT, :on_file_exit)
|
||||||
evt_menu(Wx::ID_ABOUT, :on_help_about)
|
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
|
end
|
||||||
|
|
||||||
def on_file_new
|
def refresh_palette
|
||||||
|
if @palettePanel
|
||||||
|
@mainPanelSizer.detach(@palettePanel)
|
||||||
|
@palettePanel.destroy_children
|
||||||
|
@palettePanel.destroy
|
||||||
|
refresh
|
||||||
|
end
|
||||||
|
@palettePanel = Wx::ScrolledWindow.new(@mainPanel, Wx::ID_ANY)
|
||||||
|
@paletteSizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
||||||
|
@paletteLibraryPanels = {}
|
||||||
|
|
||||||
|
progdialog = Wx::ProgressDialog.new("Rebuilding Palette...",
|
||||||
|
"Rebuilding Palette...",
|
||||||
|
@collection.library.tile_count - 1,
|
||||||
|
self,
|
||||||
|
style = Wx::PD_SMOOTH | Wx::PD_AUTO_HIDE)
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
@collection.library.each do |ts|
|
||||||
|
tsExpandCollapseBtn = Wx::Button.new(@palettePanel, Wx::ID_ANY, ts.tileset_name)
|
||||||
|
tsExpandCollapseBtn.set_min_size(
|
||||||
|
Wx::Size.new(256,
|
||||||
|
tsExpandCollapseBtn.get_size().height
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@paletteSizer.add(tsExpandCollapseBtn, 0, flags = Wx::GROW)
|
||||||
|
tpanel = Wx::Panel.new(@palettePanel, Wx::ID_ANY)
|
||||||
|
tgridSizer = Wx::GridSizer.new(cols=2)
|
||||||
|
evt_button(tsExpandCollapseBtn.get_id()) { |event| on_LibExpandCollapseClicked(ts) }
|
||||||
|
ts.tiles.each do |tile|
|
||||||
|
progdialog.update(idx)
|
||||||
|
idx += 1
|
||||||
|
tgridSizer.add(Wx::StaticBitmap.new(tpanel, Wx::ID_ANY, tile['image'].to_bitmap))
|
||||||
|
tgridSizer.add(Wx::StaticText.new(tpanel, Wx::ID_ANY, tile['name']))
|
||||||
|
end
|
||||||
|
tpanel.set_sizer(tgridSizer)
|
||||||
|
@paletteLibraryPanels[ts.tileset_name] = tpanel
|
||||||
|
@paletteSizer.add(tpanel, 0, Wx::EXPAND|Wx::ALL)
|
||||||
|
end
|
||||||
|
@palettePanel.enable_scrolling(false, true)
|
||||||
|
@palettePanel.set_scroll_rate(1, 1)
|
||||||
|
@palettePanel.set_min_size(Wx::Size.new(256, 256))
|
||||||
|
@palettePanel.set_sizer(@paletteSizer)
|
||||||
|
@paletteSizer.set_size_hints(@palettePanel)
|
||||||
|
@mainPanelSizer.add(@palettePanel, 1, Wx::EXPAND|Wx::ALL)
|
||||||
|
@mainPanelSizer.layout
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_project
|
||||||
@mainPanel = Wx::Panel.new(self)
|
@mainPanel = Wx::Panel.new(self)
|
||||||
@mainPanelSizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
@mainPanelSizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
||||||
@mainPanel.set_sizer(@mainPanelSizer)
|
@mainPanel.set_sizer(@mainPanelSizer)
|
||||||
@tilesetProperties = Tailor::GUI::TilesetProperties.new(@mainPanel, Wx::ID_ANY)
|
@tilesetProperties = Tailor::GUI::TilesetProperties.new(@mainPanel, Wx::ID_ANY)
|
||||||
@mainPanelSizer.add(@tilesetProperties)
|
@mainPanelSizer.add(@tilesetProperties)
|
||||||
button = Wx::Button.new(@mainPanel, Wx::ID_ANY, "Click me")
|
button = Wx::Button.new(@mainPanel, Wx::ID_ANY, "(Replace with drag and drop target)")
|
||||||
evt_button(button.get_id()) { |event| on_clickme(event) }
|
evt_button(button.get_id()) { |event| on_clickme(event) }
|
||||||
@mainPanelSizer.add(button, 0, Wx::EXPAND|Wx::ALL, 2)
|
@mainPanelSizer.add(button, 1, Wx::EXPAND|Wx::ALL)
|
||||||
|
|
||||||
|
refresh_palette
|
||||||
|
|
||||||
@mainPanelSizer.set_size_hints(@mainPanel)
|
@mainPanelSizer.set_size_hints(@mainPanel)
|
||||||
@mainPanelSizer.set_size_hints(self)
|
@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_LibExpandCollapseClicked(ts)
|
||||||
|
panel = @paletteLibraryPanels[ts.tileset_name]
|
||||||
|
if panel.is_shown
|
||||||
|
panel.show(show = false)
|
||||||
|
else
|
||||||
|
panel.show(show = true)
|
||||||
|
end
|
||||||
|
s = @paletteSizer.get_min_size()
|
||||||
|
@palettePanel.set_virtual_size(s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_clickme(event)
|
def on_clickme(event)
|
||||||
puts "Tile X: #{@tilesetProperties.TileX}"
|
puts "I don't do anything"
|
||||||
puts "Tile Y: #{@tilesetProperties.TileY}"
|
end
|
||||||
puts "Pad X: #{@tilesetProperties.PadX}"
|
|
||||||
puts "Pad Y: #{@tilesetProperties.PadY}"
|
def on_library_tileset
|
||||||
puts "Space X: #{@tilesetProperties.SpaceX}"
|
Tailor::GUI::TilesetEditor.new(self, Wx::ID_ANY, 'Tailor :: Tileset').show
|
||||||
puts "Space Y: #{@tilesetProperties.SpaceY}"
|
end
|
||||||
|
|
||||||
|
def on_library_manager
|
||||||
|
tlm = Tailor::GUI::LibraryManager.new(self,
|
||||||
|
Wx::ID_ANY,
|
||||||
|
'Tailor :: Library :: Manager')
|
||||||
|
tlm.show
|
||||||
|
evt_library_changed(tlm.get_id()) { |event| on_library_changed(event) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_library_changed(event)
|
||||||
|
refresh_palette
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_file_open
|
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
|
end
|
||||||
|
|
||||||
def on_file_save
|
def on_file_save
|
||||||
|
@collection.save_json(@filename)
|
||||||
|
@has_saved = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_file_export
|
def on_file_export
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_file_close
|
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
|
end
|
||||||
|
|
||||||
def on_help_about
|
def on_help_about
|
||||||
@@ -77,6 +221,9 @@ module Tailor
|
|||||||
:version => Tailor::VERSION,
|
:version => Tailor::VERSION,
|
||||||
:description => (
|
:description => (
|
||||||
"Tailor : A program for stitching tilesets\n" +
|
"Tailor : A program for stitching tilesets\n" +
|
||||||
|
"(C) 2014 Andrew Kesterson <andrew@aklabs.net>\n" +
|
||||||
|
"" +
|
||||||
|
"Released under the MIT License\n" +
|
||||||
"Please file bugs at https://github.com/akesterson/tailor"
|
"Please file bugs at https://github.com/akesterson/tailor"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
|
require 'wx'
|
||||||
require 'tailor'
|
require 'tailor'
|
||||||
require 'Tailor/GUI/TilesetProperties'
|
require 'Tailor/GUI/TilesetProperties'
|
||||||
require 'Tailor/GUI/TilesetDisplay'
|
require 'Tailor/GUI/TilesetDisplay'
|
||||||
require 'Tailor/GUI/GridDisplay'
|
require 'Tailor/GUI/GridDisplay'
|
||||||
|
require 'Tailor/Tileset'
|
||||||
module Tailor
|
module Tailor
|
||||||
module GUI
|
module GUI
|
||||||
class TilesetEditor < Wx::Frame
|
class TilesetEditor < Wx::Frame
|
||||||
|
|
||||||
|
attr_accessor :has_saved
|
||||||
|
attr_accessor :has_loaded
|
||||||
|
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super(*args)
|
super(*args)
|
||||||
|
self.has_saved = false
|
||||||
|
self.has_loaded = false
|
||||||
@tilesetFilename = ""
|
@tilesetFilename = ""
|
||||||
@tilesetImage = nil
|
@tilesetImage = nil
|
||||||
@tilesetNames = []
|
@tilesetNames = []
|
||||||
|
@tileset = nil
|
||||||
|
|
||||||
@panel = Wx::Panel.new(self)
|
@panel = Wx::Panel.new(self)
|
||||||
@sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
@sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
||||||
@@ -21,15 +29,18 @@ module Tailor
|
|||||||
@tilesetProperties = Tailor::GUI::TilesetProperties.new(@panel, Wx::ID_ANY)
|
@tilesetProperties = Tailor::GUI::TilesetProperties.new(@panel, Wx::ID_ANY)
|
||||||
evt_tileprops_changed(@tilesetProperties) { |event| on_tilepropsChanged(event) }
|
evt_tileprops_changed(@tilesetProperties) { |event| on_tilepropsChanged(event) }
|
||||||
tmpversizer.add(@tilesetProperties, 0, flag = Wx::EXPAND|Wx::ALL)
|
tmpversizer.add(@tilesetProperties, 0, flag = Wx::EXPAND|Wx::ALL)
|
||||||
@cancelBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Cancel")
|
@cancelBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Close")
|
||||||
evt_button(@cancelBtn.get_id()) { |event| on_CancelClicked(event) }
|
evt_button(@cancelBtn.get_id()) { |event| on_CancelClicked(event) }
|
||||||
@importBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Import")
|
@importBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Import Image")
|
||||||
evt_button(@importBtn.get_id()) { |event| on_ImportClicked(event) }
|
evt_button(@importBtn.get_id()) { |event| on_ImportClicked(event) }
|
||||||
|
|
||||||
@exportBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Export")
|
@exportBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Export Images")
|
||||||
evt_button(@exportBtn.get_id()) { |event| on_ExportClicked(event) }
|
evt_button(@exportBtn.get_id()) { |event| on_ExportClicked(event) }
|
||||||
|
|
||||||
@saveBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Save")
|
@loadBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Load JSON")
|
||||||
|
evt_button(@loadBtn.get_id()) { |event| on_LoadClicked(event) }
|
||||||
|
|
||||||
|
@saveBtn = Wx::Button.new(@panel, Wx::ID_ANY, "Save JSON")
|
||||||
evt_button(@saveBtn.get_id()) { |event| on_SaveClicked(event) }
|
evt_button(@saveBtn.get_id()) { |event| on_SaveClicked(event) }
|
||||||
|
|
||||||
tmpversizer.add_spacer(20)
|
tmpversizer.add_spacer(20)
|
||||||
@@ -37,12 +48,18 @@ module Tailor
|
|||||||
tmpversizer.add_spacer(20)
|
tmpversizer.add_spacer(20)
|
||||||
tmpversizer.add(@exportBtn, 0, flag=Wx::EXPAND)
|
tmpversizer.add(@exportBtn, 0, flag=Wx::EXPAND)
|
||||||
tmpversizer.add_spacer(20)
|
tmpversizer.add_spacer(20)
|
||||||
|
tmpversizer.add(@loadBtn, 0, flag=Wx::EXPAND)
|
||||||
|
tmpversizer.add_spacer(20)
|
||||||
tmpversizer.add(@saveBtn, 0, flag=Wx::EXPAND)
|
tmpversizer.add(@saveBtn, 0, flag=Wx::EXPAND)
|
||||||
tmpversizer.add_spacer(20)
|
tmpversizer.add_spacer(20)
|
||||||
tmpversizer.add(@cancelBtn, 0, flag=Wx::EXPAND)
|
tmpversizer.add(@cancelBtn, 0, flag=Wx::EXPAND)
|
||||||
rowsizer.add(tmpversizer, 0, flag = Wx::EXPAND|Wx::ALL)
|
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))
|
@tilesetSlicer.set_min_size(Wx::Size.new(320, 240))
|
||||||
rowsizer.add(@tilesetSlicer, 1, flag = Wx::EXPAND|Wx::ALL)
|
rowsizer.add(@tilesetSlicer, 1, flag = Wx::EXPAND|Wx::ALL)
|
||||||
evt_griddisplay_selected(@tilesetSlicer) { |event| on_gridSelected(event) }
|
evt_griddisplay_selected(@tilesetSlicer) { |event| on_gridSelected(event) }
|
||||||
@@ -52,6 +69,8 @@ module Tailor
|
|||||||
Wx::ID_ANY,
|
Wx::ID_ANY,
|
||||||
"Tileset Name")
|
"Tileset Name")
|
||||||
tmpversizer.add(@tilesetNameCtrl, 0, flag = Wx::EXPAND|Wx::ALIGN_TOP)
|
tmpversizer.add(@tilesetNameCtrl, 0, flag = Wx::EXPAND|Wx::ALIGN_TOP)
|
||||||
|
evt_text(@tilesetNameCtrl) { |event| on_tilesetNameChanged(event) }
|
||||||
|
|
||||||
@tileNameCtrl = Wx::TextCtrl.new(@panel,
|
@tileNameCtrl = Wx::TextCtrl.new(@panel,
|
||||||
Wx::ID_ANY,
|
Wx::ID_ANY,
|
||||||
"Tile Name")
|
"Tile Name")
|
||||||
@@ -64,6 +83,7 @@ module Tailor
|
|||||||
Wx::DEFAULT_POSITION,
|
Wx::DEFAULT_POSITION,
|
||||||
Wx::DEFAULT_SIZE,
|
Wx::DEFAULT_SIZE,
|
||||||
Wx::TE_MULTILINE|Wx::TE_WORDWRAP)
|
Wx::TE_MULTILINE|Wx::TE_WORDWRAP)
|
||||||
|
evt_text(@tilesetNotesCtrl) { |event| on_tilesetNotesChanged(event) }
|
||||||
@tilesetNotesCtrl.set_min_size(Wx::Size.new(200,120))
|
@tilesetNotesCtrl.set_min_size(Wx::Size.new(200,120))
|
||||||
tmpversizer.add(@tilesetNotesCtrl, 1, flag = Wx::EXPAND|Wx::ALL)
|
tmpversizer.add(@tilesetNotesCtrl, 1, flag = Wx::EXPAND|Wx::ALL)
|
||||||
@tilesetLicenseCtrl = Wx::TextCtrl.new(@panel,
|
@tilesetLicenseCtrl = Wx::TextCtrl.new(@panel,
|
||||||
@@ -72,6 +92,7 @@ module Tailor
|
|||||||
Wx::DEFAULT_POSITION,
|
Wx::DEFAULT_POSITION,
|
||||||
Wx::DEFAULT_SIZE,
|
Wx::DEFAULT_SIZE,
|
||||||
Wx::TE_MULTILINE|Wx::TE_WORDWRAP)
|
Wx::TE_MULTILINE|Wx::TE_WORDWRAP)
|
||||||
|
evt_text(@tilesetLicenseCtrl) { |event| on_tilesetLicenseChanged(event) }
|
||||||
@tilesetLicenseCtrl.set_min_size(Wx::Size.new(200,120))
|
@tilesetLicenseCtrl.set_min_size(Wx::Size.new(200,120))
|
||||||
tmpversizer.add(@tilesetLicenseCtrl, 1, flag = Wx::EXPAND|Wx::ALL)
|
tmpversizer.add(@tilesetLicenseCtrl, 1, flag = Wx::EXPAND|Wx::ALL)
|
||||||
|
|
||||||
@@ -81,7 +102,14 @@ module Tailor
|
|||||||
|
|
||||||
@panel.set_sizer(@sizer)
|
@panel.set_sizer(@sizer)
|
||||||
@sizer.set_size_hints(self)
|
@sizer.set_size_hints(self)
|
||||||
show()
|
end
|
||||||
|
|
||||||
|
def disable_load
|
||||||
|
@loadBtn.disable
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable_import
|
||||||
|
@importBtn.disable
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_CancelClicked(event)
|
def on_CancelClicked(event)
|
||||||
@@ -89,18 +117,23 @@ module Tailor
|
|||||||
end
|
end
|
||||||
|
|
||||||
def on_ImportClicked(event)
|
def on_ImportClicked(event)
|
||||||
|
@tileset = Tailor::Tileset.new
|
||||||
|
@tilesetSlicer.tileset = @tileset
|
||||||
|
@tilesetProperties.set_tileset(@tileset)
|
||||||
wildcards = "*.png;*.bmp;*.tiff;*.gif"
|
wildcards = "*.png;*.bmp;*.tiff;*.gif"
|
||||||
fd = Wx::FileDialog.new(self, "Select tileset to import",
|
fd = Wx::FileDialog.new(self, "Select tileset to import",
|
||||||
:wildcard => wildcards,
|
:wildcard => wildcards,
|
||||||
:style => Wx::FD_FILE_MUST_EXIST | Wx::FD_PREVIEW )
|
:style => Wx::FD_FILE_MUST_EXIST | Wx::FD_PREVIEW )
|
||||||
if fd.show_modal == Wx::ID_OK
|
if fd.show_modal == Wx::ID_OK
|
||||||
@tilesetFilename = fd.get_path
|
|
||||||
@tilesetNames = []
|
@tilesetNames = []
|
||||||
|
@tilesetImage = Wx::Image.new(fd.get_path)
|
||||||
refresh_image
|
refresh_image
|
||||||
(0..(@tilesetSlicer.get_size)).each do |i|
|
(0..(@tilesetSlicer.get_size)).each do |i|
|
||||||
@tilesetNames << "Tile #{i}"
|
@tilesetNames << "Tile #{i}"
|
||||||
end
|
end
|
||||||
@tileNameCtrl.disable
|
@tileNameCtrl.disable
|
||||||
|
self.has_saved = false
|
||||||
|
self.has_loaded = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -118,7 +151,7 @@ module Tailor
|
|||||||
self,
|
self,
|
||||||
style = Wx::PD_CAN_ABORT | Wx::PD_SMOOTH | Wx::PD_AUTO_HIDE)
|
style = Wx::PD_CAN_ABORT | Wx::PD_SMOOTH | Wx::PD_AUTO_HIDE)
|
||||||
dirname = dirfinder.get_path
|
dirname = dirfinder.get_path
|
||||||
tiles = @tilesetSlicer.get_tiles
|
tiles = @tilesetSlicer.get_tile_bitmaps
|
||||||
(0..(tiles.size-1)).each do |i|
|
(0..(tiles.size-1)).each do |i|
|
||||||
tile = tiles[i]
|
tile = tiles[i]
|
||||||
tileName = @tilesetNames[i]
|
tileName = @tilesetNames[i]
|
||||||
@@ -139,12 +172,74 @@ module Tailor
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_SaveClicked(event)
|
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"
|
||||||
|
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.from_file(fd.get_path)
|
||||||
|
tileset_changed
|
||||||
|
end
|
||||||
|
self.has_saved = false
|
||||||
|
self.has_loaded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_SaveClicked(event)
|
||||||
|
wildcards = "*.json"
|
||||||
|
fd = Wx::FileDialog.new(self, "Select tileset to import",
|
||||||
|
:wildcard => wildcards)
|
||||||
|
if fd.show_modal == Wx::ID_OK
|
||||||
|
filename = fd.get_path
|
||||||
|
|
||||||
|
progdialog = Wx::ProgressDialog.new("Saving...",
|
||||||
|
"Saving...",
|
||||||
|
@tilesetSlicer.get_size - 1,
|
||||||
|
self,
|
||||||
|
style = Wx::PD_SMOOTH | Wx::PD_AUTO_HIDE)
|
||||||
|
tiles = @tilesetSlicer.get_tile_bitmaps
|
||||||
|
(0..(tiles.size-1)).each do |i|
|
||||||
|
@tileset.add_tile(@tilesetNames[i], tiles[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
callback = Proc.new do |msg, tile, tileName, tileIndex|
|
||||||
|
progdialog.update(tileIndex)
|
||||||
|
end
|
||||||
|
|
||||||
|
File.open(filename, "w") do |file|
|
||||||
|
@tileset.write(file, callback)
|
||||||
|
end
|
||||||
|
self.has_saved = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_tilepropsChanged(event)
|
def on_tilepropsChanged(event)
|
||||||
return if @tilesetSlicer.get_size < 1
|
return if @tilesetSlicer.get_size < 1
|
||||||
|
self.has_saved = false
|
||||||
ret = Wx::MessageDialog.new(self,
|
ret = Wx::MessageDialog.new(self,
|
||||||
"Changing tile properties will reset all tile names. Continue?",
|
"Changing tile properties will reset all tile names. Continue?",
|
||||||
"WARNING").show_modal
|
"WARNING").show_modal
|
||||||
@@ -155,29 +250,33 @@ module Tailor
|
|||||||
@tileNameCtrl.set_value("")
|
@tileNameCtrl.set_value("")
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Tileset properties changed : #{event.inspect} #{event.client_data}"
|
@tilesetSlicer.refresh_grid
|
||||||
@tilesetSlicer.set_grid(event.client_data['padX'],
|
|
||||||
event.client_data['padY'],
|
|
||||||
event.client_data['pitchX'],
|
|
||||||
event.client_data['pitchY'],
|
|
||||||
event.client_data['gridX'],
|
|
||||||
event.client_data['gridY']
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
def on_tileNameChanged(event)
|
||||||
@tilesetNames[@tilesetSlicer.get_selected_index] = @tileNameCtrl.get_value
|
@tilesetNames[@tilesetSlicer.get_selected_index] = @tileNameCtrl.get_value
|
||||||
|
self.has_saved = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh_image
|
def refresh_image
|
||||||
begin
|
begin
|
||||||
@tilesetImage = Wx::Bitmap.new(@tilesetFilename)
|
|
||||||
if @tilesetImage.is_ok
|
if @tilesetImage.is_ok
|
||||||
@tilesetSlicer.set_image @tilesetImage
|
@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)
|
@sizer.set_size_hints(self)
|
||||||
end
|
end
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ module Tailor
|
|||||||
nil,
|
nil,
|
||||||
"evt_tileprops_changed",
|
"evt_tileprops_changed",
|
||||||
1)
|
1)
|
||||||
def initialize(source, grid)
|
def initialize(source)
|
||||||
super(EVT_TILEPROPS_CHANGED)
|
super(EVT_TILEPROPS_CHANGED)
|
||||||
self.id = source.get_id
|
self.id = source.get_id
|
||||||
self.client_data = grid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -21,11 +20,23 @@ module Tailor
|
|||||||
self.class.send( :define_method, name, &block )
|
self.class.send( :define_method, name, &block )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_tileset(tileset)
|
||||||
|
@tileset = tileset
|
||||||
|
self.tile_x = @tileset.tile_x
|
||||||
|
self.tile_y = @tileset.tile_y
|
||||||
|
self.pad_x = @tileset.pad_x
|
||||||
|
self.pad_y = @tileset.pad_y
|
||||||
|
self.space_x = @tileset.space_x
|
||||||
|
self.space_y = @tileset.space_y
|
||||||
|
refresh
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super(*args)
|
super(*args)
|
||||||
|
@tileset = nil
|
||||||
values = {
|
values = {
|
||||||
"Tile X" => 32,
|
"Tile X" => 0,
|
||||||
"Tile Y" => 32,
|
"Tile Y" => 0,
|
||||||
"Pad X" => 0,
|
"Pad X" => 0,
|
||||||
"Pad Y" => 0,
|
"Pad Y" => 0,
|
||||||
"Space X" => 0,
|
"Space X" => 0,
|
||||||
@@ -46,9 +57,13 @@ module Tailor
|
|||||||
evt_text_enter(elemCtrl) { |event| on_textChanged(event) }
|
evt_text_enter(elemCtrl) { |event| on_textChanged(event) }
|
||||||
tmpsizer.add(elemCtrl, flag = Wx::EXPAND|Wx::ALIGN_RIGHT)
|
tmpsizer.add(elemCtrl, flag = Wx::EXPAND|Wx::ALIGN_RIGHT)
|
||||||
|
|
||||||
rubyname = elem.gsub(/ /, '')
|
rubyname = elem.gsub(/ /, '_').downcase
|
||||||
create_method( "@#{rubyname}=".to_sym ) { |val|
|
create_method( "#{rubyname}=".to_sym ) { |val|
|
||||||
elemCtrl.set_value(val)
|
if val.instance_of?(Integer) or val.instance_of?(Fixnum)
|
||||||
|
elemCtrl.set_value(val.to_s)
|
||||||
|
else
|
||||||
|
elemCtrl.set_value(val)
|
||||||
|
end
|
||||||
}
|
}
|
||||||
create_method( "#{rubyname}".to_sym ) {
|
create_method( "#{rubyname}".to_sym ) {
|
||||||
elemCtrl.get_value.to_i
|
elemCtrl.get_value.to_i
|
||||||
@@ -59,13 +74,15 @@ module Tailor
|
|||||||
end
|
end
|
||||||
|
|
||||||
def on_textChanged(event)
|
def on_textChanged(event)
|
||||||
grid = { 'gridX' => self.TileX,
|
return if @tileset.nil?
|
||||||
'gridY' => self.TileY,
|
@tileset.tile_x = self.tile_x
|
||||||
'pitchX' => self.SpaceX,
|
@tileset.tile_y = self.tile_y
|
||||||
'pitchY' => self.SpaceY,
|
@tileset.space_x = self.space_x
|
||||||
'padX' => self.PadX,
|
@tileset.space_y = self.space_y
|
||||||
'padY' => self.PadY }
|
@tileset.pad_x = self.pad_x
|
||||||
evt = TilesetPropertiesChangedEvent.new(self, grid)
|
@tileset.pad_y = self.pad_y
|
||||||
|
|
||||||
|
evt = TilesetPropertiesChangedEvent.new(self)
|
||||||
event_handler.process_event(evt)
|
event_handler.process_event(evt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
67
lib/tailor/Library.rb
Normal file
67
lib/tailor/Library.rb
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
require 'Tailor/Tileset'
|
||||||
|
|
||||||
|
module Tailor
|
||||||
|
class Library
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@tilesets = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def tileset_names
|
||||||
|
x = []
|
||||||
|
idx = 0
|
||||||
|
@tilesets.each do |tileset|
|
||||||
|
x << tileset.tileset_name
|
||||||
|
idx += 1
|
||||||
|
end
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@tilesets.each do |tileset|
|
||||||
|
yield tileset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
@tilesets.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def tile_count
|
||||||
|
count = 0
|
||||||
|
@tilesets.each do |ts|
|
||||||
|
count += ts.count
|
||||||
|
end
|
||||||
|
count
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(tileset)
|
||||||
|
if tileset.instance_of?(String)
|
||||||
|
@tilesets.delete(by_name(tileset))
|
||||||
|
end
|
||||||
|
@tilesets.delete(tileset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_tileset(tileset)
|
||||||
|
@tilesets << tileset if tileset.instance_of?(Tailor::Tileset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_tileset(path)
|
||||||
|
ts = Tailor::Tileset.new
|
||||||
|
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
|
||||||
|
end
|
||||||
166
lib/tailor/Tileset.rb
Normal file
166
lib/tailor/Tileset.rb
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
require 'wx'
|
||||||
|
require 'json'
|
||||||
|
require 'base64'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
module Tailor
|
||||||
|
class Tileset
|
||||||
|
attr_accessor :image
|
||||||
|
attr_accessor :license
|
||||||
|
attr_accessor :notes
|
||||||
|
attr_accessor :tileset_name
|
||||||
|
attr_accessor :tile_x
|
||||||
|
attr_accessor :tile_y
|
||||||
|
attr_accessor :space_x
|
||||||
|
attr_accessor :space_y
|
||||||
|
attr_accessor :pad_x
|
||||||
|
attr_accessor :pad_y
|
||||||
|
attr_accessor :tiles
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@filename = ''
|
||||||
|
self.image = nil
|
||||||
|
self.license = ''
|
||||||
|
self.notes = ''
|
||||||
|
self.tileset_name = ''
|
||||||
|
self.tile_x = 32
|
||||||
|
self.tile_y = 32
|
||||||
|
self.space_x = 0
|
||||||
|
self.space_y = 0
|
||||||
|
self.pad_x = 0
|
||||||
|
self.pad_y = 0
|
||||||
|
self.tiles = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_tile(name, image)
|
||||||
|
if image.instance_of?(Wx::Bitmap)
|
||||||
|
image = Wx::Image.from_bitmap(image)
|
||||||
|
elsif not image.instance_of?(Wx::Image)
|
||||||
|
throw TypeError("Tailor::Tileset::add_tile only accepts Wx::Image or Wx::Bitmap")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.tiles << {"name" => name, "image" => image}
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
@tiles.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_tile(elem)
|
||||||
|
if elem.instance_of?(String)
|
||||||
|
self.tiles.each do
|
||||||
|
if tile['name'] == elem
|
||||||
|
return tile['image']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif elem.instance_of?(Integer)
|
||||||
|
if elem <= self.tiles.size
|
||||||
|
return self.tiles[elem]['image']
|
||||||
|
end
|
||||||
|
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?
|
||||||
|
callback.call("Saving JSON...", nil, '', 0)
|
||||||
|
end
|
||||||
|
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']
|
||||||
|
self.notes = js['notes']
|
||||||
|
self.tile_x = js['dimensions']['tile_x']
|
||||||
|
self.tile_y = js['dimensions']['tile_y']
|
||||||
|
self.space_x = js['dimensions']['space_x']
|
||||||
|
self.space_y = js['dimensions']['space_y']
|
||||||
|
self.pad_x = js['dimensions']['pad_x']
|
||||||
|
self.pad_y = js['dimensions']['pad_y']
|
||||||
|
StringIO.open do |iostream|
|
||||||
|
iostream.write(Base64.decode64(js['image']))
|
||||||
|
iostream.rewind
|
||||||
|
self.image = Wx::Image.read(iostream, Wx::BITMAP_TYPE_PNG)
|
||||||
|
end
|
||||||
|
self.tiles = []
|
||||||
|
js['tiles'].each do |tile|
|
||||||
|
StringIO.open do |iostream|
|
||||||
|
iostream.write(Base64.decode64(tile['image']))
|
||||||
|
iostream.rewind
|
||||||
|
self.tiles << {
|
||||||
|
'name' => tile['name'],
|
||||||
|
'image' => Wx::Image.read(iostream, Wx::BITMAP_TYPE_PNG)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_json(callback = nil)
|
||||||
|
obj = {
|
||||||
|
"name" => self.tileset_name,
|
||||||
|
"license" => self.license,
|
||||||
|
"notes" => self.notes,
|
||||||
|
"dimensions" => {
|
||||||
|
"tile_x" => self.tile_x,
|
||||||
|
"tile_y" => self.tile_y,
|
||||||
|
"space_x" => self.space_x,
|
||||||
|
"space_y" => self.space_y,
|
||||||
|
"pad_x" => self.pad_x,
|
||||||
|
"pad_y" => self.pad_y
|
||||||
|
},
|
||||||
|
"image" => "",
|
||||||
|
"tiles" => []
|
||||||
|
}
|
||||||
|
|
||||||
|
StringIO.open do |iostream|
|
||||||
|
if self.image.nil?
|
||||||
|
obj['image']=nil
|
||||||
|
else
|
||||||
|
self.image.write(iostream, Wx::BITMAP_TYPE_PNG)
|
||||||
|
iostream.rewind
|
||||||
|
obj['image'] = Base64.encode64(iostream.read)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
self.tiles.each do |tile|
|
||||||
|
StringIO.open do |iostream|
|
||||||
|
if not callback.nil?
|
||||||
|
callback.call("Converting to base64", tile['image'], tile['name'], idx)
|
||||||
|
end
|
||||||
|
tile['image'].write(iostream, Wx::BITMAP_TYPE_PNG)
|
||||||
|
iostream.rewind
|
||||||
|
data = {
|
||||||
|
"name" => tile['name'],
|
||||||
|
"image" => Base64.encode64(iostream.read)
|
||||||
|
}
|
||||||
|
obj['tiles'] << data
|
||||||
|
end
|
||||||
|
idx += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user