iconify 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77f5ba6428215e6714068c04ba0ac9f6112522e2
4
- data.tar.gz: 86effae2ea2f63f5a2cccb91d6ba13aff88bafb4
3
+ metadata.gz: ac171efe65169c0ac77d43eb220ade5922c2fabe
4
+ data.tar.gz: b1b5d585fe38a96c5fef0000293e720c3ea098f8
5
5
  SHA512:
6
- metadata.gz: 4c25bc69efd9b485c792ddedcf6b5ac9ff897b3f903959b3f6e42974b631244ad5da1c6c50465f1d18bb082bc0c20e7313dafcf3051f3715e6ccd8b029be559b
7
- data.tar.gz: 838730d2b89ea8b625b57ce2a3c7fd8fc1ac4329f1f2ade8c4c3a6676b691df62429bda6085cfd23a82271cf38a29dd7691056ffe7fb9bfdb0347972eb89207c
6
+ metadata.gz: 01c795f2a4c8ddce60a0b78d03cb0b6b765ad13d506760d465fd5ed6042b936ca473a35adb9b0319c45692a1d285573525d0cda233dd29786b61f9fbce57bf61
7
+ data.tar.gz: cd8c6ca45d9ef94eea9916aa3e6bce3bd23f932ff5a5daebacd3fc422d25c60f3ad28091ca98f571521933cabe16eaf0ca61143b28526fc253d1a758d82f08a8
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "iconify"
3
+ require 'bundler/setup'
4
+ require 'iconify'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "iconify"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
@@ -4,25 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'iconify/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "iconify"
7
+ spec.name = 'iconify'
8
8
  spec.version = Iconify::VERSION
9
- spec.authors = ["Yoteichi"]
10
- spec.email = ["plonk@piano.email.ne.jp"]
9
+ spec.authors = ['Yoteichi']
10
+ spec.email = ['plonk@piano.email.ne.jp']
11
11
 
12
- spec.summary = %q{Iconify is a utility program to turn a command line into a status icon.}
13
- spec.description = %q{Iconify is a utility program to turn a command line into a status icon.}
14
- spec.homepage = "https://github.com/plonk/iconifiy"
12
+ spec.summary = 'Iconify is a utility program to turn a command line into a status icon.'
13
+ spec.description = 'Iconify is a utility program to turn a command line into a status icon.'
14
+ spec.homepage = 'https://github.com/plonk/iconifiy'
15
15
  spec.licenses = ['GPL-2']
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
18
+ spec.bindir = 'exe'
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency "gtk3"
23
- spec.add_dependency "vte3"
22
+ spec.add_dependency 'gtk3'
23
+ spec.add_dependency 'vte3'
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.11"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency 'bundler', '~> 1.11'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
28
  end
@@ -1,130 +1,241 @@
1
1
  require 'gtk3'
2
2
  require 'vte3'
3
3
 
4
- require "iconify/version"
5
- require "iconify/program"
4
+ require 'iconify/version'
5
+ require 'iconify/program'
6
6
 
7
7
  module Iconify
8
+ # メインウィンドウ
9
+ class TerminalWindow < Gtk::Window
10
+ include Gtk
8
11
 
9
- class TerminalWindow < Gtk::Window
10
- include Gtk
12
+ type_register
13
+ signal_new('changed', GLib::Signal::ACTION, nil, nil)
11
14
 
12
- type_register
13
- signal_new('changed', GLib::Signal::ACTION, nil, nil)
15
+ attr_reader :state
14
16
 
15
- attr_reader :state
17
+ # [String]
18
+ def initialize(argv)
19
+ super()
16
20
 
17
- def initialize(argv)
18
- super()
21
+ @argv = argv
22
+ @state = :stopped
19
23
 
20
- @argv = argv
24
+ update_title
21
25
 
