iconify 0.2.0 → 0.2.1

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
  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