vimamsa 0.1.21 → 0.1.23
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 +4 -4
- data/README.md +14 -8
- data/custom_example.rb +4 -1
- data/img/screenshot1.png +0 -0
- data/img/screenshot2.png +0 -0
- data/install.sh +4 -0
- data/lib/vimamsa/buffer.rb +129 -8
- data/lib/vimamsa/buffer_changetext.rb +19 -2
- data/lib/vimamsa/buffer_cursor.rb +10 -4
- data/lib/vimamsa/buffer_list.rb +13 -1
- data/lib/vimamsa/buffer_manager.rb +1 -1
- data/lib/vimamsa/conf.rb +3 -1
- data/lib/vimamsa/diff_buffer.rb +137 -0
- data/lib/vimamsa/editor.rb +104 -34
- data/lib/vimamsa/file_manager.rb +10 -3
- data/lib/vimamsa/gui.rb +123 -87
- data/lib/vimamsa/gui_dialog.rb +2 -0
- data/lib/vimamsa/gui_file_panel.rb +93 -0
- data/lib/vimamsa/gui_form_generator.rb +4 -2
- data/lib/vimamsa/gui_image.rb +2 -4
- data/lib/vimamsa/gui_menu.rb +12 -1
- data/lib/vimamsa/gui_select_window.rb +1 -0
- data/lib/vimamsa/gui_settings.rb +155 -0
- data/lib/vimamsa/gui_sourceview.rb +86 -47
- data/lib/vimamsa/key_actions.rb +58 -13
- data/lib/vimamsa/key_binding_tree.rb +228 -28
- data/lib/vimamsa/key_bindings_vimlike.rb +174 -148
- data/lib/vimamsa/main.rb +1 -0
- data/lib/vimamsa/rbvma.rb +3 -0
- data/lib/vimamsa/string_util.rb +56 -0
- data/lib/vimamsa/util.rb +27 -36
- data/lib/vimamsa/version.rb +1 -1
- metadata +9 -2
data/lib/vimamsa/editor.rb
CHANGED
|
@@ -74,7 +74,7 @@ class Editor
|
|
|
74
74
|
|
|
75
75
|
# build_key_bindings_tree
|
|
76
76
|
@kbd = KeyBindingTree.new()
|
|
77
|
-
$kbd = @kbd
|
|
77
|
+
$kbd = @kbd #TODO: remove global
|
|
78
78
|
require "vimamsa/key_bindings_vimlike"
|
|
79
79
|
|
|
80
80
|
$buffers = BufferList.new
|
|
@@ -110,8 +110,7 @@ class Editor
|
|
|
110
110
|
|
|
111
111
|
settings_path = get_dot_path("settings.rb")
|
|
112
112
|
if File.exist?(settings_path)
|
|
113
|
-
|
|
114
|
-
#TODO
|
|
113
|
+
eval(IO.read(settings_path))
|
|
115
114
|
end
|
|
116
115
|
|
|
117
116
|
custom_script = read_file("", custom_fn)
|
|
@@ -151,6 +150,8 @@ class Editor
|
|
|
151
150
|
end
|
|
152
151
|
end
|
|
153
152
|
|
|
153
|
+
argv_has_files = ARGV.any? { |a| File.file?(File.expand_path(a)) }
|
|
154
|
+
|
|
154
155
|
if fname
|
|
155
156
|
open_new_file(fname)
|
|
156
157
|
else
|
|
@@ -167,6 +168,8 @@ class Editor
|
|
|
167
168
|
# To access via vma.FileFinder
|
|
168
169
|
# self.define_singleton_method(:FileFinder) { @_plugins[:FileFinder] }
|
|
169
170
|
|
|
171
|
+
check_session_restore unless argv_has_files
|
|
172
|
+
|
|
170
173
|
@hook.call(:after_init)
|
|
171
174
|
end
|
|
172
175
|
|
|
@@ -225,6 +228,7 @@ class Editor
|
|
|
225
228
|
end
|
|
226
229
|
|
|
227
230
|
def shutdown()
|
|
231
|
+
save_session
|
|
228
232
|
@hook.call(:shutdown)
|
|
229
233
|
save_state
|
|
230
234
|
@gui.quit
|
|
@@ -233,6 +237,44 @@ class Editor
|
|
|
233
237
|
def save_state
|
|
234
238
|
end
|
|
235
239
|
|
|
240
|
+
def save_session
|
|
241
|
+
fnames = vma.buffers.list
|
|
242
|
+
.map { |b| b.fname }
|
|
243
|
+
.compact
|
|
244
|
+
.select { |f| File.exist?(f) }
|
|
245
|
+
IO.write(get_dot_path("session.txt"), fnames.join("\n") + "\n")
|
|
246
|
+
rescue => ex
|
|
247
|
+
debug "save_session failed: #{ex}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def check_session_restore
|
|
251
|
+
session_path = get_dot_path("session.txt")
|
|
252
|
+
return unless File.exist?(session_path)
|
|
253
|
+
|
|
254
|
+
fnames = File.read(session_path).lines.map(&:strip).reject(&:empty?)
|
|
255
|
+
fnames.select! { |f| File.exist?(f) }
|
|
256
|
+
return if fnames.empty?
|
|
257
|
+
|
|
258
|
+
n = fnames.size
|
|
259
|
+
label = n == 1 ? "1 file" : "#{n} files"
|
|
260
|
+
params = {
|
|
261
|
+
"title" => "Restore previous session? (#{label})",
|
|
262
|
+
"inputs" => {
|
|
263
|
+
"yes_btn" => { :label => "Restore", :type => :button, :default_focus => true },
|
|
264
|
+
"no_btn" => { :label => "No", :type => :button },
|
|
265
|
+
},
|
|
266
|
+
:callback => proc { |x|
|
|
267
|
+
if x["yes_btn"] == "submit"
|
|
268
|
+
initial = vma.buffers.list.find { |b| b.fname.nil? }
|
|
269
|
+
fnames.each { |f| load_buffer(f) }
|
|
270
|
+
initial&.close
|
|
271
|
+
message("Session restored: #{label}")
|
|
272
|
+
end
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
PopupFormGenerator.new(params).run
|
|
276
|
+
end
|
|
277
|
+
|
|
236
278
|
def add_content_search_path(pathstr)
|
|
237
279
|
p = File.expand_path(pathstr)
|
|
238
280
|
if !@file_content_search_paths.include?(p)
|
|
@@ -261,6 +303,9 @@ class Editor
|
|
|
261
303
|
if p and !@file_content_search_paths.include?(p)
|
|
262
304
|
r.insert(0, p)
|
|
263
305
|
end
|
|
306
|
+
|
|
307
|
+
# Ensure that paths are in correct format
|
|
308
|
+
r = r.map{|x|File.expand_path(x)}
|
|
264
309
|
|
|
265
310
|
return r
|
|
266
311
|
end
|
|
@@ -365,35 +410,13 @@ def start_minibuffer_cmd(bufname, bufstr, cmd)
|
|
|
365
410
|
$minibuffer.call_func = method(cmd)
|
|
366
411
|
end
|
|
367
412
|
|
|
368
|
-
def
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
kbd_s << "===============================================\n"
|
|
376
|
-
kbd_s << vma.kbd.to_s
|
|
377
|
-
kbd_s << "\n"
|
|
378
|
-
kbd_s << "===============================================\n"
|
|
379
|
-
b = create_new_buffer(kbd_s, "key-bindings")
|
|
380
|
-
gui_set_file_lang(b.id, "hyperplaintext")
|
|
381
|
-
#
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def diff_buffer()
|
|
385
|
-
bufstr = ""
|
|
386
|
-
orig_path = vma.buf.fname
|
|
387
|
-
infile = Tempfile.new("out")
|
|
388
|
-
infile = Tempfile.new("in")
|
|
389
|
-
infile.write(vma.buf.to_s)
|
|
390
|
-
infile.flush
|
|
391
|
-
cmd = "diff -w '#{orig_path}' #{infile.path}"
|
|
392
|
-
# debug cmd
|
|
393
|
-
bufstr << run_cmd(cmd)
|
|
394
|
-
# debug bufstr
|
|
395
|
-
infile.close; infile.unlink
|
|
396
|
-
create_new_file(nil, bufstr)
|
|
413
|
+
def if_cmd_exists(cmd)
|
|
414
|
+
cmd = cmd.gsub(/[^a-zA-Z0-9_\-]/, '')
|
|
415
|
+
if !system("which #{cmd} > /dev/null 2>&1")
|
|
416
|
+
message("Command \"#{cmd}\" not found!")
|
|
417
|
+
return false
|
|
418
|
+
end
|
|
419
|
+
return true
|
|
397
420
|
end
|
|
398
421
|
|
|
399
422
|
def invoke_command()
|
|
@@ -412,14 +435,14 @@ end
|
|
|
412
435
|
|
|
413
436
|
def minibuffer_end()
|
|
414
437
|
debug "minibuffer_end"
|
|
415
|
-
|
|
438
|
+
vma.kbd.set_mode(:command)
|
|
416
439
|
minibuffer_input = $minibuffer.to_s[0..-2]
|
|
417
440
|
return $minibuffer.call_func.call(minibuffer_input)
|
|
418
441
|
end
|
|
419
442
|
|
|
420
443
|
def minibuffer_cancel()
|
|
421
444
|
debug "minibuffer_cancel"
|
|
422
|
-
|
|
445
|
+
vma.kbd.set_mode(:command)
|
|
423
446
|
minibuffer_input = $minibuffer.to_s[0..-2]
|
|
424
447
|
# $minibuffer.call_func.call('')
|
|
425
448
|
end
|
|
@@ -597,6 +620,7 @@ def open_new_file(filename, file_contents = "")
|
|
|
597
620
|
fname = filename
|
|
598
621
|
bu = load_buffer(fname)
|
|
599
622
|
vma.buffers.set_current_buffer_by_id(bu.id)
|
|
623
|
+
bu.check_autosave_load
|
|
600
624
|
end
|
|
601
625
|
return bu
|
|
602
626
|
end
|
|
@@ -659,3 +683,49 @@ def find_project_dir_of_cur_buffer()
|
|
|
659
683
|
# debug "Proj dir of current file: #{pdir}"
|
|
660
684
|
return pdir
|
|
661
685
|
end
|
|
686
|
+
|
|
687
|
+
def reload_customrb
|
|
688
|
+
custom_fn = get_dot_path("custom.rb")
|
|
689
|
+
custom_script = read_file("", custom_fn)
|
|
690
|
+
eval(custom_script) if custom_script
|
|
691
|
+
message("Reloaded #{custom_fn}")
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def save_settings_to_file
|
|
695
|
+
settings_path = get_dot_path("settings.rb")
|
|
696
|
+
lines = SETTINGS_DEFS.flat_map { |section| section[:settings] }.map do |s|
|
|
697
|
+
key_str = "cnf." + s[:key].join(".")
|
|
698
|
+
val = get(s[:key])
|
|
699
|
+
"#{key_str} = #{val.inspect}"
|
|
700
|
+
end
|
|
701
|
+
IO.write(settings_path, lines.join("\n") + "\n")
|
|
702
|
+
message("Settings saved to #{settings_path}")
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
DEMO_FILES = ["demo.txt", "sheep.jpg", "README.md"]
|
|
706
|
+
|
|
707
|
+
def install_demo_files
|
|
708
|
+
dest_dir = File.expand_path("~/Documents/VimamsaDemo")
|
|
709
|
+
mkdir_if_not_exists dest_dir
|
|
710
|
+
title = "Install demo files to #{dest_dir}?"
|
|
711
|
+
Gui.confirm(title, proc { |x| install_demo_files_callback(x) })
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def install_demo_files_callback(x)
|
|
715
|
+
return unless x["yes_btn"] == "submit"
|
|
716
|
+
src_dir = File.expand_path("../..", __dir__)
|
|
717
|
+
dest_dir = File.expand_path("~/Documents/VimamsaDemo")
|
|
718
|
+
FileUtils.mkdir_p(dest_dir)
|
|
719
|
+
DEMO_FILES.each do |fname|
|
|
720
|
+
src = File.join(src_dir, fname)
|
|
721
|
+
dst = File.join(dest_dir, fname)
|
|
722
|
+
if File.exist?(src)
|
|
723
|
+
FileUtils.cp(src, dst)
|
|
724
|
+
else
|
|
725
|
+
message("Demo file not found: #{src}")
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
open_new_file(File.join(dest_dir, "demo.txt"))
|
|
729
|
+
message("Demo files installed to #{dest_dir}")
|
|
730
|
+
end
|
|
731
|
+
|
data/lib/vimamsa/file_manager.rb
CHANGED
|
@@ -18,7 +18,7 @@ class FileManager
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.init()
|
|
21
|
-
reg_act(:start_file_selector, proc { FileManager.new.run; vma.kbd.set_mode(:file_exp)
|
|
21
|
+
reg_act(:start_file_selector, proc { FileManager.new.run; vma.kbd.set_mode(:file_exp) }, "File selector")
|
|
22
22
|
|
|
23
23
|
reg_act(:fexp_chdir_parent, proc { FileManager.chdir_parent }, "File selector")
|
|
24
24
|
reg_act(:fexp_select, proc { buf.module.select_line }, "")
|
|
@@ -162,13 +162,14 @@ class FileManager
|
|
|
162
162
|
#TODO:
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
# Main inteface, show contents of current dir
|
|
165
166
|
def dir_to_buf(dirpath, b = nil)
|
|
166
167
|
# File.stat("testfile").mtime
|
|
167
168
|
|
|
168
169
|
debug "last file: #{vma.buffers.last_file}", 2
|
|
169
170
|
lastf = vma.buffers.last_file
|
|
170
171
|
jumpto = nil
|
|
171
|
-
if File.dirname(lastf) == dirpath
|
|
172
|
+
if !lastf.nil? and File.dirname(lastf) == dirpath
|
|
172
173
|
jumpto = File.basename(lastf)
|
|
173
174
|
end
|
|
174
175
|
vma.buffers.last_dir = dirpath
|
|
@@ -231,6 +232,13 @@ class FileManager
|
|
|
231
232
|
else
|
|
232
233
|
@buf.set_line_and_column_pos(@header.size, 0)
|
|
233
234
|
end
|
|
235
|
+
|
|
236
|
+
if @cdirs.size > 0
|
|
237
|
+
r = vma.buf.line_range(2, @cdirs.size+1)
|
|
238
|
+
|
|
239
|
+
# Hilight works only if done after buffer is drawn
|
|
240
|
+
run_as_idle proc { Gui.hilight_range(vma.buf, r, color: "#4488ffff") }
|
|
241
|
+
end
|
|
234
242
|
end
|
|
235
243
|
|
|
236
244
|
def fullp(fn)
|
|
@@ -267,7 +275,6 @@ class FileManager
|
|
|
267
275
|
jump_to_file(fn)
|
|
268
276
|
# vma.buffers.set_current_buffer(idx)
|
|
269
277
|
vma.buffers.close_other_buffer(@buf.id)
|
|
270
|
-
|
|
271
278
|
else
|
|
272
279
|
open_with_default_program(fn)
|
|
273
280
|
end
|
data/lib/vimamsa/gui.rb
CHANGED
|
@@ -148,6 +148,12 @@ def gui_create_buffer(id, bufo)
|
|
|
148
148
|
$vmag.buffers[id] = view
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
def gui_close_buffer(id)
|
|
152
|
+
view = vma.gui.buffers.delete(id)
|
|
153
|
+
return if view.nil?
|
|
154
|
+
view.unparent if view.parent
|
|
155
|
+
end
|
|
156
|
+
|
|
151
157
|
def gui_set_file_lang(id, lname)
|
|
152
158
|
view = $vmag.buffers[id]
|
|
153
159
|
lm = GtkSource::LanguageManager.new
|
|
@@ -183,7 +189,8 @@ def gui_set_current_buffer(id)
|
|
|
183
189
|
end
|
|
184
190
|
|
|
185
191
|
def gui_set_window_title(wtitle, subtitle = "")
|
|
186
|
-
|
|
192
|
+
wtitle = wtitle[0..150]
|
|
193
|
+
$vmag.window.title = "Vimamsa - #{wtitle}"
|
|
187
194
|
# $vmag.subtitle.markup = "<span weight='ultrabold'>#{subtitle}</span>"
|
|
188
195
|
$vmag.subtitle.markup = "<span weight='light' size='small'>#{subtitle}</span>"
|
|
189
196
|
# $vmag.window.titlebar.subtitle = subtitle #TODO:gtk4
|
|
@@ -191,7 +198,7 @@ end
|
|
|
191
198
|
|
|
192
199
|
class VMAgui
|
|
193
200
|
attr_accessor :buffers, :sw1, :sw2, :view, :buf1, :window, :delex, :statnfo, :overlay, :sws, :two_c
|
|
194
|
-
attr_reader :two_column, :windows, :subtitle, :app, :active_window
|
|
201
|
+
attr_reader :two_column, :windows, :subtitle, :app, :active_window, :action_trail_label, :file_panel
|
|
195
202
|
|
|
196
203
|
def initialize()
|
|
197
204
|
@two_column = false
|
|
@@ -348,88 +355,35 @@ class VMAgui
|
|
|
348
355
|
sw.set_child(view)
|
|
349
356
|
end
|
|
350
357
|
|
|
351
|
-
|
|
358
|
+
def make_header_button(action_id, icon, cb)
|
|
359
|
+
act = Gio::SimpleAction.new(action_id)
|
|
360
|
+
@app.add_action(act)
|
|
361
|
+
act.signal_connect("activate") { |_a, _p| cb.call }
|
|
362
|
+
btn = Gtk::Button.new
|
|
363
|
+
btn.set_child(Gtk::Image.new(icon_name: icon))
|
|
364
|
+
btn.action_name = "app.#{action_id}"
|
|
365
|
+
btn
|
|
366
|
+
end
|
|
367
|
+
|
|
352
368
|
def init_header_bar()
|
|
353
369
|
header = Gtk::HeaderBar.new
|
|
354
370
|
@header = header
|
|
355
|
-
header.show_close_button = true
|
|
356
|
-
# header.title = ""#TODO:gtk4
|
|
357
|
-
# header.has_subtitle = true#TODO:gtk4
|
|
358
|
-
header.subtitle = ""
|
|
359
|
-
|
|
360
|
-
# icon = Gio::ThemedIcon.new("mail-send-receive-symbolic")
|
|
361
|
-
# icon = Gio::ThemedIcon.new("document-open-symbolic")
|
|
362
|
-
# icon = Gio::ThemedIcon.new("dialog-password")
|
|
363
|
-
|
|
364
|
-
#edit-redo edit-paste edit-find-replace edit-undo edit-find edit-cut edit-copy
|
|
365
|
-
#document-open document-save document-save-as document-properties document-new
|
|
366
|
-
# document-revert-symbolic
|
|
367
|
-
#
|
|
368
|
-
|
|
369
|
-
#TODO:
|
|
370
|
-
# button = Gtk::Button.new
|
|
371
|
-
# icon = Gio::ThemedIcon.new("open-menu-symbolic")
|
|
372
|
-
# image = Gtk::Image.new(:icon => icon, :size => :button)
|
|
373
|
-
# button.add(image)
|
|
374
|
-
# header.append(button)
|
|
375
|
-
|
|
376
|
-
button = Gtk::Button.new
|
|
377
|
-
icon = Gio::ThemedIcon.new("document-open-symbolic")
|
|
378
|
-
image = Gtk::Image.new(:icon => icon, :size => :button)
|
|
379
|
-
button.add(image)
|
|
380
|
-
header.append(button)
|
|
381
|
-
|
|
382
|
-
button.signal_connect "clicked" do |_widget|
|
|
383
|
-
open_file_dialog
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
button = Gtk::Button.new
|
|
387
|
-
icon = Gio::ThemedIcon.new("document-save-symbolic")
|
|
388
|
-
image = Gtk::Image.new(:icon => icon, :size => :button)
|
|
389
|
-
button.add(image)
|
|
390
|
-
header.append(button)
|
|
391
|
-
button.signal_connect "clicked" do |_widget|
|
|
392
|
-
buf.save
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
button = Gtk::Button.new
|
|
396
|
-
icon = Gio::ThemedIcon.new("document-new-symbolic")
|
|
397
|
-
image = Gtk::Image.new(:icon => icon, :size => :button)
|
|
398
|
-
button.add(image)
|
|
399
|
-
header.append(button)
|
|
400
|
-
button.signal_connect "clicked" do |_widget|
|
|
401
|
-
create_new_file
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
box = Gtk::Box.new(:horizontal, 0)
|
|
405
|
-
box.style_context.add_class("linked")
|
|
406
371
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
end
|
|
372
|
+
file_box = Gtk::Box.new(:horizontal, 0)
|
|
373
|
+
file_box.style_context.add_class("linked")
|
|
374
|
+
file_box.append(make_header_button("hdr-open", "document-open-symbolic", proc { open_file_dialog }))
|
|
375
|
+
file_box.append(make_header_button("hdr-save", "document-save-symbolic", proc { buf.save }))
|
|
376
|
+
file_box.append(make_header_button("hdr-new", "document-new-symbolic", proc { create_new_file }))
|
|
377
|
+
header.pack_start(file_box)
|
|
414
378
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
history_switch_forwards
|
|
421
|
-
end
|
|
379
|
+
nav_box = Gtk::Box.new(:horizontal, 0)
|
|
380
|
+
nav_box.style_context.add_class("linked")
|
|
381
|
+
nav_box.append(make_header_button("hdr-prev", "pan-start-symbolic", proc { history_switch_backwards }))
|
|
382
|
+
nav_box.append(make_header_button("hdr-next", "pan-end-symbolic", proc { history_switch_forwards }))
|
|
383
|
+
header.pack_start(nav_box)
|
|
422
384
|
|
|
423
|
-
|
|
424
|
-
icon = Gio::ThemedIcon.new("window-close-symbolic")
|
|
425
|
-
image = Gtk::Image.new(:icon => icon, :size => :button)
|
|
426
|
-
button.add(image)
|
|
427
|
-
box.add(button)
|
|
428
|
-
button.signal_connect "clicked" do |_widget|
|
|
429
|
-
bufs.close_current_buffer
|
|
430
|
-
end
|
|
385
|
+
header.pack_end(make_header_button("hdr-close", "window-close-symbolic", proc { bufs.close_current_buffer }))
|
|
431
386
|
|
|
432
|
-
header.pack_start(box)
|
|
433
387
|
@window.titlebar = header
|
|
434
388
|
end
|
|
435
389
|
|
|
@@ -555,6 +509,8 @@ class VMAgui
|
|
|
555
509
|
@last_debug_idle = Time.now
|
|
556
510
|
app = Gtk::Application.new("net.samiddhi.vimamsa.r#{rand(1000)}", :flags_none)
|
|
557
511
|
@app = app
|
|
512
|
+
|
|
513
|
+
|
|
558
514
|
|
|
559
515
|
Gtk::Settings.default.gtk_application_prefer_dark_theme = true
|
|
560
516
|
Gtk::Settings.default.gtk_theme_name = "Adwaita"
|
|
@@ -569,7 +525,7 @@ class VMAgui
|
|
|
569
525
|
@vpaned = Gtk::Paned.new(:vertical)
|
|
570
526
|
|
|
571
527
|
@vbox = Gtk::Grid.new()
|
|
572
|
-
@window.
|
|
528
|
+
@window.set_child(@vbox)
|
|
573
529
|
|
|
574
530
|
Thread.new {
|
|
575
531
|
GLib::Idle.add(proc { debug_idle_func })
|
|
@@ -577,17 +533,36 @@ class VMAgui
|
|
|
577
533
|
|
|
578
534
|
reset_controllers
|
|
579
535
|
|
|
536
|
+
focus_controller = Gtk::EventControllerFocus.new
|
|
537
|
+
|
|
538
|
+
focus_controller.signal_connect("enter") do
|
|
539
|
+
debug "Gained focus"
|
|
540
|
+
draw_cursor_bug_workaround
|
|
541
|
+
end
|
|
542
|
+
@window.add_controller(focus_controller)
|
|
543
|
+
|
|
544
|
+
motion_controller = Gtk::EventControllerMotion.new
|
|
545
|
+
motion_controller.signal_connect("motion") do |controller, x, y|
|
|
546
|
+
# label.set_text("Mouse at: (%.1f, %.1f)" % [x, y])
|
|
547
|
+
# puts "MOVE #{x} #{y}"
|
|
548
|
+
|
|
549
|
+
# Cursor vanishes when hovering over menubar
|
|
550
|
+
draw_cursor_bug_workaround if y < 30
|
|
551
|
+
@last_cursor = [x, y]
|
|
552
|
+
@cursor_move_time = Time.now
|
|
553
|
+
end
|
|
554
|
+
@window.add_controller(motion_controller)
|
|
555
|
+
|
|
580
556
|
@windows[1] = new_window(1)
|
|
581
557
|
|
|
582
558
|
@last_adj_time = Time.now
|
|
583
559
|
|
|
584
|
-
|
|
585
560
|
# To show keyboard key binding state
|
|
586
561
|
@statnfo = Gtk::Label.new
|
|
587
|
-
|
|
562
|
+
|
|
588
563
|
# To show e.g. current folder
|
|
589
564
|
@subtitle = Gtk::Label.new("")
|
|
590
|
-
|
|
565
|
+
|
|
591
566
|
@statbox = Gtk::Box.new(:horizontal, 2)
|
|
592
567
|
@statnfo.set_size_request(150, 10)
|
|
593
568
|
@statbox.append(@subtitle)
|
|
@@ -609,15 +584,35 @@ class VMAgui
|
|
|
609
584
|
init_minibuffer
|
|
610
585
|
|
|
611
586
|
menubar = Gio::Menu.new
|
|
612
|
-
app.menubar = menubar
|
|
613
|
-
@window.show_menubar = true
|
|
614
|
-
|
|
615
587
|
@menubar = menubar
|
|
616
588
|
|
|
589
|
+
# TODO: Doesn't work, why?:
|
|
590
|
+
# menubar_bar = Gtk::PopoverMenuBar.new(menu_model: menubar)
|
|
591
|
+
|
|
592
|
+
menubar_bar = Gtk::PopoverMenuBar.new()
|
|
593
|
+
menubar_bar.set_menu_model(menubar)
|
|
594
|
+
|
|
595
|
+
menubar_bar.hexpand = true
|
|
596
|
+
@action_trail_label = Gtk::Label.new("")
|
|
597
|
+
@action_trail_label.add_css_class("action-trail")
|
|
598
|
+
menubar_row = Gtk::Box.new(:horizontal, 0)
|
|
599
|
+
menubar_row.append(menubar_bar)
|
|
600
|
+
menubar_row.append(@action_trail_label)
|
|
601
|
+
@vbox.attach(menubar_row, 0, 0, 2, 1)
|
|
602
|
+
|
|
617
603
|
@active_window = @windows[1]
|
|
618
604
|
|
|
605
|
+
init_header_bar
|
|
606
|
+
file_panel_init
|
|
607
|
+
|
|
619
608
|
@window.show
|
|
620
609
|
|
|
610
|
+
surface = @window.native.surface
|
|
611
|
+
tt = Time.now
|
|
612
|
+
surface.signal_connect("layout") do
|
|
613
|
+
# puts "Window resized or moved, other redraw."
|
|
614
|
+
end
|
|
615
|
+
|
|
621
616
|
run_as_idle proc { idle_set_size }
|
|
622
617
|
|
|
623
618
|
prov = Gtk::CssProvider.new
|
|
@@ -645,7 +640,9 @@ class VMAgui
|
|
|
645
640
|
min-width: 15px;
|
|
646
641
|
}
|
|
647
642
|
|
|
648
|
-
popover background > contents { padding: 8px; border-radius: 20px; }
|
|
643
|
+
popover background > contents { padding: 8px; border-radius: 20px; }
|
|
644
|
+
|
|
645
|
+
label.action-trail { font-family: monospace; font-size: 10pt; margin-right: 8px; color: #aaaaaa; }
|
|
649
646
|
")
|
|
650
647
|
@window.style_context.add_provider(prov)
|
|
651
648
|
|
|
@@ -666,9 +663,9 @@ class VMAgui
|
|
|
666
663
|
return true if Time.now - @monitor_time < 0.2
|
|
667
664
|
# Detect element resize
|
|
668
665
|
if swa.width != @sw_width
|
|
669
|
-
|
|
666
|
+
debug "Width change sw_width #{@sw_width}"
|
|
670
667
|
@sw_width = swa.width
|
|
671
|
-
DelayExecutioner.exec(id: :scale_images, wait: 0.7, callable: proc { vma.gui.scale_all_images })
|
|
668
|
+
DelayExecutioner.exec(id: :scale_images, wait: 0.7, callable: proc { debug ":scale_images"; vma.gui.scale_all_images })
|
|
672
669
|
end
|
|
673
670
|
@monitor_time = Time.now
|
|
674
671
|
return true
|
|
@@ -915,4 +912,43 @@ class VMAgui
|
|
|
915
912
|
# view.place_cursor_onscreen #TODO: needed?
|
|
916
913
|
view.draw_cursor
|
|
917
914
|
end
|
|
915
|
+
|
|
916
|
+
def file_panel_init
|
|
917
|
+
@file_panel = FileTreePanel.new
|
|
918
|
+
@file_panel_shown = false
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
def file_panel_refresh
|
|
922
|
+
return unless @file_panel_shown
|
|
923
|
+
@file_panel.refresh
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
def show_file_panel
|
|
927
|
+
return if @file_panel_shown
|
|
928
|
+
inner = @two_column ? @pane : @windows[1][:overlay]
|
|
929
|
+
@vbox.remove(inner)
|
|
930
|
+
@file_panel_pane = Gtk::Paned.new(:horizontal)
|
|
931
|
+
@file_panel_pane.hexpand = true
|
|
932
|
+
@file_panel_pane.vexpand = true
|
|
933
|
+
@file_panel_pane.set_start_child(@file_panel.widget)
|
|
934
|
+
@file_panel_pane.set_end_child(inner)
|
|
935
|
+
@file_panel_pane.set_position(180)
|
|
936
|
+
@vbox.attach(@file_panel_pane, 0, 2, 2, 1)
|
|
937
|
+
@file_panel_shown = true
|
|
938
|
+
@file_panel.refresh
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
def hide_file_panel
|
|
942
|
+
return unless @file_panel_shown
|
|
943
|
+
inner = @file_panel_pane.end_child
|
|
944
|
+
@file_panel_pane.set_start_child(nil)
|
|
945
|
+
@file_panel_pane.set_end_child(nil)
|
|
946
|
+
@vbox.remove(@file_panel_pane)
|
|
947
|
+
@vbox.attach(inner, 0, 2, 2, 1)
|
|
948
|
+
@file_panel_shown = false
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
def toggle_file_panel
|
|
952
|
+
@file_panel_shown ? hide_file_panel : show_file_panel
|
|
953
|
+
end
|
|
918
954
|
end
|
data/lib/vimamsa/gui_dialog.rb
CHANGED
|
@@ -39,6 +39,8 @@ end
|
|
|
39
39
|
class OneInputAction
|
|
40
40
|
def initialize(main_window, title, field_label, button_title, callback, opt = {})
|
|
41
41
|
@window = Gtk::Window.new()
|
|
42
|
+
@window.set_transient_for($vmag.window) if $vmag&.window
|
|
43
|
+
@window.modal = true
|
|
42
44
|
# @window.screen = main_window.screen
|
|
43
45
|
@window.title = ""
|
|
44
46
|
# @window.width_request = 800
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
class FileTreePanel
|
|
2
|
+
COL_LABEL = 0
|
|
3
|
+
COL_BUF_ID = 1 # 0 = folder row (not selectable)
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@store = Gtk::TreeStore.new(String, Integer)
|
|
7
|
+
@tree = Gtk::TreeView.new(@store)
|
|
8
|
+
@tree.headers_visible = false
|
|
9
|
+
@tree.activate_on_single_click = true
|
|
10
|
+
|
|
11
|
+
renderer = Gtk::CellRendererText.new
|
|
12
|
+
renderer.ellipsize = Pango::EllipsizeMode::START
|
|
13
|
+
col = Gtk::TreeViewColumn.new("", renderer, text: COL_LABEL)
|
|
14
|
+
col.expand = true
|
|
15
|
+
@tree.append_column(col)
|
|
16
|
+
|
|
17
|
+
@tree.signal_connect("row-activated") do |tv, path, _col|
|
|
18
|
+
iter = @store.get_iter(path)
|
|
19
|
+
next if iter.nil?
|
|
20
|
+
buf_id = iter[COL_BUF_ID]
|
|
21
|
+
next if buf_id.nil? || buf_id == 0
|
|
22
|
+
vma.buffers.set_current_buffer(buf_id)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@context_menu = nil
|
|
26
|
+
rightclick = Gtk::GestureClick.new
|
|
27
|
+
rightclick.button = 3
|
|
28
|
+
@tree.add_controller(rightclick)
|
|
29
|
+
rightclick.signal_connect("pressed") do |gesture, n_press, x, y|
|
|
30
|
+
result = @tree.get_path_at_pos(x.to_i, y.to_i)
|
|
31
|
+
next unless result
|
|
32
|
+
iter = @store.get_iter(result[0])
|
|
33
|
+
next unless iter
|
|
34
|
+
buf_id = iter[COL_BUF_ID]
|
|
35
|
+
next unless buf_id && buf_id != 0
|
|
36
|
+
@context_buf_id = buf_id
|
|
37
|
+
show_context_menu(x, y)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@sw = Gtk::ScrolledWindow.new
|
|
41
|
+
@sw.set_policy(:never, :automatic)
|
|
42
|
+
@sw.set_child(@tree)
|
|
43
|
+
@sw.set_size_request(180, -1)
|
|
44
|
+
@sw.vexpand = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def widget
|
|
48
|
+
@sw
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def init_context_menu
|
|
52
|
+
act = Gio::SimpleAction.new("fp_close_buffer")
|
|
53
|
+
vma.gui.app.add_action(act)
|
|
54
|
+
act.signal_connect("activate") { vma.buffers.close_buffer(@context_buf_id) if @context_buf_id }
|
|
55
|
+
@context_menu = Gtk::PopoverMenu.new
|
|
56
|
+
@context_menu.set_parent(@tree)
|
|
57
|
+
@context_menu.has_arrow = false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def show_context_menu(x, y)
|
|
61
|
+
init_context_menu if @context_menu.nil?
|
|
62
|
+
menu = Gio::Menu.new
|
|
63
|
+
menu.append("Close file", "app.fp_close_buffer")
|
|
64
|
+
@context_menu.set_menu_model(menu)
|
|
65
|
+
@context_menu.set_pointing_to(Gdk::Rectangle.new(x.to_i, y.to_i, 1, 1))
|
|
66
|
+
@context_menu.popup
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def refresh
|
|
70
|
+
@store.clear
|
|
71
|
+
bh = {}
|
|
72
|
+
vma.buffers.list.each do |b|
|
|
73
|
+
dname = b.fname ? File.dirname(b.fname) : "*"
|
|
74
|
+
bname = b.fname ? File.basename(b.fname) : (b.list_str || "(untitled)")
|
|
75
|
+
bh[dname] ||= []
|
|
76
|
+
bh[dname] << { bname: bname, buf: b }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
bh.keys.sort.each do |dname|
|
|
80
|
+
dir_iter = @store.append(nil)
|
|
81
|
+
dir_iter[COL_LABEL] = "📂 #{tilde_path(dname)}"
|
|
82
|
+
dir_iter[COL_BUF_ID] = 0
|
|
83
|
+
bh[dname].sort_by { |x| x[:bname] }.each do |bnfo|
|
|
84
|
+
active_mark = bnfo[:buf].is_active? ? "● " : " "
|
|
85
|
+
file_iter = @store.append(dir_iter)
|
|
86
|
+
file_iter[COL_LABEL] = "#{active_mark}#{bnfo[:bname]}"
|
|
87
|
+
file_iter[COL_BUF_ID] = bnfo[:buf].id
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@tree.expand_all
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -25,6 +25,8 @@ class PopupFormGenerator
|
|
|
25
25
|
|
|
26
26
|
@callback = params[:callback]
|
|
27
27
|
@window.title = ""
|
|
28
|
+
@window.set_transient_for($vmag.window) if $vmag&.window
|
|
29
|
+
@window.modal = true
|
|
28
30
|
|
|
29
31
|
frame = Gtk::Frame.new()
|
|
30
32
|
frame.margin_bottom = 8
|
|
@@ -56,7 +58,7 @@ class PopupFormGenerator
|
|
|
56
58
|
@vals = {}
|
|
57
59
|
@default_button = nil
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
params["inputs"].each do |id, elem|
|
|
60
62
|
if elem[:type] == :button
|
|
61
63
|
button = Gtk::Button.new(:label => elem[:label])
|
|
62
64
|
hbox.append(button)
|
|
@@ -92,7 +94,7 @@ class PopupFormGenerator
|
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
|
-
end
|
|
97
|
+
end # each
|
|
96
98
|
|
|
97
99
|
vbox.append(hbox)
|
|
98
100
|
|