cryptopunks-gui 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2f736dec6d87a407c215776056a10f0c85fc26c847e67f42952f494c3d6729a
4
- data.tar.gz: 16f48f9207cbfdb66ec804efb566d85438901ac836f9d6029d10b7aea2039ced
3
+ metadata.gz: ce9f96af3964a08b2afadfa66fffae558338ced44cc09871fb3e967eeff84bc0
4
+ data.tar.gz: 669c2e11acfda26508c3494b4cf66bfd3213fac457b30257dfb6edf2dd590fc8
5
5
  SHA512:
6
- metadata.gz: 2d30b9e4506bfd1c41badfd467526013a2474b9a708215acb2a947423eaedbab312423b765622b3630257cc17f6eb8bda166c6fb12394230f8ba44d53fc14ca2
7
- data.tar.gz: 9de36325f799f16dc00544aa7f67f407d993e10f9457cc5d9ac27de47605bceeb9ed7e35314a4acaee0afe3e9371ab73565dee03ffab1a1fc2b7fb63992ad53d
6
+ metadata.gz: 0e8be7e8519cb582a373acfa18d93ad4f62f954c7c8f0278694a66481e828ee248133caffbf2f0a73ce86ae7c8398ef24287d7b834da637b5db7923847ea4aad
7
+ data.tar.gz: '068c1cc0ea6687d2e5c915673973897febd2613b37569d349502031414b798b53aa7de85ab591c69968d21e99a6f81c223710dd476653ee8a0f69fc37f2b65bd'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.12
4
+
5
+ - Upgrade to glimmer-dsl-tk 0.0.46
6
+ - File & Help Menus
7
+ - Update COLLECTIONS_YAML_URL to point to cryptopunksnotdead repository (recently moved there)
8
+ - Add 'Mirror', 'Flip', 'Led' and 'Sketch' details (e.g. spacing or line) to output file name (e.g. '...-mirror.png')
9
+ - Fix issue with allowing out of bounds image index when entering index that is equal to count of images in a collection (off by 1 error)
10
+ - Fix written file name for downloaded sprites (was excluding .png before)
11
+ - Fix issue with @punk_directory mistakenly referenced after being removed through refactoring
12
+
3
13
  ## 0.0.11
4
14
 
5
15
  - Support configuring default collections through a web hosted yaml file at: https://raw.githubusercontent.com/AndyObtiva/cryptopunks-gui/master/cryptopunks-collections.yml
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021 Andy Maleh
1
+ Copyright (c) 2021 Crypto Punk's Not Dead
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # <img src="https://raw.githubusercontent.com/AndyObtiva/cryptopunks-gui/master/icons/cryptopunks-gui.png" height=85 /> CryptoPunks GUI 0.0.11
1
+ # <img src="https://raw.githubusercontent.com/AndyObtiva/cryptopunks-gui/master/icons/cryptopunks-gui.png" height=85 /> CryptoPunks GUI 0.0.12
2
2
  ## Simplified Minting