22
- @terminal = Vte::Terminal.new
23
- @terminal.font = Pango::FontDescription.new("monospace 14")
24
- @terminal.set_size_request(@terminal.char_width * 80, @terminal.char_height * 24)
25
- @terminal.cursor_blink_mode = Vte::CursorBlinkMode::OFF
26
+ @rerun_button = create_rerun_button
27
+ @kill_button = create_kill_button
28
+ @quit_button = create_quit_button
29
+ @terminal = create_vte_terminal
30
+ @copy_button = create_copy_button
31
+ @paste_button = create_paste_button
26
32
 
27
- self.title = "iconify - #{argv[0]}"
33
+ layout
28
34
 
29
- @state = :stopped
35
+ set_geometry_hints
36
+ end
30
37
 
31
- vbox = Box.new(:vertical)
32
- hbox = ButtonBox.new(:horizontal)
33
- rerun_button = Button.new(label: "Rerun")
34
- rerun_button.signal_connect('clicked') do
35
- self.exec
38
+ def set_geometry_hints
39
+ @terminal.realize
40
+ @terminal.set_geometry_hints_for_window(self)
36
41
  end
37
- signal_connect('changed') do
38
- rerun_button.sensitive = (@state == :stopped)
42
+
43
+ def layout
44
+ vbox = Box.new(:vertical)
45
+ toolbar = Toolbar.new
46
+
47
+ toolbar.add(@rerun_button)
48
+ toolbar.add(@kill_button)
49
+ toolbar.add(SeparatorToolItem.new)
50
+ toolbar.add(@copy_button)
51
+ toolbar.add(@paste_button)
52
+ toolbar.add(SeparatorToolItem.new)
53
+ toolbar.add(@quit_button)
54
+ vbox.pack_start(toolbar, expand: false)
55
+
56
+ padding_box = Box.new(:vertical)
57
+ padding_box.pack_start(@terminal, expand: true, fill: true)
58
+ padding_box.border_width = 18
59
+ override_background_color(StateFlags::NORMAL, COLORS[15])
60
+ vbox.pack_start(padding_box, expand: true, fill: true)
61
+
62
+ add vbox
39
63
  end
40
64
 
41
- kill_button = Button.new(label: "Kill")
42
- kill_button.signal_connect('clicked') do
43
- Process.kill("KILL", @pid) if @pid
65
+ def create_copy_button
66
+ ToolButton.new(stock_id: Stock::COPY).tap do |b|
67
+ b.tooltip_text = 'Copy'
68
+ b.signal_connect('clicked') do
69
+ @terminal.copy_clipboard
70
+ end
71
+ end
44
72
  end
45
- signal_connect('changed') do
46
- kill_button.sensitive = (@state == :running)
73
+
74
+ def create_paste_button
75
+ ToolButton.new(stock_id: Stock::PASTE).tap do |b|
76
+ b.tooltip_text = 'Paste'
77
+ b.signal_connect('clicked') do
78
+ @terminal.paste_clipboard
79
+ end
80
+ end
47
81
  end
48
82
 
49
- quit_button = Button.new(label: "Quit")
50
- quit_button.signal_connect('clicked') do
51
- Gtk.main_quit
83
+ def create_kill_button
84
+ ToolButton.new(label: 'Kill').tap do |b|
85
+ b.icon_name = Stock::STOP
86
+ b.tooltip_text = 'Stop the child process by sending the QUIT signal.'
87
+ b.signal_connect('clicked') do
88
+ Process.kill('KILL', @pid) if @pid
89
+ end
90
+ end
52
91
  end
53
- hbox.pack_start(rerun_button)
54
- hbox.pack_start(kill_button)
55
- hbox.pack_start(quit_button)
56
- vbox.pack_start(hbox, expand: false)
57
- vbox.pack_start(@terminal, expand: true, fill: true)
58
92
 
59
- add vbox
93
+ def create_rerun_button
94
+ ToolButton.new(label: 'Rerun').tap do |b|
95
+ b.icon_name = Stock::REFRESH
96
+ b.tooltip_text = 'Rerun the program.'
97
+ b.signal_connect('clicked') do
98
+ exec
99
+ end
100
+ end
101
+ end
60
102
 
