1 -- Luakit configuration file, more information at http://luakit.org/
7 -- Widget construction aliases
8 function eventbox() return widget
{type="eventbox"} end
9 function hbox() return widget
{type="hbox"} end
10 function label() return widget
{type="label"} end
11 function notebook() return widget
{type="notebook"} end
12 function vbox() return widget
{type="vbox"} end
13 function webview() return widget
{type="webview"} end
14 function window() return widget
{type="window"} end
15 function entry() return widget
{type="entry"} end
17 -- Variable definitions
18 HOMEPAGE
= "http://luakit.org/"
19 --HOMEPAGE = "http://github.com/mason-larobina/luakit"
26 font
= "monospace normal 9",
31 statusbar_fg
= "#fff",
32 statusbar_bg
= "#000",
37 loaded_fg
= "#33AADD",
40 selected_tablabel_fg
= "#fff",
41 selected_tablabel_bg
= "#000",
43 -- Enforce a minimum tab width of 30 characters to prevent longer tab
44 -- titles overshadowing small tab titles when things get crowded.
45 tablabel_format
= "%-30s",
48 widget
.add_signal("new", function(wi
)
49 wi
:add_signal("init", function(wi
)
50 if wi
.type == "window" then
51 wi
:add_signal("destroy", function ()
52 -- Call the quit function if this was the last window left
53 if #luakit
.windows
== 0 then luakit
.quit() end
59 -- Add key bindings to be used across all windows
62 -- bind.key({Modifiers}, Key name, function (w, opts) .. end, opts),
63 bind
.key({}, "Escape", function (w
) w
:set_mode() end),
64 bind
.key({"Control"}, "[", function (w
) w
:set_mode() end),
67 -- bind.key({Modifiers}, Key name, function (w, opts) .. end, opts),
68 bind
.key({}, "Escape", function (w
) w
:set_mode() end),
69 bind
.key({}, "i", function (w
) w
:set_mode("insert") end),
70 bind
.key({}, ":", function (w
) w
:set_mode("command") end),
71 bind
.key({}, "h", function (w
) w
:scroll_horiz("-"..SCROLL_STEP
.."px") end),
72 bind
.key({}, "j", function (w
) w
:scroll_vert ("+"..SCROLL_STEP
.."px") end),
73 bind
.key({}, "k", function (w
) w
:scroll_vert ("-"..SCROLL_STEP
.."px") end),
74 bind
.key({}, "l", function (w
) w
:scroll_horiz("+"..SCROLL_STEP
.."px") end),
75 bind
.key({}, "Left", function (w
) w
:scroll_horiz("-"..SCROLL_STEP
.."px") end),
76 bind
.key({}, "Down", function (w
) w
:scroll_vert ("+"..SCROLL_STEP
.."px") end),
77 bind
.key({}, "Up", function (w
) w
:scroll_vert ("-"..SCROLL_STEP
.."px") end),
78 bind
.key({}, "Right", function (w
) w
:scroll_horiz("+"..SCROLL_STEP
.."px") end),
80 -- bind.buf(Pattern, function (w, buffer, opts) .. end, opts),
81 bind
.buf("^[0-9]*H$", function (w
, b
) w
:back (tonumber(string.match(b
, "^(%d*)H$") or 1)) end),
82 bind
.buf("^[0-9]*L$", function (w
, b
) w
:forward(tonumber(string.match(b
, "^(%d*)L$") or 1)) end),
83 bind
.buf("^gg$", function (w
) w
:scroll_vert("0%") end),
84 bind
.buf("^G$", function (w
) w
:scroll_vert("100%") end),
85 bind
.buf("^[0-9]*gT$", function (w
, b
) w
:prev_tab(tonumber(string.match(b
, "^(%d*)gT$") or 1)) end),
86 bind
.buf("^[0-9]*gt$", function (w
, b
) w
:next_tab(tonumber(string.match(b
, "^(%d*)gt$") or 1)) end),
87 bind
.buf("^[\-\+]?[0-9]+[%%|G]$", function (w
, b
) w
:scroll_vert(string.match(b
, "^([\-\+]?%d+)[%%G]$") .. "%") end),
88 bind
.buf("^gH$", function (w
) w
:new_tab(HOMEPAGE
) end),
89 bind
.buf("^gh$", function (w
) w
:navigate(HOMEPAGE
) end),
90 bind
.buf("^ZZ$", function (w
) luakit
.quit() end),
93 bind
.key({}, "Up", function (w
) w
:cmd_hist_prev() end),
94 bind
.key({}, "Down", function (w
) w
:cmd_hist_next() end),
101 -- bind.cmd({Command, Alias1, ...}, function (w, arg, opts) .. end, opts),
102 bind
.cmd({"open", "o"}, function (w
, a
) w
:navigate(a
) end),
103 bind
.cmd({"tabopen", "t"}, function (w
, a
) w
:new_tab(a
) end),
104 bind
.cmd({"back" }, function (w
, a
) w
:back(tonumber(a
) or 1) end),
105 bind
.cmd({"forward", "f"}, function (w
, a
) w
:forward(tonumber(a
) or 1) end),
106 bind
.cmd({"scroll" }, function (w
, a
) w
:scroll_vert(a
) end),
107 bind
.cmd({"quit", "q"}, function (w
) luakit
.quit() end),
110 -- Build and pack window widgets
111 function build_window()
112 -- Create a table for widgets and state variables for a window
124 -- Status bar widgets
128 -- Left aligned widgets
135 -- Fills space between the left and right aligned widgets
137 -- Right aligned widgets
156 w
.ebox
:set_child(w
.layout
)
157 w
.win
:set_child(w
.ebox
)
161 t
.ebox
:set_child(t
.layout
, false, false, 0)
162 w
.layout
:pack_start(t
.ebox
, false, false, 0)
165 w
.layout
:pack_start(w
.tabs
, true, true, 0)
167 -- Pack left-aligned statusbar elements
169 l
.layout
:pack_start(l
.uri
, false, false, 0)
170 l
.layout
:pack_start(l
.loaded
, false, false, 0)
171 l
.ebox
:set_child(l
.layout
)
173 -- Pack right-aligned statusbar elements
175 r
.layout
:pack_start(r
.buf
, false, false, 0)
176 r
.layout
:pack_start(r
.tabi
, false, false, 0)
177 r
.layout
:pack_start(r
.scroll
, false, false, 0)
178 r
.ebox
:set_child(r
.layout
)
180 -- Pack status bar elements
182 s
.layout
:pack_start(l
.ebox
, false, false, 0)
183 s
.layout
:pack_start(s
.filler
, true, true, 0)
184 s
.layout
:pack_start(r
.ebox
, false, false, 0)
185 s
.ebox
:set_child(s
.layout
)
186 w
.layout
:pack_start(s
.ebox
, false, false, 0)
190 i
.layout
:pack_start(i
.prompt
, false, false, 0)
191 i
.layout
:pack_start(i
.input
, true, true, 0)
192 i
.ebox
:set_child(i
.layout
)
193 w
.layout
:pack_start(i
.ebox
, false, false, 0)
196 i
.input
.show_frame
= false
197 w
.tabs
.show_tabs
= false
199 l
.uri
.selectable
= true
204 function attach_window_signals(w
)
205 -- Attach notebook widget signals
206 w
.tabs
:add_signal("page-added", function(nbook
, view
, idx
)
207 w
:update_tab_count(idx
)
208 w
:update_tab_labels()
211 w
.tabs
:add_signal("switch-page", function(nbook
, view
, idx
)
212 w
:update_tab_count(idx
)
213 w
:update_win_title(view
)
215 w
:update_progress(view
)
216 w
:update_tab_labels(idx
)
219 -- Attach window widget signals
220 w
.win
:add_signal("key-press", function(win
, mods
, key
)
221 if w
:hit(mods
, key
) then
226 w
.win
:add_signal("mode-changed", function(win
, mode
)
228 w
.cmd_hist_cursor
= nil
230 if mode
== "normal" then
233 elseif mode
== "insert" then
235 w
.ibar
.input
.text
= ""
236 w
.ibar
.prompt
.text
= "-- INSERT --"
238 elseif mode
== "command" then
240 w
.ibar
.input
.text
= ":"
243 w
.ibar
.input
:set_position(-1)
245 w
.ibar
.prompt
.text
= ""
246 w
.ibar
.input
.text
= ""
250 -- Attach inputbar widget signals
251 w
.ibar
.input
:add_signal("changed", function()
252 -- Auto-exit "command" mode if you backspace or delete the ":"
253 -- character at the start of the input box when in "command" mode.
254 if w
:is_mode("command") and not string.match(w
.ibar
.input
.text
, "^:") then
259 w
.ibar
.input
:add_signal("activate", function()
260 local buffer
= w
.ibar
.input
.text
261 w
:cmd_hist_add(buffer
)
262 w
:match_cmd(string.sub(buffer
, 2))
267 -- Attach signal handlers to a new tab's webview
268 function attach_webview_signals(w
, view
)
269 view
:add_signal("title-changed", function (v
)
270 w
:update_tab_labels()
271 if w
:is_current(v
) then
272 w
:update_win_title(v
)
276 view
:add_signal("property::uri", function(v
)
277 w
:update_tab_labels()
278 if w
:is_current(v
) then
283 view
:add_signal("key-press", function ()
284 -- Only allow key press events to hit the webview if the user is in
286 if not w
:is_mode("insert") then
291 view
:add_signal("load-start", function (v
)
292 if w
:is_current(v
) then
293 w
:update_progress(v
, 0)
298 view
:add_signal("progress-update", function (v
)
299 if w
:is_current(v
) then
304 view
:add_signal("expose", function(v
)
305 if w
:is_current(v
) then
311 -- Parses scroll amounts of the form:
312 -- Relative: "+20%", "-20%", "+20px", "-20px"
313 -- Absolute: 20, "20%", "20px"
314 -- And returns an absolute value.
315 function parse_scroll(current
, max, value
)
316 if string.match(value
, "^%d+px$") then
317 return tonumber(string.match(value
, "^(%d+)px$"))
318 elseif string.match(value
, "^%d+%%$") then
319 return math
.ceil(max * (tonumber(string.match(value
, "^(%d+)%%$")) / 100))
320 elseif string.match(value
, "^[\-\+]%d+px") then
321 return current
+ tonumber(string.match(value
, "^([\-\+]%d+)px"))
322 elseif string.match(value
, "^[\-\+]%d+%%$") then
323 return math
.ceil(current
+ (max * (tonumber(string.match(value
, "^([\-\+]%d+)%%$")) / 100)))
325 print("E: unable to parse scroll amount:", value
)
329 -- Helper functions which operate on a windows widget structure
331 -- Return the widget in the currently active tab
332 get_current
= function(w
) return w
.tabs
:atindex(w
.tabs
:current()) end,
333 -- Check if given widget is the widget in the currently active tab
334 is_current
= function(w
, wi
) return w
.tabs
:indexof(wi
) == w
.tabs
:current() end,
336 -- Wrappers around the mode plugin
337 set_mode
= function(w
, name
) mode
.set(w
.win
, name
) end,
338 get_mode
= function(w
) return mode
.get(w
.win
) end,
339 is_mode
= function(w
, name
) return name
== w
:get_mode() end,
340 is_any_mode
= function(w
, t
, name
) return util
.table.hasitem(t
, name
or w
:get_mode()) end,
342 -- Wrappers around the view:get_prop & view:set_prop methods
343 get
= function (w
, prop
, view
)
344 if not view
then view
= w
:get_current() end
345 return view
:get_prop(prop
)
348 set
= function (w
, prop
, val
, view
)
349 if not view
then view
= w
:get_current() end
350 view
:set_prop(prop
, val
)
353 get_tab_title
= function (w
, view
)
354 if not view
then view
= w
:get_current() end
355 return view
:get_prop("title") or view
.uri
or "(Untitled)"
358 navigate
= function(w
, uri
, view
)
359 (view
or w
:get_current()).uri
= uri
362 new_tab
= function(w
, uri
)
363 local view
= webview()
365 attach_webview_signals(w
, view
)
366 if uri
then view
.uri
= uri
end
367 view
.show_scrollbars
= false
371 -- Wrapper around the bind plugin's hit method
372 hit
= function (w
, mods
, key
)
373 local caught
, newbuf
= bind
.hit(w
.binds
or {}, mods
, key
, w
.buffer
, w
:is_mode("normal"), w
)
379 -- Wrapper around the bind plugin's match_cmd method
380 match_cmd
= function (w
, buffer
)
381 return bind
.match_cmd(commands
, buffer
, w
)
384 -- Command history adding
385 cmd_hist_add
= function(w
, cmd
)
386 if not w
.cmd_hist
then w
.cmd_hist
= {} end
387 -- Make sure history doesn't overflow
388 if #w
.cmd_hist
> ((MAX_HISTORY
or 100) + 5) then
389 while #w
.cmd_hist
> (MAX_HISTORY
or 100) do
390 table.remove(w
.cmd_hist
, 1)
393 table.insert(w
.cmd_hist
, cmd
)
396 -- Command history traversing
397 cmd_hist_prev
= function(w
)
398 if not w
.cmd_hist
then w
.cmd_hist
= {} end
399 if not w
.cmd_hist_cursor
then
400 w
.cmd_hist_cursor
= #w
.cmd_hist
+ 1
401 w
.cmd_hist_current
= w
.ibar
.input
.text
403 local c
= w
.cmd_hist_cursor
- 1
404 if w
.cmd_hist
[c
] then
405 w
.cmd_hist_cursor
= c
406 w
.ibar
.input
.text
= w
.cmd_hist
[c
]
407 w
.ibar
.input
:set_position(-1)
411 cmd_hist_next
= function(w
)
412 if not w
.cmd_hist
then w
.cmd_hist
= {} end
413 local c
= (w
.cmd_hist_cursor
or #w
.cmd_hist
) + 1
414 if w
.cmd_hist
[c
] then
415 w
.cmd_hist_cursor
= c
416 w
.ibar
.input
.text
= w
.cmd_hist
[c
]
417 w
.ibar
.input
:set_position(-1)
418 elseif w
.cmd_hist_current
then
419 w
.cmd_hist_cursor
= nil
420 w
.ibar
.input
.text
= w
.cmd_hist_current
421 w
.ibar
.input
:set_position(-1)
425 -- Webview scroll functions
426 scroll_vert
= function(w
, value
, view
)
427 if not view
then view
= w
:get_current() end
428 local cur
, max = view
:get_scroll_vert()
429 if type(value
) == "string" then
430 value
= parse_scroll(cur
, max, value
)
432 view
:set_scroll_vert(value
)
435 scroll_horiz
= function(w
, value
)
436 if not view
then view
= w
:get_current() end
437 local cur
, max = view
:get_scroll_horiz()
438 if type(value
) == "string" then
439 value
= parse_scroll(cur
, max, value
)
441 view
:set_scroll_horiz(value
)
444 -- Tab traversing functions
445 next_tab
= function(w
, n
)
446 w
.tabs
:switch((((n
or 1) + w
.tabs
:current() -1) % w
.tabs
:count()) + 1)
448 prev_tab
= function(w
, n
)
449 w
.tabs
:switch(((w
.tabs
:current() - (n
or 1) -1) % w
.tabs
:count()) + 1)
451 goto_tab
= function(w
, n
)
455 -- History traversing functions
456 back
= function(w
, n
, view
)
457 (view
or w
:get_current()):go_back(n
or 1)
459 forward
= function(w
, n
, view
)
460 (view
or w
:get_current()):go_forward(n
or 1)
463 -- GUI content update functions
464 update_tab_count
= function (w
, i
, t
)
465 w
.sbar
.r
.tabi
.text
= string.format("[%d/%d]", i
or w
.tabs
:current(), t
or w
.tabs
:count())
468 update_win_title
= function (w
, view
)
469 if not view
then view
= w
:get_current() end
470 local title
= view
:get_prop("title")
472 if not title
and not uri
then
473 w
.win
.title
= "luakit"
475 w
.win
.title
= (title
or "luakit") .. " - " .. (uri
or "about:blank")
479 update_uri
= function (w
, view
, uri
)
480 if not view
then view
= w
:get_current() end
481 w
.sbar
.l
.uri
.text
= (uri
or view
.uri
or "about:blank")
484 update_progress
= function (w
, view
, p
)
485 if not view
then view
= w
:get_current() end
486 if not p
then p
= view
:get_prop("progress") end
487 if not view
:loading() or p
== 1 then
488 w
.sbar
.l
.loaded
:hide()
490 w
.sbar
.l
.loaded
:show()
491 w
.sbar
.l
.loaded
.text
= string.format("(%d%%)", p
* 100)
495 update_scroll
= function (w
, view
)
496 if not view
then view
= w
:get_current() end
497 local val
, max = view
:get_scroll_vert()
498 if max == 0 then val
= "All"
499 elseif val
== 0 then val
= "Top"
500 elseif val
== max then val
= "Bot"
501 else val
= string.format("%2d%%", (val
/max) * 100)
503 w
.sbar
.r
.scroll
.text
= val
506 update_buf
= function (w
)
508 w
.sbar
.r
.buf
.text
= string.format(" %-3s", w
.buffer
)
515 update_binds
= function (w
, mode
)
516 -- Generate the list of binds for this mode + all
517 w
.binds
= util
.table.clone(mode_binds
[mode
])
518 for _
, b
in ipairs(mode_binds
["all"]) do
519 table.insert(w
.binds
, b
)
521 -- Clear & hide buffer
526 -- Tab label functions
527 make_tab_label
= function (w
, pos
)
534 t
.label
.font
= theme
.tablabel_font
or theme
.font
535 t
.layout
:pack_start(t
.label
, true, true, 0)
536 t
.layout
:pack_start(t
.sep
, false, false, 0)
537 t
.ebox
:set_child(t
.layout
)
538 t
.ebox
:add_signal("clicked", function(e
) w
.tabs
:switch(pos
) end)
542 destroy_tab_label
= function(w
, t
)
543 if not t
then t
= table.remove(w
.tbar
.titles
) end
544 for _
, wi
in pairs(t
) do
549 update_tab_labels
= function (w
, current
)
551 local count
, current
= w
.tabs
:count(), current
or w
.tabs
:current()
553 if count
~= #tb
.titles
then
554 -- Grow the number of labels
555 while count
> #tb
.titles
do
556 local t
= w
:make_tab_label(#tb
.titles
+ 1)
557 tb
.layout
:pack_start(t
.ebox
, true, true, 0)
558 table.insert(tb
.titles
, t
)
560 -- Prune number of labels
561 while count
< #tb
.titles
do
562 w
:destroy_tab_label()
568 local t
= tb
.titles
[i
]
569 local title
= " " ..i
.. " "..w
:get_tab_title(w
.tabs
:atindex(i
))
570 t
.label
.text
= string.format(theme
.tablabel_format
or "%s", title
)
571 w
:apply_tablabel_theme(t
, i
== current
)
578 apply_tablabel_theme
= function (w
, t
, selected
, atheme
)
579 local theme
= atheme
or theme
581 t
.label
.fg
= theme
.selected_tablabel_fg
or theme
.tablabel_fg
or theme
.fg
582 t
.ebox
.bg
= theme
.selected_tablabel_bg
or theme
.tablabel_bg
or theme
.bg
584 t
.label
.fg
= theme
.tablabel_fg
or theme
.fg
585 t
.ebox
.bg
= theme
.tablabel_bg
or theme
.bg
589 apply_window_theme
= function (w
, atheme
)
590 local theme
= atheme
or theme
591 local s
, i
, t
= w
.sbar
, w
.ibar
, w
.tbar
592 local fg
, bg
, font
= theme
.fg
, theme
.bg
, theme
.font
596 [s
.l
.uri
] = theme
.uri_fg
or theme
.statusbar_fg
or fg
,
597 [s
.l
.loaded
] = theme
.loaded_fg
or theme
.statusbar_fg
or fg
,
598 [s
.r
.buf
] = theme
.buf_fg
or theme
.statusbar_fg
or fg
,
599 [s
.r
.tabi
] = theme
.tabi_fg
or theme
.statusbar_fg
or fg
,
600 [s
.r
.scroll
] = theme
.scroll_fg
or theme
.statusbar_fg
or fg
,
601 [i
.prompt
] = theme
.prompt_fg
or theme
.inputbar_fg
or fg
,
602 [i
.input
] = theme
.input_fg
or theme
.inputbar_fg
or fg
,
607 [s
.l
.ebox
] = theme
.statusbar_bg
or bg
,
608 [s
.r
.ebox
] = theme
.statusbar_bg
or bg
,
609 [s
.ebox
] = theme
.statusbar_bg
or bg
,
610 [i
.ebox
] = theme
.inputbar_bg
or bg
,
611 [i
.input
] = theme
.input_bg
or theme
.inputbar_bg
or bg
,
616 [s
.l
.uri
] = theme
.uri_font
or theme
.statusbar_font
or font
,
617 [s
.l
.loaded
] = theme
.loaded_font
or theme
.statusbar_font
or font
,
618 [s
.r
.buf
] = theme
.buf_font
or theme
.statusbar_font
or font
,
619 [s
.r
.tabi
] = theme
.tabi_font
or theme
.statusbar_font
or font
,
620 [s
.r
.scroll
] = theme
.scroll_font
or theme
.statusbar_font
or font
,
621 [i
.prompt
] = theme
.prompt_font
or theme
.inputbar_font
or font
,
622 [i
.input
] = theme
.input_font
or theme
.inputbar_font
or font
,
623 }) do wi
.font
= v
end
628 function new_window(uris
)
629 local w
= build_window()
631 -- Pack the window table full of the common helper functions
632 for k
, v
in pairs(window_helpers
) do w
[k
] = v
end
634 attach_window_signals(w
)
636 -- Apply window theme
637 w
:apply_window_theme()
639 -- Populate notebook with tabs
640 for _
, uri
in ipairs(uris
or {}) do
644 -- Make sure something is loaded
645 if w
.tabs
:count() == 0 then