3
3
  [![Gem Version](https://badge.fury.io/rb/cryptopunks-gui.svg)](http://badge.fury.io/rb/cryptopunks-gui)
4
4
 
5
- This is a Graphical User Interface for the famous [cryptopunks Ruby gem](https://github.com/cryptopunksnotdead/cryptopunks/tree/master/cryptopunks).
5
+ This is a Graphical User Interface for the [cryptopunks Ruby gem](https://github.com/cryptopunksnotdead/cryptopunks/tree/master/cryptopunks).
6
6
 
7
7
  It automatically downloads image collection sprites on first use (e.g. `punks.png` from https://github.com/larvalabs/cryptopunks).
8
8
 
@@ -18,14 +18,14 @@ Minted cryptopunks are stored at `~/cryptopunks/` by default ([output location c
18
18
 
19
19
  ## Setup
20
20
 
21
- You can use CryptoPunks GUI via gem or via cloning repository.
21
+ You can use CryptoPunks GUI via gem or via cloning repository (if you encounter any issues, please try older versions of the gem and report the issues encountered).
22
22
 
23
23
  ### Gem
24
24
 
25
25
  Run:
26
26
 
27
27
  ```
28
- gem install cryptopunks-gui -v0.0.11
28
+ gem install cryptopunks-gui -v0.0.12
29
29
  ```
30
30
 
31
31
  Afterwards, run app via:
@@ -236,7 +236,7 @@ You may select a new output location by clicking on the `...` button.
236
236
 
237
237
  [MIT](LICENSE.txt)
238
238
 
239
- Copyright (c) 2021 - Cryptopunks GUI by [Andy Maleh](https://github.com/AndyObtiva)
239
+ Copyright (c) 2021 - Cryptopunks GUI by [Crypto Punk's Not Dead](https://github.com/cryptopunksnotdead)
240
240
 
241
241
  --
242
242
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.11
1
+ 0.0.12
@@ -1,323 +1,42 @@
1
1
  require 'glimmer-dsl-tk'
2
+ require 'glimmer/data_binding/observer'
2
3
  require 'cryptopunks'
3
4
  require 'facets'
4
5
  require 'fileutils'
5
6
  require 'net/http'
6
7
  require 'uri'
7
- require 'glimmer/data_binding/observer'
8
8
  require 'yaml'
9
9
  require 'puts_debuggerer'
10
10
 
11
+ require_relative 'model/image'
12
+ require_relative 'view/menu_bar'
13
+ require_relative 'view/image_frame'
14
+ require_relative 'view/style_options_frame'
15
+
11
16
  class CryptopunksGui
12
17
  include Glimmer
13
-
14
- PALETTES = ['Standard'] + (Palette8bit.constants).map(&:name).map {|palette| palette.split('_').map(&:capitalize).join(' ')}.reject { |palette| palette.include?(' ') }.sort
15
- STYLES = ['Normal', 'Led', 'Sketch']
16
- COLLECTIONS_YAML_URL = 'https://raw.githubusercontent.com/AndyObtiva/cryptopunks-gui/master/cryptopunks-collections.yml'
17
- COLLECTIONS_YAML_PATH = File.expand_path('../cryptopunks-collections.yml', __dir__)
18
-
19
- attr_accessor :collection, :image_index, :zoom, :palette, :style, :led_spacing, :led_round_corner, :sketch_line, :flip, :mirror
18
+ include View::MenuBar
19
+ include View::ImageFrame
20
+ include View::StyleOptionsFrame
20
21
 
21
22
  def initialize
22
- initialize_punk_directory
23
- initialize_collections_map
24
- initialize_collection
25
- load_config
26
- initialize_defaults
27
- observe_image_attribute_changes
23
+ @image = Model::Image.new
28
24
  create_gui
29
- self.image_index = 0
30
- @root.open
31
- end
32
-
33
- def collection_options
34
- @collections_map.keys
35
- end
36
-
37
- def palette_options
38
- PALETTES
39
- end
40
-
41
- def style_options
42
- STYLES
43
- end
44
-
45
- def initialize_punk_directory
46
- @punk_directory = @punk_config_directory = File.join(Dir.home, 'cryptopunks')
47
- FileUtils.mkdir_p(@punk_directory)
48
- end
49
-
50
- def initialize_collections_map
51
- begin
52
- http_response = Net::HTTP.get_response(URI(COLLECTIONS_YAML_URL))
53
- if http_response.is_a?(Net::HTTPSuccess)
54
- @collections_map = YAML.load(http_response.body)
55
- else
56
- raise "code: #{http_response.code} message: #{http_response.message}"
57
- end
58
- rescue StandardError, SocketError => e
59
- puts "Failed to utilize collection YAML from: #{COLLECTIONS_YAML_URL}"
60
- puts e.full_message
61
- puts "Utilizing local collection YAML instead: #{COLLECTIONS_YAML_PATH}"
62
- @collections_map = YAML.load(File.read(COLLECTIONS_YAML_PATH))
63
- end
25
+ @image.image_index = 0
64
26
  end
65
27
 
66
- def initialize_collection
67
- return if @collection && @collection == @last_collection
68
- @collection ||= @collections_map.keys.first
69
- url = @collections_map[@collection][:url]
70
- width = @collections_map[@collection][:width]
71
- height = @collections_map[@collection][:height]
72
- @punk_file = File.join(@punk_config_directory, File.basename(url, '.png'))
73
- File.write(@punk_file, Net::HTTP.get(URI(url))) unless File.exist?(@punk_file)
74
- @images ||= {}
75
- @images[@collection] ||= Punks::Image::Composite.read(@punk_file, width: width, height: height)
76
- @last_collection = @collection
77
- self.image_index = 0
78
- @image_index_spinbox.to = @images[@collection].size - 1 if @image_index_spinbox
79
- end
80
-
81
- def load_config
82
- @punk_config_file = File.join(@punk_config_directory, 'cryptopunks.yml')
83
- FileUtils.touch(@punk_config_file)
84
- @punk_config = YAML.load(File.read(@punk_config_file)) || {punk_directory: @punk_directory}
85
- @punk_directory = @punk_config[:punk_directory]
86
- end
87
-
88
- def save_config
89
- File.write(@punk_config_file, YAML.dump(@punk_config))
90
- end
91
-
92
- def initialize_defaults
93
- @collection = @collections_map.keys.first
94
- @zoom = 12
95
- @palette = PALETTES.first
96
- @style = STYLES.first
97
- @led_spacing = 2
98
- @led_round_corner = false
99
- @sketch_line = 1
100
- @mirror = false
101
- @flip = false
102
- end
103
-
104
- def observe_image_attribute_changes
105
- observer = Glimmer::DataBinding::Observer.proc { generate_image }
106
- observer.observe(self, :collection)
107
- observer.observe(self, :image_index)
108
- observer.observe(self, :zoom)
109
- observer.observe(self, :palette)
110
- observer.observe(self, :style)
111
- observer.observe(self, :led_spacing)
112
- observer.observe(self, :led_round_corner)
113
- observer.observe(self, :sketch_line)
114
- observer.observe(self, :mirror)
115
- observer.observe(self, :flip)
116
- end
117
-
118
- def generate_image
119
- initialize_collection
120
- return if @image_index.to_i > @images[@collection].size
121
- image_location = File.join(@punk_directory, "#{@collection.gsub(' ', '').downcase}-#{@image_index}#{"x#{@zoom}" if @zoom.to_i > 1}#{"-#{@palette.underscore}" if @palette != PALETTES.first}#{"-#{@style.underscore}" if @style != STYLES.first}.png")
122
- puts "Writing punk image to #{image_location}"
123
- selected_punk = @images[@collection][@image_index.to_i]
124
- selected_punk = selected_punk.change_palette8bit(Palette8bit.const_get(@palette.gsub(' ', '_').upcase.to_sym)) if @palette != PALETTES.first
125
- @original_zoom = @zoom
126
- if @previous_collection && @collection != @previous_collection && @collections_map[@collection][:width] != @collections_map[@previous_collection][:width]
127
- @zoom = @collections_map[@collection][:default_zoom]
128
- end
129
- if @style != STYLES.first
130
- style_options = {}
131
- if @style == 'Led'
132
- style_options[:spacing] = @led_spacing.to_i
133
- style_options[:round_corner] = @led_round_corner
134
- end
135
- if @style == 'Sketch'
136
- style_options[:line] = @sketch_line.to_i
137
- end
138
- selected_punk = selected_punk.send(@style.underscore, @zoom.to_i, **style_options)
139
- end
140
- selected_punk = selected_punk.mirror if @mirror
141
- selected_punk = selected_punk.flip if @flip
142
- selected_punk = selected_punk.zoom(@zoom.to_i) if @style == STYLES.first
143
- selected_punk.save(image_location)
144
- @image_label.image = image_location
145
- @output_location_entry.text = image_location
146
- @previous_style = @style
147
- @previous_collection = @collection
148
- notify_observers(:zoom) if @zoom != @original_zoom
28
+ def launch
29
+ @root.open
149
30
  end
150
31
 
151
32
  def create_gui
152
- @root = root {
33
+ @root = root { |root_proxy|
153
34
  title 'CryptoPunks GUI'
154
35
  iconphoto File.expand_path('../icons/cryptopunks-gui.png', __dir__)
155
36
 
156
- frame {
157
- label {
158
- text 'Collection:'
159
- }
160
- combobox {
161
- readonly true
162
- text <=> [self, :collection]
163
- }
164
-
165
- label {
166
- text 'Image Index:'
167
- }
168
- @image_index_spinbox = spinbox {
169
- from 0
170
- to @images[@collection].size - 1
171
- text <=> [self, :image_index]
172
- }
173
-
174
- label {
175
- text 'Zoom:'
176
- }
177
- spinbox {
178
- from 1
179
- to 72
180
- text <=> [self, :zoom]
181
- }
182
-
183
- label {
184
- text 'Palette:'
185
- }
186
- combobox {
187
- readonly true
188
- text <=> [self, :palette]
189
- }
190
-
191
- label {
192
- text 'Style:'
193
- }
194
- combobox {
195
- readonly true
196
- text <=> [self, :style, after_write: ->(val) {add_style_options}]
197
- }
198
-
199
- @style_options_frame = frame {
200
- padding 0
201
- }
202
-
203
- frame {
204
- padding 0
205
-
206
- checkbutton {
207
- grid row: 0, column: 0, column_weight: 0
208
- variable <=> [self, :mirror]
209
- }
210
- label {
211
- grid row: 0, column: 1
212
- text 'Mirror'
213
-
214
- on('Button-1') do
215
- self.mirror = !mirror
216
- end
217
- }
218
-
219
- checkbutton {
220
- grid row: 0, column: 2
221
- variable <=> [self, :flip]
222
- }
223
- label {
224
- grid row: 0, column: 3
225
- text 'Flip'
226
-
227
- on('Button-1') do
228
- self.flip = !flip
229
- end
230
- }
231
- }
232
-
233
- label {
234
- text 'Output Location:'
235
- }
236
- frame {
237
- padding 0
238
-
239
- @output_location_entry = entry {
240
- grid row: 0, column: 0
241
- readonly true
242
- width 47
243
- }
244
- button {
245
- grid row: 0, column: 1
246
- text '...'
247
- width 1.1
248
-
249
- on('command') do
250
- new_punk_directory = choose_directory(parent: @root)
251
- unless new_punk_directory.to_s.empty?
252
- @punk_directory = new_punk_directory
253
- @punk_config[:punk_directory] = @punk_directory
254
- save_config
255
- generate_image
256
- end
257
- end
258
- }
259
- }
260
-
261
- @image_label = label {
262
- grid row_weight: 1
263
- }
264
- }
265
- }
266
- end
267
-
268
- def add_style_options
269
- @style_options_frame.content {
270
- @style_options_frame.children.each(&:destroy)
271
- if @style == 'Led'
272
- frame {
273
- padding 0
274
-
275
- label {
276
- grid row: 0, column: 0, column_weight: 0
277
- text 'Spacing:'
278
- }
279
- spinbox {
280
- grid row: 0, column: 1
281
- from 1
282
- to 72
283
- text <=> [self, :led_spacing]
284
- }
285
-
286
- checkbutton {
287
- grid row: 0, column: 2
288
- variable <=> [self, :led_round_corner]
289
- }
290
- label {
291
- grid row: 0, column: 3
292
- text 'Round Corner'
293
-
294
- on('Button-1') do
295
- self.led_round_corner = !led_round_corner
296
- end
297
- }
298
- }
299
- elsif @style == 'Sketch'
300
- frame {
301
- padding 0
302
-
303
- label {
304
- grid row: 0, column: 0, column_weight: 0
305
- text 'Line:'
306
- }
307
- spinbox {
308
- grid row: 0, column: 1
309
- from 1
310
- to 72
311
- text <=> [self, :sketch_line]
312
- }
313
- }
314
- else
315
- frame { # filler
316
- padding 0
317
- height 0
318
- width 0
319
- }
320
- end
37
+ cryptopunks_gui_menu_bar(root: root_proxy, image: @image)
38
+
39
+ image_frame(root: root_proxy, image: @image)
321
40
  }
322
41
  end
323
42
  end
@@ -0,0 +1,187 @@
1
+ class CryptopunksGui
2
+ module Model
3
+ class Image
4
+ PALETTES = ['Standard'] + (Palette8bit.constants).map(&:name).map {|palette| palette.split('_').map(&:capitalize).join(' ')}.reject { |palette| palette.include?(' ') }.sort
5
+ STYLES = ['Normal', 'Led', 'Sketch']
6
+ OUTPUT_LOCATION_DEFAULT = File.join(Dir.home, 'cryptopunks')
7
+ CONFIG_FILE = File.join(OUTPUT_LOCATION_DEFAULT, 'cryptopunks.yml')
8
+ COLLECTIONS_YAML_PATH = File.join(OUTPUT_LOCATION_DEFAULT, 'cryptopunks-collections.yml')
9
+ COLLECTIONS_YAML_URL = 'https://raw.githubusercontent.com/cryptopunksnotdead/cryptopunks-gui/master/cryptopunks-collections.yml'
10
+ COLLECTIONS_YAML_REPO_PATH = File.expand_path('../../cryptopunks-collections.yml', __dir__)
11
+
12
+ attr_accessor :collection, :image_index, :zoom, :palette, :style, :led_spacing, :led_round_corner, :sketch_line, :flip, :mirror,
13
+ :collection_size, :collections_map, :images, :image_location, :output_location, :config
14
+
15
+ def initialize
16
+ initialize_output_location
17
+ initialize_collections_map
18
+ initialize_collection
19
+ load_config
20
+ initialize_defaults
21
+ observe_image_attribute_changes
22
+ end
23
+
24
+ def collection_options
25
+ @collections_map.keys.select {|collection_name| @collections_map[collection_name][:enabled]}
26
+ end
27
+
28
+ def palette_options
29
+ PALETTES
30
+ end
31
+
32
+ def style_options
33
+ STYLES
34
+ end
35
+
36
+ def initialize_output_location
37
+ @output_location = OUTPUT_LOCATION_DEFAULT
38
+ FileUtils.mkdir_p(@output_location)
39
+ end
40
+
41
+ def initialize_collections_map(reset: false)
42
+ FileUtils.touch(COLLECTIONS_YAML_PATH)
43
+ @collections_map = reset ? {} : (YAML.load(File.read(COLLECTIONS_YAML_PATH)) || {})
44
+ new_collections_map = {}
45
+ begin
46
+ http_response = Net::HTTP.get_response(URI(COLLECTIONS_YAML_URL))
47
+ if http_response.is_a?(Net::HTTPSuccess)
48
+ new_collections_map = YAML.load(http_response.body)
49
+ else
50
+ raise "code: #{http_response.code} message: #{http_response.message}"
51
+ end
52
+ rescue StandardError, SocketError => e
53
+ puts "Failed to utilize collection YAML from: #{COLLECTIONS_YAML_URL}"
54
+ puts e.full_message
55
+ puts "Utilizing local collection YAML instead: #{COLLECTIONS_YAML_REPO_PATH}"
56
+ new_collections_map = YAML.load(File.read(COLLECTIONS_YAML_REPO_PATH)) rescue {}
57
+ end
58
+ @collections_map_observers ||= {}
59
+ new_collections_map.each do |collection_name, collection_options|
60
+ @collections_map[collection_name] ||= {}
61
+ original_collections_map_for_collection = @collections_map[collection_name]
62
+ @collections_map[collection_name] = @collections_map[collection_name]
63
+ @collections_map[collection_name].reverse_merge!(collection_options)
64
+ @collections_map[collection_name][:enabled] = true unless @collections_map[collection_name].has_key?(:enabled)
65
+ @collections_map_observers[collection_name] ||= Glimmer::DataBinding::Observer.proc { |value, key|
66
+ if key == :enabled
67
+ self.collection = @collections_map.find { |name, options| options[:enabled] }.first if key == :enabled && value == false && @collection == collection_name
68
+ notify_observers(:collection_options)
69
+ end
70
+ save_collections_map
71
+ }.tap {|o| o.observe(original_collections_map_for_collection)}
72
+ end
73
+ @collections_map_observer ||= Glimmer::DataBinding::Observer.proc { |collection_options, collection_name|
74
+ if collection_options.nil?
75
+ self.collection = @collections_map.find { |name, options| options[:enabled] }.first if @collection == collection_name
76
+ self.collection = collection_name if @collections_map.select { |name, options| options[:enabled] }.count == 0
77
+ end
78
+ notify_observers(:collection_options)
79
+ save_collections_map
80
+ }.tap {|o| o.observe(@collections_map)}
81
+ save_collections_map
82
+ end
83
+
84
+ def save_collections_map
85
+ normalized_collections_map = Hash[@collections_map.map {|k,v| [k, v.to_h]}]
86
+ File.write(COLLECTIONS_YAML_PATH, YAML.dump(normalized_collections_map))
87
+ end
88
+
89
+ def initialize_collection
90
+ return if @collection && @collection == @last_collection
91
+ @collection ||= @collections_map.keys.first
92
+ url = @collections_map[@collection][:url]
93
+ width = @collections_map[@collection][:width]
94
+ height = @collections_map[@collection][:height]
95
+ @image_file = File.join(OUTPUT_LOCATION_DEFAULT, File.basename(url))
96
+ File.write(@image_file, Net::HTTP.get(URI(url))) unless File.exist?(@image_file)
97
+ @images ||= {}
98
+ @images[@collection] ||= Punks::Image::Composite.read(@image_file, width: width, height: height)
99
+ @last_collection = @collection
100
+ self.image_index = 0
101
+ end
102
+
103
+ def load_config
104
+ FileUtils.touch(CONFIG_FILE)
105
+ @config = YAML.load(File.read(CONFIG_FILE)) || {}
106
+ @output_location = @config[:output_location]
107
+ @config[:output_location] = @output_location = OUTPUT_LOCATION_DEFAULT if @output_location.nil?
108
+ end
109
+
110
+ def save_config
111
+ File.write(CONFIG_FILE, YAML.dump(@config))
112
+ end
113
+
114
+ def initialize_defaults
115
+ @collection = @collections_map.keys.first
116
+ @zoom = 12
117
+ @palette = PALETTES.first
118
+ @style = STYLES.first
119
+ @led_spacing = 2
120
+ @led_round_corner = false
121
+ @sketch_line = 1
122
+ @mirror = false
123
+ @flip = false
124
+ end
125
+
126
+ def observe_image_attribute_changes
127
+ observer = Glimmer::DataBinding::Observer.proc { generate_image }
128
+ observer.observe(self, :collection)
129
+ observer.observe(self, :image_index)
130
+ observer.observe(self, :zoom)
131
+ observer.observe(self, :palette)
132
+ observer.observe(self, :style)
133
+ observer.observe(self, :led_spacing)
134
+ observer.observe(self, :led_round_corner)
135
+ observer.observe(self, :sketch_line)
136
+ observer.observe(self, :mirror)
137
+ observer.observe(self, :flip)
138
+ end
139
+
140
+ def generate_image
141
+ initialize_collection
142
+ return if @image_index.to_i >= @images[@collection].size
143
+ new_image_location = File.join(@output_location, "#{@collection.gsub(' ', '').downcase}-#{@image_index}#{"x#{@zoom}" if @zoom.to_i > 1}#{"-#{@palette.underscore}" if @palette != PALETTES.first}#{"-#{@style.underscore}" if @style != STYLES.first}#{"-spacing#{@led_spacing.to_i}" if @style == 'Led'}#{'-round-corner' if @style == 'Led' && @led_round_corner}#{"-line#{@sketch_line.to_i}" if @style == 'Sketch'}#{'-mirror' if @mirror}#{'-flip' if @flip}.png")
144
+ puts "Writing punk image to #{new_image_location}"
145
+ selected_punk = @images[@collection][@image_index.to_i]
146
+ selected_punk = selected_punk.change_palette8bit(Palette8bit.const_get(@palette.gsub(' ', '_').upcase.to_sym)) if @palette != PALETTES.first
147
+ @original_zoom = @zoom
148
+ if @previous_collection && @collection != @previous_collection && @collections_map[@collection][:width] != @collections_map[@previous_collection][:width]
149
+ @zoom = @collections_map[@collection][:default_zoom]
150
+ end
151
+ if @style != STYLES.first
152
+ style_options = {}
153
+ if @style == 'Led'
154
+ style_options[:spacing] = @led_spacing.to_i
155
+ style_options[:round_corner] = @led_round_corner
156
+ end
157
+ if @style == 'Sketch'
158
+ style_options[:line] = @sketch_line.to_i
159
+ end
160
+ selected_punk = selected_punk.send(@style.underscore, @zoom.to_i, **style_options)
161
+ end
162
+ selected_punk = selected_punk.mirror if @mirror
163
+ selected_punk = selected_punk.flip if @flip
164
+ selected_punk = selected_punk.zoom(@zoom.to_i) if @style == STYLES.first
165
+ selected_punk.save(new_image_location)
166
+ self.image_location = new_image_location
167
+ notify_observers(:zoom) if @zoom != @original_zoom
168
+ @previous_style = @style
169
+ @previous_collection = @collection
170
+ end
171
+
172
+ def change_output_location(new_output_location)
173
+ @output_location = new_output_location
174
+ @config[:output_location] = @output_location
175
+ save_config
176
+ generate_image
177
+ end
178
+
179
+ def reset_output_location
180
+ @output_location = OUTPUT_LOCATION_DEFAULT
181
+ @config[:output_location] = @output_location
182
+ save_config
183
+ generate_image
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,31 @@
1
+ class CryptopunksGui
2
+ module View
3
+ module HelpDialog
4
+ def help_dialog(root: )
5
+ toplevel(root) {
6
+ title 'CryptoPunks GUI - README.md'
7
+ width 800
8
+ height 600
9
+ escapable true
10
+
11
+ help_dialog = text {
12
+ grid row: 0, column: 0, row_weight: 1, column_weight: 1
13
+ value help
14
+ }
15
+
16
+ help_dialog_yscrollbar = scrollbar {
17
+ grid row: 0, column: 1
18
+ orient 'vertical'
19
+ }
20
+ help_dialog.yscrollbar help_dialog_yscrollbar.tk
21
+
22
+ help_dialog_xscrollbar = scrollbar {
23
+ grid row: 1, column: 0, column_span: 2
24
+ orient 'horizontal'
25
+ }
26
+ help_dialog.xscrollbar help_dialog_xscrollbar.tk
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,145 @@
1
+ class CryptopunksGui
2
+ module View
3
+ module ImageFrame
4
+ def image_frame(root: , image: )
5
+ image_label = output_location_entry = image_index_spinbox = style_options_container_frame = nil
6
+
7
+ frame {
8
+ label {
9
+ text 'Collection:'
10
+ }
11
+ combobox {
12
+ readonly true
13
+ values <= [image, :collection_options]
14
+ text <=> [image, :collection]
15
+ }
16
+
17
+ label {
18
+ text 'Image Index:'
19
+ }
20
+ image_index_spinbox = spinbox {
21
+ from 0
22
+ to image.images[image.collection].size - 1
23
+ text <=> [image, :image_index]
24
+ }
25
+
26
+ label {
27
+ text 'Zoom:'
28
+ }
29
+ spinbox {
30
+ from 1
31
+ to 72
32
+ text <=> [image, :zoom]
33
+ }
34
+
35
+ label {
36
+ text 'Palette:'
37
+ }
38
+ combobox {
39
+ readonly true
40
+ text <=> [image, :palette]
41
+ }
42
+
43
+ label {
44
+ text 'Style:'
45
+ }
46
+ combobox {
47
+ readonly true
48
+ text <=> [image, :style, after_write: ->(val) {add_style_options(style_options_container_frame: style_options_container_frame, image: image)}]
49
+ }
50
+
51
+ style_options_container_frame = frame {
52
+ padding 0
53
+ }
54
+
55
+ frame {
56
+ padding 0
57
+
58
+ checkbutton {
59
+ grid row: 0, column: 0, column_weight: 0
60
+ variable <=> [image, :mirror]
61
+ }
62
+ label {
63
+ grid row: 0, column: 1
64
+ text 'Mirror'
65
+
66
+ on('Button-1') do
67
+ image.mirror = !image.mirror
68
+ end
69
+ }
70
+
71
+ checkbutton {
72
+ grid row: 0, column: 2
73
+ variable <=> [image, :flip]
74
+ }
75
+ label {
76
+ grid row: 0, column: 3
77
+ text 'Flip'
78
+
79
+ on('Button-1') do
80
+ image.flip = !image.flip
81
+ end
82
+ }
83
+ }
84
+
85
+ label {
86
+ text 'Output Location:'
87
+ }
88
+ frame {
89
+ padding 0
90
+
91
+ output_location_entry = entry {
92
+ grid row: 0, column: 0
93
+ readonly true
94
+ width 47
95
+ }
96
+ button {
97
+ grid row: 0, column: 1
98
+ text '...'
99
+ width 1.1
100
+
101
+ on('command') do
102
+ change_output_location(root: root, image: image)
103
+ end
104
+ }
105
+ }
106
+
107
+ image_label = label {
108
+ grid row_weight: 1
109
+ }
110
+ }
111
+
112
+ register_image_frame_observers(image_label: image_label, output_location_entry: output_location_entry, image_index_spinbox: image_index_spinbox, image: image)
113
+ end
114
+
115
+ def register_image_frame_observers(image_label: , output_location_entry: , image_index_spinbox: , image: )
116
+ Glimmer::DataBinding::Observer.proc do
117
+ image_label.image = output_location_entry.text = image.image_location
118
+ end.observe(image, :image_location)
119
+
120
+ Glimmer::DataBinding::Observer.proc do
121
+ image_index_spinbox.to = image.images[image.collection].size - 1
122
+ end.observe(image, :images)
123
+ end
124
+
125
+ def add_style_options(style_options_container_frame: , image: )
126
+ style_options_container_frame.children.each(&:destroy)
127
+ style_options_container_frame.content {
128
+ case image.style
129
+ when 'Led'
130
+ led_style_options_frame(image: image)
131
+ when 'Sketch'
132
+ sketch_style_options_frame(image: image)
133
+ else
134
+ no_style_options_frame
135
+ end
136
+ }
137
+ end
138
+
139
+ def change_output_location(root: , image: )
140
+ new_output_location = choose_directory(parent: root)
141
+ image.change_output_location(new_output_location) unless new_output_location.to_s.empty?
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,88 @@
1
+ # require_relative 'preferences_dialog'
2
+ require_relative 'help_dialog'
3
+
4
+ class CryptopunksGui
5
+ module View
6
+ module MenuBar
7
+ # include View::PreferencesDialog
8
+ include View::HelpDialog
9
+
10
+ LICENSE = File.expand_path('../../LICENSE.txt', __dir__)
11
+ HELP = File.expand_path('../../README.md', __dir__)
12
+
13
+ def cryptopunks_gui_menu_bar(root: , image: )
14
+ menu {
15
+ if OS.mac?
16
+ menu(:application) {
17
+ menu_item(:about, label: 'About') {
18
+ accelerator OS.mac? ? 'Command+Shift+A' : 'Control+Alt+A'
19
+
20
+ on('command') do
21
+ show_about_dialog(root: root)
22
+ end
23
+ }
24
+
25
+ # menu_item(:preferences) {
26
+ # on('command') do
27
+ # preferences_dialog(root: root, image: image)
28
+ # end
29
+ # }
30
+ }
31
+ end
32
+
33
+ menu(label: 'File', underline: 0) {
34
+ menu_item(label: 'Change Output Location...', underline: 7) {
35
+ accelerator OS.mac? ? 'Command+O' : 'Control+O'
36
+
37
+ on('command') do
38
+ change_output_location(root: root, image: image) # assumes View::ImageFrame is mixed in
39
+ end
40
+ }
41
+
42
+ menu_item(label: 'Reset Output Location', underline: 0) {
43
+ on('command') do
44
+ image.reset_output_location
45
+ end
46
+ }
47
+
48
+ menu_item(:separator)
49
+
50
+ # menu_item(label: 'Preferences...', underline: 0) {
51
+ # on('command') do
52
+ # preferences_dialog(root: root, image: image)
53
+ # end
54
+ # }
55
+
56
+ menu_item(:separator)
57
+
58
+ menu_item(label: 'Exit', underline: 1) {
59
+ on('command') do
60
+ exit(0)
61
+ end
62
+ }
63
+ }
64
+
65
+ menu(label: 'Help') {
66
+ menu_item(:help) {
67
+ on('command') do
68
+ help_dialog(root: root)
69
+ end
70
+ }
71
+ }
72
+ }
73
+ end
74
+
75
+ def show_about_dialog(root: )
76
+ message_box(parent: root, title: 'CryptoPunks GUI', message: "CryptoPunks GUI\n\n#{license}")
77
+ end
78
+
79
+ def license
80
+ @license ||= File.read(LICENSE)
81
+ end
82
+
83
+ def help
84
+ @help ||= File.read(HELP)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,174 @@
1
+ class CryptopunksGui
2
+ module View
3
+ module PreferencesDialog
4
+ def preferences_dialog(root: , image: )
5
+ toplevel(root) {
6
+ title 'Preferences'
7
+ width 1280
8
+ height 700
9
+ escapable true
10
+
11
+ scrollbar_frame {
12
+ label {
13
+ grid row: 0, column: 0
14
+ text ''
15
+ }
16
+
17
+ label {
18
+ grid row: 0, column: 1
19
+ text 'Collection Name'
20
+ }
21
+
22
+ label {
23
+ grid row: 0, column: 2
24
+ text 'URL'
25
+ width 82
26
+ }
27
+
28
+ label {
29
+ grid row: 0, column: 3
30
+ text 'Width'
31
+ width 3
32
+ }
33
+
34
+ label {
35
+ grid row: 0, column: 4
36
+ text 'Height'
37
+ width 3
38
+ }
39
+
40
+ label {
41
+ grid row: 0, column: 5
42
+ text "Default\nZoom"
43
+ width 3
44
+ }
45
+
46
+ label {
47
+ grid row: 0, column: 6, column_span: 3
48
+ text 'Actions'
49
+ }
50
+
51
+ image.collections_map.each_with_index do |pair, index|
52
+ collection_name, collection_options = pair
53
+ row = index + 1
54
+
55
+ checkbutton { |cb|
56
+ grid row: row, column: 0
57
+ # TODO consider using hash_proxy object to bind more effectively
58
+ variable <= [image.collections_map[collection_name], :enabled]
59
+
60
+ on('command') do
61
+ collection_options[:enabled] = cb.variable
62
+ end
63
+ }
64
+
65
+ entry {
66
+ grid row: row, column: 1
67
+ text collection_name
68
+ }
69
+
70
+ entry {
71
+ grid row: row, column: 2
72
+ text collection_options[:url]
73
+ width 82
74
+ }
75
+
76
+ spinbox {
77
+ grid row: row, column: 3
78
+ from 1
79
+ to 512
80
+ text collection_options[:width]
81
+ width 3
82
+ }
83
+
84
+ spinbox {
85
+ grid row: row, column: 4
86
+ from 1
87
+ to 512
88
+ text collection_options[:height]
89
+ width 3
90
+ }
91
+
92
+ spinbox {
93
+ grid row: row, column: 5
94
+ from 1
95
+ to 72
96
+ text collection_options[:default_zoom]
97
+ width 3
98
+ }
99
+
100
+ button {
101
+ grid row: row, column: 6
102
+ text 'X'
103
+ width 1
104
+ }
105
+ button {
106
+ grid row: row, column: 7
107
+ text '^'
108
+ width 1
109
+ }
110
+ button {
111
+ grid row: row, column: 8
112
+ text 'v'
113
+ width 1
114
+ }
115
+ end
116
+
117
+ row = image.collections_map.keys.size + 1
118
+
119
+ checkbutton {
120
+ grid row: row, column: 0
121
+
122
+ }
123
+
124
+ entry {
125
+ grid row: row, column: 1
126
+ text ''
127
+ }
128
+
129
+ entry {
130
+ grid row: row, column: 2
131
+ text ''
132
+ width 82
133
+ }
134
+
135
+ spinbox {
136
+ grid row: row, column: 3
137
+ from 1
138
+ to 512
139
+ text '24'
140
+ width 3
141
+ }
142
+
143
+ spinbox {
144
+ grid row: row, column: 4
145
+ from 1
146
+ to 512
147
+ text '24'
148
+ width 3
149
+ }
150
+
151
+ spinbox {
152
+ grid row: row, column: 5
153
+ from 1
154
+ to 72
155
+ text '12'
156
+ width 3
157
+ }
158
+ }
159
+
160
+ frame {
161
+ button {
162
+ text 'Reset'
163
+
164
+ on('command') do
165
+ image.initialize_collections_map(reset: true)
166
+ # TODO cause an update to all collections
167
+ end
168
+ }
169
+ }
170
+ }
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,60 @@
1
+ class CryptopunksGui
2
+ module View
3
+ module StyleOptionsFrame
4
+ def led_style_options_frame(image: )
5
+ frame {
6
+ padding 0
7
+
8
+ label {
9
+ grid row: 0, column: 0, column_weight: 0
10
+ text 'Spacing:'
11
+ }
12
+ spinbox {
13
+ grid row: 0, column: 1
14
+ from 1
15
+ to 72
16
+ text <=> [image, :led_spacing]
17
+ }
18
+
19
+ checkbutton {
20
+ grid row: 0, column: 2
21
+ variable <=> [image, :led_round_corner]
22
+ }
23
+ label {
24
+ grid row: 0, column: 3
25
+ text 'Round Corner'
26
+
27
+ on('Button-1') do
28
+ image.led_round_corner = !image.led_round_corner
29
+ end
30
+ }
31
+ }
32
+ end
33
+
34
+ def sketch_style_options_frame(image: )
35
+ frame {
36
+ padding 0
37
+
38
+ label {
39
+ grid row: 0, column: 0, column_weight: 0
40
+ text 'Line:'
41
+ }
42
+ spinbox {
43
+ grid row: 0, column: 1
44
+ from 1
45
+ to 72
46
+ text <=> [image, :sketch_line]
47
+ }
48
+ }
49
+ end
50
+
51
+ def no_style_options_frame
52
+ frame { # filler
53
+ padding 0
54
+ height 0
55
+ width 0
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: cryptopunks-gui 0.0.11 ruby app
5
+ # stub: cryptopunks-gui 0.0.12 ruby app
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "cryptopunks-gui".freeze
9
- s.version = "0.0.11"
9
+ s.version = "0.0.12"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["app".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2021-11-06"
14
+ s.date = "2021-11-22"
15
15
  s.description = "CryptoPunks GUI for Simplified Minting - Built with Glimmer DSL for Tk (requires ActiveTcl to run cryptopunks-gui command)".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.executables = ["cryptopunks-gui".freeze]
@@ -26,6 +26,12 @@ Gem::Specification.new do |s|
26
26
  "README.md",
27
27
  "VERSION",
28
28
  "app/cryptopunks_gui.rb",
29
+ "app/model/image.rb",
30
+ "app/view/help_dialog.rb",
31
+ "app/view/image_frame.rb",
32
+ "app/view/menu_bar.rb",
33
+ "app/view/preferences_dialog.rb",
34
+ "app/view/style_options_frame.rb",
29
35
  "bin/cryptopunks-gui",
30
36
  "cryptopunks-gui.gemspec",
31
37
  "icons/cryptopunks-gui.icns",
@@ -34,7 +40,7 @@ Gem::Specification.new do |s|
34
40
  s.homepage = "http://github.com/AndyObtiva/cryptopunks-gui".freeze
35
41
  s.licenses = ["MIT".freeze]
36
42
  s.post_install_message = "To launch CryptoPunks GUI, run command: cryptopunks-gui".freeze
37
- s.rubygems_version = "3.2.22".freeze
43
+ s.rubygems_version = "3.2.31".freeze
38
44
  s.summary = "CryptoPunks GUI for Simplified Minting".freeze
39
45
 
40
46
  if s.respond_to? :specification_version then
@@ -42,7 +48,7 @@ Gem::Specification.new do |s|
42
48
  end
43
49
 
44
50
  if s.respond_to? :add_runtime_dependency then
45
- s.add_runtime_dependency(%q<glimmer-dsl-tk>.freeze, ["~> 0.0.33"])
51
+ s.add_runtime_dependency(%q<glimmer-dsl-tk>.freeze, ["= 0.0.47"])
46
52
  s.add_runtime_dependency(%q<cryptopunks>.freeze, ["~> 2.0.1"])
47
53
  s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
48
54
  s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
@@ -50,7 +56,7 @@ Gem::Specification.new do |s|
50
56
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
51
57
  s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
52
58
  else
53
- s.add_dependency(%q<glimmer-dsl-tk>.freeze, ["~> 0.0.33"])
59
+ s.add_dependency(%q<glimmer-dsl-tk>.freeze, ["= 0.0.47"])
54
60
  s.add_dependency(%q<cryptopunks>.freeze, ["~> 2.0.1"])
55
61
  s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
56
62
  s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cryptopunks-gui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-06 00:00:00.000000000 Z
11
+ date: 2021-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer-dsl-tk
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.33
19
+ version: 0.0.47
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.33
26
+ version: 0.0.47
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: cryptopunks
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -124,6 +124,12 @@ files:
124
124
  - README.md
125
125
  - VERSION
126
126
  - app/cryptopunks_gui.rb
127
+ - app/model/image.rb
128
+ - app/view/help_dialog.rb
129
+ - app/view/image_frame.rb
130
+ - app/view/menu_bar.rb
131
+ - app/view/preferences_dialog.rb
132
+ - app/view/style_options_frame.rb
127
133
  - bin/cryptopunks-gui
128
134
  - cryptopunks-gui.gemspec
129
135
  - icons/cryptopunks-gui.icns
@@ -147,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
153
  - !ruby/object:Gem::Version
148
154
  version: '0'
149
155
  requirements: []
150
- rubygems_version: 3.2.22
156
+ rubygems_version: 3.2.31
151
157
  signing_key:
152
158
  specification_version: 4
153
159
  summary: CryptoPunks GUI for Simplified Minting