61
- @terminal.signal_connect('child-exited') do
62
- @state = :stopped
63
- @pid = nil
64
- rerun_button.sensitive = true
65
- signal_emit('changed')
103
+ def create_quit_button
104
+ ToolButton.new(label: 'Quit').tap do |b|
105
+ b.icon_name = Stock::QUIT
106
+ b.tooltip_text = 'Stop the program and quit.'
107
+ b.signal_connect('clicked') do
108
+ Gtk.main_quit
109
+ end
110
+ end
66
111
  end
67
- end
68
112
 
69
- def exec
70
- @pid = @terminal.spawn(argv: @argv)
71
- @state = :running
72
- signal_emit('changed')
73
- end
113
+ def update_title
114
+ self.title = "iconify - #{@argv[0]}"
115
+ end
74
116
 
75
- end
117
+ def changed
118
+ @rerun_button.sensitive = (@state == :stopped)
119
+ @kill_button.sensitive = (@state == :running)
76
120
 
77
- class CommandStatusIcon < Gtk::StatusIcon
78
- include Gtk
121
+ @copy_button.sensitive = @terminal.has_selection?
79
122
 
80
- def initialize(name)
81
- super()
123
+ signal_emit('changed')
124
+ end
82
125
 
83
- @name = name
84
- end
126
+ # the shimbun color scheme
127
+ COLORS = [[0x30, 0x30, 0x30],
128
+ [0xbe, 0x11, 0x37],
129
+ [0x29, 0x73, 0x2c],
130
+ [0xc9, 0x5c, 0x26],
131
+ [0x2a, 0x5a, 0xa2],
132
+ [0xcd, 0x3a, 0x93],
133
+ [0x07, 0x86, 0x92],
134
+ [0xd0, 0xd0, 0xd0],
135
+ [0x50, 0x50, 0x50],
136
+ [0xe6, 0x2b, 0x5d],
137
+ [0x40, 0x9e, 0x01],
138
+ [0xec, 0x75, 0x42],
139
+ [0x17, 0x7f, 0xe0],
140
+ [0xe9, 0x53, 0xba],
141
+ [0x00, 0xa9, 0xb2],
142
+ [0xf2, 0xf2, 0xf2]]
143
+ .map { |rgb| Gdk::RGBA.new(*rgb.map { |n| n.fdiv(255) }, 1.0) }
144
+
145
+ def create_vte_terminal
146
+ Vte::Terminal.new.tap do |t|
147
+ t.font = Pango::FontDescription.new('monospace 14')
148
+ t.set_size_request(t.char_width * 40, t.char_height * 12)
149
+ t.set_size(80, 24)
150
+ t.cursor_blink_mode = Vte::CursorBlinkMode::OFF
151
+ t.set_colors(COLORS[0], COLORS[15], COLORS)
152
+
153
+ t.signal_connect('child-exited') do
154
+ on_child_exited
155
+ end
156
+ t.signal_connect('selection-changed') do
157
+ changed
158
+ end
159
+ end
160
+ end
85
161
 
86
- def set_state(state)
87
- case state
88
- when :running
89
- @background_color = [0.67, 1.0, 0.0]
90
- @foreground_color = [0.1, 0.1, 0.1]
91
- when :stopped
92
- @background_color = [192/255.0, 0.0, 0.0]
93
- @foreground_color = [0.9, 0.9, 0.9]
94
- else
95
- raise "unknown sate #{state}"
96
- end
97
- redraw
162
+ def on_child_exited
163
+ @state = :stopped
164
+ @pid = nil
165
+ @rerun_button.sensitive = true
166
+ changed
167
+ end
168
+
169
+ def exec
170
+ @pid = @terminal.spawn(argv: @argv)
171
+ @state = :running
172
+ changed
173
+ end
98
174
  end
99
175
 
