Add helper methods for evaluating javascript code
[luakit.git] / rc.lua
blob6b7ccff9336d2ce073ba0d12986c9ae17d9c37fb
1 -- Luakit configuration file, more information at http://luakit.org/
3 require("math")
4 require("mode")
5 require("bind")
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"
20 SCROLL_STEP = 20
21 MAX_CMD_HISTORY = 100
22 MAX_SRCH_HISTORY = 100
24 -- Luakit theme
25 theme = theme or {
26 -- Default settings
27 font = "monospace normal 9",
28 fg = "#fff",
29 bg = "#000",
31 -- General settings
32 statusbar_fg = "#fff",
33 statusbar_bg = "#000",
34 inputbar_fg = "#000",
35 inputbar_bg = "#fff",
37 -- Specific settings
38 loaded_fg = "#33AADD",
39 tablabel_fg = "#999",
40 tablabel_bg = "#111",
41 selected_tablabel_fg = "#fff",
42 selected_tablabel_bg = "#000",
44 -- Enforce a minimum tab width of 30 characters to prevent longer tab
45 -- titles overshadowing small tab titles when things get crowded.
46 tablabel_format = "%-30s",
49 widget.add_signal("new", function(wi)
50 wi:add_signal("init", function(wi)
51 if wi.type == "window" then
52 wi:add_signal("destroy", function ()
53 -- Call the quit function if this was the last window left
54 if #luakit.windows == 0 then luakit.quit() end
55 end)
56 end
57 end)
58 end)
60 -- Search engines
61 search_engines = {
62 google = "http://google.com/search?q={0}",
63 imdb = "http://imdb.com/find?s=all&q={0}",
64 sourceforge = "http://sf.net/search/?words={0}"
67 -- Add key bindings to be used across all windows
68 mode_binds = {
69 -- bind.buf(Pattern, function (w, buffer, opts) .. end, opts),
70 -- bind.key({Modifiers}, Key name, function (w, opts) .. end, opts),
71 all = {
72 bind.key({}, "Escape", function (w) w:set_mode() end),
73 bind.key({"Control"}, "[", function (w) w:set_mode() end),
75 normal = {
76 bind.key({}, "i", function (w) w:set_mode("insert") end),
77 bind.key({}, ":", function (w) w:set_mode("command") end),
79 -- Scrolling
80 bind.key({}, "h", function (w) w:scroll_horiz("-"..SCROLL_STEP.."px") end),
81 bind.key({}, "j", function (w) w:scroll_vert ("+"..SCROLL_STEP.."px") end),
82 bind.key({}, "k", function (w) w:scroll_vert ("-"..SCROLL_STEP.."px") end),
83 bind.key({}, "l", function (w) w:scroll_horiz("+"..SCROLL_STEP.."px") end),
84 bind.key({}, "Left", function (w) w:scroll_horiz("-"..SCROLL_STEP.."px") end),
85 bind.key({}, "Down", function (w) w:scroll_vert ("+"..SCROLL_STEP.."px") end),
86 bind.key({}, "Up", function (w) w:scroll_vert ("-"..SCROLL_STEP.."px") end),
87 bind.key({}, "Right", function (w) w:scroll_horiz("+"..SCROLL_STEP.."px") end),
88 bind.buf("^gg$", function (w) w:scroll_vert("0%") end),
89 bind.buf("^G$", function (w) w:scroll_vert("100%") end),
90 bind.buf("^[\-\+]?[0-9]+[%%G]$", function (w, b) w:scroll_vert(string.match(b, "^([\-\+]?%d+)[%%G]$") .. "%") end),
92 -- Clipboard
93 bind.key({}, "p", function (w) w:navigate(luakit.selection()) end),
94 bind.key({}, "P", function (w) w:new_tab(luakit.selection()) end),
96 -- Commands
97 bind.buf("^o$", function (w, c) w:enter_cmd(":open ") end),
98 bind.buf("^t$", function (w, c) w:enter_cmd(":tabopen ") end),
99 bind.buf("^,g$", function (w, c) w:enter_cmd(":websearch google ") end),
101 -- Searching
102 bind.key({}, "/", function (w) w:start_search(true) end),
103 bind.key({}, "?", function (w) w:start_search(false) end),
104 bind.key({}, "n", function (w) w:search(nil, true) end),
105 bind.key({}, "N", function (w) w:search(nil, false) end),
107 -- History
108 bind.buf("^[0-9]*H$", function (w, b) w:back (tonumber(string.match(b, "^(%d*)H$") or 1)) end),
109 bind.buf("^[0-9]*L$", function (w, b) w:forward(tonumber(string.match(b, "^(%d*)L$") or 1)) end),
111 -- Tab
112 bind.buf("^[0-9]*gT$", function (w, b) w:prev_tab(tonumber(string.match(b, "^(%d*)gT$") or 1)) end),
113 bind.buf("^[0-9]*gt$", function (w, b) w:next_tab(tonumber(string.match(b, "^(%d*)gt$") or 1)) end),
114 bind.buf("^gH$", function (w) w:new_tab(HOMEPAGE) end),
115 bind.buf("^d$", function (w) w:close_tab() end),
117 bind.buf("^gh$", function (w) w:navigate(HOMEPAGE) end),
118 bind.buf("^ZZ$", function (w) luakit.quit() end),
120 command = {
121 bind.key({"Shift"}, "Insert", function (w) w:insert_cmd(luakit.selection()) end),
122 bind.key({}, "Up", function (w) w:cmd_hist_prev() end),
123 bind.key({}, "Down", function (w) w:cmd_hist_next() end),
124 bind.key({}, "Tab", function (w) w:cmd_completion() end),
125 bind.key({"Control"}, "w", function (w) w:del_word() end),
126 bind.key({"Control"}, "u", function (w) w:del_line() end),
128 search = {
129 bind.key({}, "Up", function (w) w:srch_hist_prev() end),
130 bind.key({}, "Down", function (w) w:srch_hist_next() end),
132 insert = { },
135 -- Commands
136 commands = {
137 -- bind.cmd({Command, Alias1, ...}, function (w, arg, opts) .. end, opts),
138 bind.cmd({"open", "o"}, function (w, a) w:navigate(a) end),
139 bind.cmd({"tabopen", "t"}, function (w, a) w:new_tab(a) end),
140 bind.cmd({"back" }, function (w, a) w:back(tonumber(a) or 1) end),
141 bind.cmd({"forward", "f"}, function (w, a) w:forward(tonumber(a) or 1) end),
142 bind.cmd({"scroll" }, function (w, a) w:scroll_vert(a) end),
143 bind.cmd({"quit", "q"}, function (w) luakit.quit() end),
144 bind.cmd({"close", "c"}, function (w) w:close_tab() end),
145 bind.cmd({"websearch", "ws"}, function (w, e, s) w:websearch(e, s) end),
148 -- Build and pack window widgets
149 function build_window()
150 -- Create a table for widgets and state variables for a window
151 local w = {
152 win = window(),
153 ebox = eventbox(),
154 layout = vbox(),
155 tabs = notebook(),
156 -- Tab bar widgets
157 tbar = {
158 layout = hbox(),
159 ebox = eventbox(),
160 titles = { },
162 -- Status bar widgets
163 sbar = {
164 layout = hbox(),
165 ebox = eventbox(),
166 -- Left aligned widgets
167 l = {
168 layout = hbox(),
169 ebox = eventbox(),
170 uri = label(),
171 loaded = label(),
173 -- Fills space between the left and right aligned widgets
174 filler = label(),
175 -- Right aligned widgets
176 r = {
177 layout = hbox(),
178 ebox = eventbox(),
179 buf = label(),
180 tabi = label(),
181 scroll = label(),
184 -- Input bar widgets
185 ibar = {
186 layout = hbox(),
187 ebox = eventbox(),
188 prompt = label(),
189 input = entry(),
193 -- Assemble window
194 w.ebox:set_child(w.layout)
195 w.win:set_child(w.ebox)
197 -- Pack tab bar
198 local t = w.tbar
199 t.ebox:set_child(t.layout, false, false, 0)
200 w.layout:pack_start(t.ebox, false, false, 0)
202 -- Pack notebook
203 w.layout:pack_start(w.tabs, true, true, 0)
205 -- Pack left-aligned statusbar elements
206 local l = w.sbar.l
207 l.layout:pack_start(l.uri, false, false, 0)
208 l.layout:pack_start(l.loaded, false, false, 0)
209 l.ebox:set_child(l.layout)
211 -- Pack right-aligned statusbar elements
212 local r = w.sbar.r
213 r.layout:pack_start(r.buf, false, false, 0)
214 r.layout:pack_start(r.tabi, false, false, 0)
215 r.layout:pack_start(r.scroll, false, false, 0)
216 r.ebox:set_child(r.layout)
218 -- Pack status bar elements
219 local s = w.sbar
220 s.layout:pack_start(l.ebox, false, false, 0)
221 s.layout:pack_start(s.filler, true, true, 0)
222 s.layout:pack_start(r.ebox, false, false, 0)
223 s.ebox:set_child(s.layout)
224 w.layout:pack_start(s.ebox, false, false, 0)
226 -- Pack input bar
227 local i = w.ibar
228 i.layout:pack_start(i.prompt, false, false, 0)
229 i.layout:pack_start(i.input, true, true, 0)
230 i.ebox:set_child(i.layout)
231 w.layout:pack_start(i.ebox, false, false, 0)
233 -- Other settings
234 i.input.show_frame = false
235 w.tabs.show_tabs = false
236 l.loaded:hide()
237 l.uri.selectable = true
239 return w
242 function attach_window_signals(w)
243 -- Attach notebook widget signals
244 w.tabs:add_signal("page-added", function(nbook, view, idx)
245 w:update_tab_count(idx)
246 w:update_tab_labels()
247 end)
249 w.tabs:add_signal("switch-page", function(nbook, view, idx)
250 w:update_tab_count(idx)
251 w:update_win_title(view)
252 w:update_uri(view)
253 w:update_progress(view)
254 w:update_tab_labels(idx)
255 end)
257 -- Attach window widget signals
258 w.win:add_signal("key-press", function(win, mods, key)
259 -- Reset command line completion
260 if w:get_mode() == "command" and key ~= "Tab" and w.compl_start then
261 w:update_uri()
262 w.compl_index = 0
265 if w:hit(mods, key) then
266 return true
268 end)
270 w.win:add_signal("mode-changed", function(win, mode)
271 w:update_binds(mode)
272 w.cmd_hist_cursor = nil
274 -- If a user aborts a search return to the original position
275 if w.search_start_marker then
276 w:get_current():set_scroll_vert(w.search_start_marker)
277 w.search_start_marker = nil
280 if mode == "normal" then
281 w.ibar.prompt:hide()
282 w.ibar.input:hide()
283 elseif mode == "insert" then
284 w.ibar.input:hide()
285 w.ibar.input.text = ""
286 w.ibar.prompt.text = "-- INSERT --"
287 w.ibar.prompt:show()
288 elseif mode == "command" then
289 w.ibar.prompt:hide()
290 w.ibar.input.text = ":"
291 w.ibar.input:show()
292 w.ibar.input:focus()
293 w.ibar.input:set_position(-1)
294 elseif mode == "search" then
295 w.ibar.prompt:hide()
296 w.ibar.input:show()
297 else
298 w.ibar.prompt.text = ""
299 w.ibar.input.text = ""
301 end)
303 -- Attach inputbar widget signals
304 w.ibar.input:add_signal("changed", function()
305 local text = w.ibar.input.text
306 -- Auto-exit "command" mode if you backspace or delete the ":"
307 -- character at the start of the input box when in "command" mode.
308 if w:is_mode("command") and not string.match(text, "^:") then
309 w:set_mode()
310 elseif w:is_mode("search") then
311 if string.match(text, "^[\?\/]") then
312 w:search(string.sub(text, 2), (string.sub(text, 1, 1) == "/"))
313 else
314 w:clear_search()
315 w:set_mode()
318 end)
320 w.ibar.input:add_signal("activate", function()
321 local text = w.ibar.input.text
322 if w:is_mode("command") then
323 w:cmd_hist_add(text)
324 w:match_cmd(string.sub(text, 2))
325 w:set_mode()
326 elseif w:is_mode("search") then
327 w:srch_hist_add(text)
328 w:search(string.sub(text, 2), string.sub(text, 1, 1) == "/")
329 -- User doesn't want to return to start position
330 w.search_start_marker = nil
331 w:set_mode()
332 w.ibar.prompt.text = text
333 w.ibar.prompt:show()
335 end)
338 -- Attach signal handlers to a new tab's webview
339 function attach_webview_signals(w, view)
340 view:add_signal("title-changed", function (v)
341 w:update_tab_labels()
342 if w:is_current(v) then
343 w:update_win_title(v)
345 end)
347 view:add_signal("property::uri", function(v)
348 w:update_tab_labels()
349 if w:is_current(v) then
350 w:update_uri(v)
352 end)
354 view:add_signal("key-press", function ()
355 -- Only allow key press events to hit the webview if the user is in
356 -- "insert" mode.
357 if not w:is_mode("insert") then
358 return true
360 end)
362 view:add_signal("load-start", function (v)
363 if w:is_current(v) then
364 w:update_progress(v, 0)
365 w:set_mode()
367 end)
369 view:add_signal("progress-update", function (v)
370 if w:is_current(v) then
371 w:update_progress(v)
373 end)
375 view:add_signal("expose", function(v)
376 if w:is_current(v) then
377 w:update_scroll(v)
379 end)
382 -- Parses scroll amounts of the form:
383 -- Relative: "+20%", "-20%", "+20px", "-20px"
384 -- Absolute: 20, "20%", "20px"
385 -- And returns an absolute value.
386 function parse_scroll(current, max, value)
387 if string.match(value, "^%d+px$") then
388 return tonumber(string.match(value, "^(%d+)px$"))
389 elseif string.match(value, "^%d+%%$") then
390 return math.ceil(max * (tonumber(string.match(value, "^(%d+)%%$")) / 100))
391 elseif string.match(value, "^[\-\+]%d+px") then
392 return current + tonumber(string.match(value, "^([\-\+]%d+)px"))
393 elseif string.match(value, "^[\-\+]%d+%%$") then
394 return math.ceil(current + (max * (tonumber(string.match(value, "^([\-\+]%d+)%%$")) / 100)))
395 else
396 print("E: unable to parse scroll amount:", value)
400 -- Helper functions which operate on a windows widget structure
401 window_helpers = {
402 -- Return the widget in the currently active tab
403 get_current = function(w) return w.tabs:atindex(w.tabs:current()) end,
404 -- Check if given widget is the widget in the currently active tab
405 is_current = function(w, wi) return w.tabs:indexof(wi) == w.tabs:current() end,
407 -- Wrappers around the mode plugin
408 set_mode = function(w, name) mode.set(w.win, name) end,
409 get_mode = function(w) return mode.get(w.win) end,
410 is_mode = function(w, name) return name == w:get_mode() end,
411 is_any_mode = function(w, t, name) return util.table.hasitem(t, name or w:get_mode()) end,
413 -- Wrappers around the view:get_prop & view:set_prop methods
414 get = function (w, prop, view)
415 if not view then view = w:get_current() end
416 return view:get_prop(prop)
417 end,
419 set = function (w, prop, val, view)
420 if not view then view = w:get_current() end
421 view:set_prop(prop, val)
422 end,
424 get_tab_title = function (w, view)
425 if not view then view = w:get_current() end
426 return view:get_prop("title") or view.uri or "(Untitled)"
427 end,
429 navigate = function(w, uri, view)
430 local v = view or w:get_current()
431 if v then
432 v.uri = uri
433 else
434 return w:new_tab(uri)
436 end,
438 new_tab = function(w, uri)
439 local view = webview()
440 w.tabs:append(view)
441 attach_webview_signals(w, view)
442 if uri then view.uri = uri end
443 view.show_scrollbars = false
444 w:update_tab_count()
445 end,
447 -- close the current tab
448 close_tab = function(w)
449 view = w:get_current()
450 if not view then return end
451 w.tabs:remove(view)
452 view:destroy()
453 w:update_tab_count()
454 end,
456 -- evaluate javascript code and return string result
457 eval_js = function(w, script, file, view)
458 if not view then view = w:get_current() end
459 return view:eval_js(script, file or "(buffer)")
460 end,
462 -- evaluate javascript code from file and return string result
463 eval_js_from_file = function(w, file, view)
464 local fh, err = io.open(file)
465 if not fh then return error(err) end
466 local script = fh:read("*a")
467 fh:close()
468 return w:eval_js(script, file, view)
469 end,
471 -- Wrapper around the bind plugin's hit method
472 hit = function (w, mods, key)
473 local caught, newbuf = bind.hit(w.binds or {}, mods, key, w.buffer, w:is_mode("normal"), w)
474 w.buffer = newbuf
475 w:update_buf()
476 return caught
477 end,
479 -- Wrapper around the bind plugin's match_cmd method
480 match_cmd = function (w, buffer)
481 return bind.match_cmd(commands, buffer, w)
482 end,
484 -- enter command or characters into command line
485 enter_cmd = function(w, cmd)
486 local i = w.ibar.input
487 w:set_mode("command")
488 i.text = cmd
489 i:set_position(-1)
490 end,
492 -- insert a string into the command line at the current cursor position
493 insert_cmd = function(w, str)
494 if not str then return nil end
495 local i = w.ibar.input
496 local text = i.text
497 local pos = i:get_position()
498 local left, right = string.sub(text, 1, pos), string.sub(text, pos+1)
499 i.text = left .. str .. right
500 i:set_position(pos + #str + 1)
501 end,
503 -- search engine wrapper
504 websearch = function(w, args)
505 local sep = string.find(args, " ")
506 local engine = string.sub(args, 1, sep-1)
507 local search = string.sub(args, sep+1)
508 if not search_engines[engine] then
509 print("E: No matching search engine found:", engine)
510 return 0
512 local uri = string.gsub(search_engines[engine], "{%d}", search)
513 return w:navigate(uri)
514 end,
516 -- Command line completion of available commands
517 cmd_completion = function(w)
518 local i = w.ibar.input
519 local s = w.sbar.l.uri
520 local cmpl = {}
522 -- Get last completion (is reset on key press other than <Tab>)
523 if not w.compl_start or w.compl_index == 0 then
524 w.compl_start = "^" .. string.sub(i.text, 2)
525 w.compl_index = 1
528 -- Get suitable commands
529 for _, b in ipairs(commands) do
530 for _, c in pairs(b.commands) do
531 if c and string.match(c, w.compl_start) then
532 table.insert(cmpl, c)
537 table.sort(cmpl)
539 if #cmpl > 0 then
540 local text = ""
541 for index, comp in pairs(cmpl) do
542 if index == w.compl_index then
543 i.text = ":" .. comp .. " "
544 i:set_position(-1)
546 if text ~= "" then
547 text = text .. " | "
549 text = text .. comp
552 -- cycle through all possible completions
553 if w.compl_index == #cmpl then
554 w.compl_index = 1
555 else
556 w.compl_index = w.compl_index + 1
558 s.text = text
560 end,
562 del_word = function(w)
563 local i = w.ibar.input
564 local text = i.text
565 local pos = i:get_position()
566 if text and #text > 1 and pos > 1 then
567 local left, right = string.sub(text, 2, pos), string.sub(text, pos+1)
568 if not string.find(left, "%s") then
569 left = ""
570 elseif string.find(left, "%w+%s*$") then
571 left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1)
572 elseif string.find(left, "%W+%s*$") then
573 left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1)
575 i.text = string.sub(text, 1, 1) .. left .. right
576 i:set_position(#left + 2)
578 end,
580 del_line = function(w)
581 local i = w.ibar.input
582 if i.text ~= ":" then
583 i.text = ":"
584 i:set_position(-1)
586 end,
588 -- Search history adding
589 srch_hist_add = function(w, srch)
590 if not w.srch_hist then w.srch_hist = {} end
591 -- Check overflow
592 if #w.srch_hist > ((MAX_SRCH_HISTORY or 100) + 5) then
593 while #w.srch_hist > (MAX_SRCH_HISTORY or 100) do
594 table.remove(w.srch_hist, 1)
597 table.insert(w.srch_hist, srch)
598 end,
600 -- Search history traversing
601 srch_hist_prev = function(w)
602 if not w.srch_hist then w.srch_hist = {} end
603 if not w.srch_hist_cursor then
604 w.srch_hist_cursor = #w.srch_hist + 1
605 w.srch_hist_current = w.ibar.input.text
607 local c = w.srch_hist_cursor - 1
608 if w.srch_hist[c] then
609 w.srch_hist_cursor = c
610 w.ibar.input.text = w.srch_hist[c]
611 w.ibar.input:set_position(-1)
613 end,
615 srch_hist_next = function(w)
616 if not w.srch_hist then w.srch_hist = {} end
617 local c = (w.srch_hist_cursor or #w.srch_hist) + 1
618 if w.srch_hist[c] then
619 w.srch_hist_cursor = c
620 w.ibar.input.text = w.srch_hist[c]
621 w.ibar.input:set_position(-1)
622 elseif w.srch_hist_current then
623 w.srch_hist_cursor = nil
624 w.ibar.input.text = w.srch_hist_current
625 w.ibar.input:set_position(-1)
627 end,
629 -- Command history adding
630 cmd_hist_add = function(w, cmd)
631 if not w.cmd_hist then w.cmd_hist = {} end
632 -- Make sure history doesn't overflow
633 if #w.cmd_hist > ((MAX_CMD_HISTORY or 100) + 5) then
634 while #w.cmd_hist > (MAX_CMD_HISTORY or 100) do
635 table.remove(w.cmd_hist, 1)
638 table.insert(w.cmd_hist, cmd)
639 end,
641 -- Command history traversing
642 cmd_hist_prev = function(w)
643 if not w.cmd_hist then w.cmd_hist = {} end
644 if not w.cmd_hist_cursor then
645 w.cmd_hist_cursor = #w.cmd_hist + 1
646 w.cmd_hist_current = w.ibar.input.text
648 local c = w.cmd_hist_cursor - 1
649 if w.cmd_hist[c] then
650 w.cmd_hist_cursor = c
651 w.ibar.input.text = w.cmd_hist[c]
652 w.ibar.input:set_position(-1)
654 end,
656 cmd_hist_next = function(w)
657 if not w.cmd_hist then w.cmd_hist = {} end
658 local c = (w.cmd_hist_cursor or #w.cmd_hist) + 1
659 if w.cmd_hist[c] then
660 w.cmd_hist_cursor = c
661 w.ibar.input.text = w.cmd_hist[c]
662 w.ibar.input:set_position(-1)
663 elseif w.cmd_hist_current then
664 w.cmd_hist_cursor = nil
665 w.ibar.input.text = w.cmd_hist_current
666 w.ibar.input:set_position(-1)
668 end,
670 -- Searching functions
671 start_search = function(w, forward)
672 -- Clear previous search results
673 w:clear_search()
674 w:set_mode("search")
675 local i = w.ibar.input
676 if forward then
677 i.text = "/"
678 else
679 i.text = "?"
681 i:focus()
682 i:set_position(-1)
683 end,
685 search = function(w, text, forward)
686 local view = w:get_current()
687 local text = text or w.last_search
688 if forward == nil then forward = true end
689 local case_sensitive = false
690 local wrap = true
692 if not text or #text == 0 then
693 w:clear_search()
694 return nil
697 w.last_search = text
698 if w.searching_forward == nil then
699 w.searching_forward = forward
700 w.search_start_marker = view:get_scroll_vert()
701 else
702 -- Invert the direction if originally searching in reverse
703 forward = (w.searching_forward == forward)
706 view:search(text, case_sensitive, forward, wrap);
707 end,
709 clear_search = function (w)
710 w:get_current():clear_search()
711 -- Clear search state
712 w.last_search = nil
713 w.searching_forward = nil
714 w.search_start_marker = nil
715 end,
717 -- Webview scroll functions
718 scroll_vert = function(w, value, view)
719 if not view then view = w:get_current() end
720 local cur, max = view:get_scroll_vert()
721 if type(value) == "string" then
722 value = parse_scroll(cur, max, value)
724 view:set_scroll_vert(value)
725 end,
727 scroll_horiz = function(w, value)
728 if not view then view = w:get_current() end
729 local cur, max = view:get_scroll_horiz()
730 if type(value) == "string" then
731 value = parse_scroll(cur, max, value)
733 view:set_scroll_horiz(value)
734 end,
736 -- Tab traversing functions
737 next_tab = function(w, n)
738 w.tabs:switch((((n or 1) + w.tabs:current() -1) % w.tabs:count()) + 1)
739 end,
740 prev_tab = function(w, n)
741 w.tabs:switch(((w.tabs:current() - (n or 1) -1) % w.tabs:count()) + 1)
742 end,
743 goto_tab = function(w, n)
744 w.tabs:switch(n)
745 end,
747 -- History traversing functions
748 back = function(w, n, view)
749 (view or w:get_current()):go_back(n or 1)
750 end,
751 forward = function(w, n, view)
752 (view or w:get_current()):go_forward(n or 1)
753 end,
755 -- GUI content update functions
756 update_tab_count = function (w, i, t)
757 w.sbar.r.tabi.text = string.format("[%d/%d]", i or w.tabs:current(), t or w.tabs:count())
758 end,
760 update_win_title = function (w, view)
761 if not view then view = w:get_current() end
762 local title = view:get_prop("title")
763 local uri = view.uri
764 if not title and not uri then
765 w.win.title = "luakit"
766 else
767 w.win.title = (title or "luakit") .. " - " .. (uri or "about:blank")
769 end,
771 update_uri = function (w, view, uri)
772 if not view then view = w:get_current() end
773 w.sbar.l.uri.text = (uri or view.uri or "about:blank")
774 end,
776 update_progress = function (w, view, p)
777 if not view then view = w:get_current() end
778 if not p then p = view:get_prop("progress") end
779 if not view:loading() or p == 1 then
780 w.sbar.l.loaded:hide()
781 else
782 w.sbar.l.loaded:show()
783 w.sbar.l.loaded.text = string.format("(%d%%)", p * 100)
785 end,
787 update_scroll = function (w, view)
788 if not view then view = w:get_current() end
789 local val, max = view:get_scroll_vert()
790 if max == 0 then val = "All"
791 elseif val == 0 then val = "Top"
792 elseif val == max then val = "Bot"
793 else val = string.format("%2d%%", (val/max) * 100)
795 w.sbar.r.scroll.text = val
796 end,
798 update_buf = function (w)
799 if w.buffer then
800 w.sbar.r.buf.text = string.format(" %-3s", w.buffer)
801 w.sbar.r.buf:show()
802 else
803 w.sbar.r.buf:hide()
805 end,
807 update_binds = function (w, mode)
808 -- Generate the list of active key & buffer binds for this mode
809 w.binds = util.table.join(mode_binds[mode], mode_binds.all)
810 -- Clear & hide buffer
811 w.buffer = nil
812 w:update_buf()
813 end,
815 -- Tab label functions
816 make_tab_label = function (w, pos)
817 local t = {
818 label = label(),
819 sep = label(),
820 ebox = eventbox(),
821 layout = hbox(),
823 t.label.font = theme.tablabel_font or theme.font
824 t.layout:pack_start(t.label, true, true, 0)
825 t.layout:pack_start(t.sep, false, false, 0)
826 t.ebox:set_child(t.layout)
827 t.ebox:add_signal("clicked", function(e) w.tabs:switch(pos) end)
828 return t
829 end,
831 destroy_tab_label = function(w, t)
832 if not t then t = table.remove(w.tbar.titles) end
833 for _, wi in pairs(t) do
834 wi:destroy()
836 end,
838 update_tab_labels = function (w, current)
839 local tb = w.tbar
840 local count, current = w.tabs:count(), current or w.tabs:current()
841 tb.ebox:hide()
843 -- Leave the tablist hidden if there is only one tab open
844 if count <= 1 then
845 return nil
848 if count ~= #tb.titles then
849 -- Grow the number of labels
850 while count > #tb.titles do
851 local t = w:make_tab_label(#tb.titles + 1)
852 tb.layout:pack_start(t.ebox, true, true, 0)
853 table.insert(tb.titles, t)
855 -- Prune number of labels
856 while count < #tb.titles do
857 w:destroy_tab_label()
861 if count ~= 0 then
862 for i = 1, count do
863 local t = tb.titles[i]
864 local title = " " ..i.. " "..w:get_tab_title(w.tabs:atindex(i))
865 t.label.text = string.format(theme.tablabel_format or "%s", title)
866 w:apply_tablabel_theme(t, i == current)
869 tb.ebox:show()
870 end,
872 -- Theme functions
873 apply_tablabel_theme = function (w, t, selected, atheme)
874 local theme = atheme or theme
875 if selected then
876 t.label.fg = theme.selected_tablabel_fg or theme.tablabel_fg or theme.fg
877 t.ebox.bg = theme.selected_tablabel_bg or theme.tablabel_bg or theme.bg
878 else
879 t.label.fg = theme.tablabel_fg or theme.fg
880 t.ebox.bg = theme.tablabel_bg or theme.bg
882 end,
884 apply_window_theme = function (w, atheme)
885 local theme = atheme or theme
886 local s, i, t = w.sbar, w.ibar, w.tbar
887 local fg, bg, font = theme.fg, theme.bg, theme.font
889 -- Set foregrounds
890 for wi, v in pairs({
891 [s.l.uri] = theme.uri_fg or theme.statusbar_fg or fg,
892 [s.l.loaded] = theme.loaded_fg or theme.statusbar_fg or fg,
893 [s.r.buf] = theme.buf_fg or theme.statusbar_fg or fg,
894 [s.r.tabi] = theme.tabi_fg or theme.statusbar_fg or fg,
895 [s.r.scroll] = theme.scroll_fg or theme.statusbar_fg or fg,
896 [i.prompt] = theme.prompt_fg or theme.inputbar_fg or fg,
897 [i.input] = theme.input_fg or theme.inputbar_fg or fg,
898 }) do wi.fg = v end
900 -- Set backgrounds
901 for wi, v in pairs({
902 [s.l.ebox] = theme.statusbar_bg or bg,
903 [s.r.ebox] = theme.statusbar_bg or bg,
904 [s.ebox] = theme.statusbar_bg or bg,
905 [i.ebox] = theme.inputbar_bg or bg,
906 [i.input] = theme.input_bg or theme.inputbar_bg or bg,
907 }) do wi.bg = v end
909 -- Set fonts
910 for wi, v in pairs({
911 [s.l.uri] = theme.uri_font or theme.statusbar_font or font,
912 [s.l.loaded] = theme.loaded_font or theme.statusbar_font or font,
913 [s.r.buf] = theme.buf_font or theme.statusbar_font or font,
914 [s.r.tabi] = theme.tabi_font or theme.statusbar_font or font,
915 [s.r.scroll] = theme.scroll_font or theme.statusbar_font or font,
916 [i.prompt] = theme.prompt_font or theme.inputbar_font or font,
917 [i.input] = theme.input_font or theme.inputbar_font or font,
918 }) do wi.font = v end
919 end,
922 -- Create new window
923 function new_window(uris)
924 local w = build_window()
926 -- Pack the window table full of the common helper functions
927 for k, v in pairs(window_helpers) do w[k] = v end
929 attach_window_signals(w)
931 -- Apply window theme
932 w:apply_window_theme()
934 -- Populate notebook with tabs
935 for _, uri in ipairs(uris or {}) do
936 w:new_tab(uri)
939 -- Make sure something is loaded
940 if w.tabs:count() == 0 then
941 w:new_tab(HOMEPAGE)
944 -- Set initial mode
945 w:set_mode()
947 return w
950 new_window(uris)
952 -- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80