100
- def redraw
101
- # pixmap = Gdk::Pixmap.new(nil, 64, 64, 24)
176
+ # プログラム名を表示するステータスアイコン
177
+ class CommandStatusIcon < Gtk::StatusIcon
178
+ include Gtk
179
+ include Cairo
180
+
181
+ def initialize(name)
182
+ super()
102
183
 
103
- image_surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, 64, 64)
184
+ @name = name
185
+ end
104
186
 
105
- cr = Cairo::Context.new(image_surface)
187
+ COLOR_SCHEME = {
188
+ running: [[0.67, 1.0, 0.0], [0.1, 0.1, 0.1]],
189
+ stopped: [[0.75, 0.0, 0.0], [0.9, 0.9, 0.9]]
190
+ }.freeze
106
191
 
107
- cr.set_source_rgb(0.8, 0.8, 0.8)
108
- cr.set_operator(Cairo::OPERATOR_SOURCE)
109
- cr.paint
192
+ def update(state)
193
+ raise "unknown sate #{state}" unless COLOR_SCHEME.key?(state)
110
194
 
111
- cr.set_source_rgb(*@background_color)
112
- cr.rounded_rectangle(1, 1, 62, 62, 15)
113
- cr.fill
195
+ @background_color, @foreground_color = COLOR_SCHEME[state]
196
+ redraw
197
+ end
114
198
 
115
- cr.set_font_size(24)
116
- cr.move_to(3, 64 / 2 + cr.font_extents.ascent / 2)
117
- cr.set_source_rgba(*@foreground_color, 1)
118
- cr.show_text(@name)
199
+ def using(destroyable)
200
+ yield destroyable
201
+ destroyable.destroy
202
+ end
119
203
 
120
- cr.destroy
204
+ def paint_background(cr)
205
+ cr.save do
206
+ cr.set_source_rgb(0.8, 0.8, 0.8)
207
+ cr.set_operator(OPERATOR_SOURCE)
208
+ cr.paint
209
+ end
210
+ end
121
211
 
122
- buf = image_surface.to_pixbuf(0, 0, 64, 64)
123
- self.pixbuf = buf
212
+ def fill_rounded_rectangle(cr)
213
+ cr.save do
214
+ cr.set_source_rgb(*@background_color)
215
+ cr.rounded_rectangle(1, 1, 62, 62, 15)
216
+ cr.fill
217
+ end
218
+ end
124
219
 
125
- image_surface.destroy
126
- end
220
+ def draw_name(cr)
221
+ cr.save do
222
+ cr.set_font_size(24)
223
+ cr.move_to(3, 64 / 2 + cr.font_extents.ascent / 2)
224
+ cr.set_source_rgba(*@foreground_color, 1)
225
+ cr.show_text(@name)
226
+ end
227
+ end
127
228
 
128
- end
229
+ def redraw
230
+ using ImageSurface.new(FORMAT_ARGB32, 64, 64) do |surface|
231
+ using Context.new(surface) do |cr|
232
+ paint_background(cr)
233
+ fill_rounded_rectangle(cr)
234
+ draw_name(cr)
235
+ end
129
236
 
237
+ self.pixbuf = surface.to_pixbuf(0, 0, 64, 64)
238
+ end
239
+ end
240
+ end
130
241
  end
@@ -1,59 +1,82 @@
1
+ require 'optparse'
2
+
1
3
  module Iconify
4
+ class Program
5
+ include Gtk
2
6
 
3
- class Program
4
- def initialize(argv)
5
- if argv.size < 1
6
- STDERR.puts "arg"
7
- exit 1
8
- end
9
- @argv = argv
10
- @status_icon = CommandStatusIcon.new(argv[0])
11
- @terminal_window = TerminalWindow.new(argv)
12
- @terminal_window.signal_connect('delete-event') do
13
- if @status_icon.embedded?
14
- @terminal_window.hide
15
- else
16
- run_dialog("The status icon is not embedded in a notification area. The window cannot be hidden.")
7
+ def initialize(argv)
8
+ @start_minimized = false
9
+
10
+ parse_args!(argv)
11
+
12
+ @argv = argv
13
+ @status_icon = CommandStatusIcon.new(argv[0])
14
+ @terminal_window = TerminalWindow.new(argv)
15
+ @terminal_window.signal_connect('delete-event') do
16
+ if @status_icon.embedded?
17
+ @terminal_window.hide
18
+ else
19
+ run_dialog('The status icon is not embedded in a notification area. The window cannot be hidden.')
20
+ end
21
+ true # do not close
17
22
  end
18
- true # do not close
19
- end
20
- @terminal_window.signal_connect('changed') do
21
- @status_icon.set_state(@terminal_window.state)
22
- @terminal_window.icon = @status_icon.pixbuf
23
- end
24
- @terminal_window.show_all
25
- GLib::Timeout.add(500) do
26
- if @status_icon.embedded?
27
- @terminal_window.hide
28
- else
29
- run_dialog("Iconify has detected its status icon is not embedded in a notification area. The window cannot be hidden.")
23
+ @terminal_window.signal_connect('changed') do
24
+ @status_icon.update(@terminal_window.state)
25
+ @terminal_window.icon = @status_icon.pixbuf
26
+ end
27
+ @terminal_window.show_all
28
+ GLib::Timeout.add(500) do
29
+ if @start_minimized
30
+ if @status_icon.embedded?
31
+ @terminal_window.hide
32
+ else
33
+ run_dialog('Iconify has detected its status icon is not embedded in a notification area. The window cannot be hidden.')
34
+ end
35
+ end
36
+ false # one time
37
+ end
38
+
39
+ @status_icon.signal_connect('activate') do
40
+ if @terminal_window.visible? && @status_icon.embedded?
41
+ @terminal_window.hide
42
+ else
43
+ @terminal_window.show
44
+ end
30
45
  end
31
- false # one time
32
46
  end
33
47
 
34
- @status_icon.signal_connect("activate") do
35
- if @terminal_window.visible? && @status_icon.embedded?
36
- @terminal_window.hide
37
- else
38
- @terminal_window.show
48
+ def parse_args!(argv)
49
+ parser = OptionParser.new do |opts|
50
+ opts.banner = 'Usage: iconify [OPTIONS] COMMAND [ARGUMENTS...]'
51
+
52
+ opts.on('-m', '--minimized', 'Start minimized') do |m|
53
+ @start_minimized = m
54
+ end
55
+ end
56
+ parser.parse!(argv)
57
+
58
+ if argv.empty?
59
+ STDERR.puts parser.banner
60
+ exit 1
39
61
  end
62
+ rescue OptionParser::InvalidOption => e
63
+ STDERR.puts e
64
+ exit 1
40
65
  end
41
- end
42
66
 
43
- def run_dialog(message)
44
- dialog = Gtk::MessageDialog.new(@terminal_window,
45
- Gtk::Dialog::DESTROY_WITH_PARENT,
46
- Gtk::MessageDialog::QUESTION,
47
- Gtk::MessageDialog::BUTTONS_CLOSE,
48
- message)
49
- dialog.run
50
- dialog.destroy
51
- end
67
+ def run_dialog(message)
68
+ dialog = MessageDialog.new(parent: @terminal_window,
69
+ flags: DialogFlags::DESTROY_WITH_PARENT,
70
+ type: MessageType::QUESTION,
71
+ buttons: ButtonsType::CLOSE,
72
+ message: message)
73
+ dialog.run
74
+ dialog.destroy
75
+ end
52
76
 
53
- def run
54
- @terminal_window.exec
55
- Gtk.main
77
+ def run
78
+ @terminal_window.exec
79
+ Gtk.main
80
+ end
56
81
  end
57
82
  end
58
-
59
- end
@@ -1,3 +1,3 @@
1
1
  module Iconify
2
- VERSION = "0.2.0"
2
+ VERSION = '0.2.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iconify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoteichi