diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2014-02-15 01:05:08 +0200 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2014-02-15 01:05:08 +0200 |
| commit | f0d0bcc1e4fff649db80316de4df40c1d19d89a4 (patch) | |
| tree | 3e35ee2e560ff0c07759f27117b45027d4473f3c /.config/luakit/window.lua | |
| parent | f03f2de688e6adf9a073ea2e71105675ce99da3c (diff) | |
Add initia luakit files
Diffstat (limited to '.config/luakit/window.lua')
| -rw-r--r-- | .config/luakit/window.lua | 859 |
1 files changed, 859 insertions, 0 deletions
diff --git a/.config/luakit/window.lua b/.config/luakit/window.lua new file mode 100644 index 0000000..5e921d6 --- /dev/null +++ b/.config/luakit/window.lua @@ -0,0 +1,859 @@ +------------------ +-- Window class -- +------------------ + +require "lfs" + +-- Window class table +window = {} + +-- List of active windows by window widget +window.bywidget = setmetatable({}, { __mode = "k" }) + +-- Widget construction aliases +local function entry() return widget{type="entry"} end +local function eventbox() return widget{type="eventbox"} end +local function hbox() return widget{type="hbox"} end +local function label() return widget{type="label"} end +local function notebook() return widget{type="notebook"} end +local function vbox() return widget{type="vbox"} end + +-- Build and pack window widgets +function window.build() + -- Create a table for widgets and state variables for a window + local w = { + win = widget{type="window"}, + ebox = eventbox(), + layout = vbox(), + paned = widget{type="vpaned"}, + tabs = notebook(), + -- Tablist widget + tablist = lousy.widget.tablist(), + -- Status bar widgets + sbar = { + layout = hbox(), + ebox = eventbox(), + -- Left aligned widgets + l = { + layout = hbox(), + ebox = eventbox(), + uri = label(), + hist = label(), + loaded = label(), + }, + -- Fills space between the left and right aligned widgets + sep = eventbox(), + -- Right aligned widgets + r = { + layout = hbox(), + ebox = eventbox(), + buf = label(), + ssl = label(), + tabi = label(), + scroll = label(), + }, + }, + + -- Vertical menu window widget (completion results, bookmarks, qmarks, ..) + menu = lousy.widget.menu(), + + -- Input bar widgets + ibar = { + layout = hbox(), + ebox = eventbox(), + prompt = label(), + input = entry(), + }, + closed_tabs = {} + } + + -- Assemble window + w.ebox.child = w.paned + w.paned:pack1(w.layout) + w.win.child = w.ebox + + -- Pack tablist + w.layout:pack(w.tablist.widget) + + -- Pack notebook + w.layout:pack(w.tabs, { expand = true, fill = true }) + + -- Pack left-aligned statusbar elements + local l = w.sbar.l + l.layout:pack(l.uri) + l.layout:pack(l.hist) + l.layout:pack(l.loaded) + l.ebox.child = l.layout + + -- Pack right-aligned statusbar elements + local r = w.sbar.r + r.layout:pack(r.buf) + r.layout:pack(r.ssl) + r.layout:pack(r.tabi) + r.layout:pack(r.scroll) + r.ebox.child = r.layout + + -- Pack status bar elements + local s = w.sbar + s.layout:pack(l.ebox) + s.layout:pack(s.sep, { expand = true, fill = true }) + s.layout:pack(r.ebox) + s.ebox.child = s.layout + w.layout:pack(s.ebox) + + -- Pack menu widget + w.layout:pack(w.menu.widget) + w.menu:hide() + + -- Pack input bar + local i = w.ibar + i.layout:pack(i.prompt) + i.layout:pack(i.input, { expand = true, fill = true }) + i.ebox.child = i.layout + w.layout:pack(i.ebox) + + -- Other settings + i.input.show_frame = false + w.tabs.show_tabs = false + l.loaded:hide() + l.hist:hide() + l.uri.selectable = true + r.ssl:hide() + + -- Allows indexing of window struct by window widget + window.bywidget[w.win] = w + + return w +end + +-- Table of functions to call on window creation. Normally used to add signal +-- handlers to the new windows widgets. +window.init_funcs = { + -- Attach notebook widget signals + notebook_signals = function (w) + w.tabs:add_signal("page-added", function (nbook, view, idx) + luakit.idle_add(function () + w:update_tab_count() + w:update_tablist() + return false + end) + end) + w.tabs:add_signal("switch-page", function (nbook, view, idx) + w.view = nil + w:set_mode() + -- Update widgets after tab switch + luakit.idle_add(function () + w:update_tab_count() + w:update_win_title() + w:update_uri() + w:update_progress() + w:update_tablist() + w:update_buf() + w:update_ssl() + w:update_hist() + return false + end) + end) + w.tabs:add_signal("page-reordered", function (nbook, view, idx) + w:update_tab_count() + w:update_tablist() + end) + end, + + last_win_check = function (w) + w.win:add_signal("destroy", function () + -- call the quit function if this was the last window left + if #luakit.windows == 0 then luakit.quit() end + if w.close_win then w:close_win() end + end) + end, + + key_press_match = function (w) + w.win:add_signal("key-press", function (_, mods, key) + -- Match & exec a bind + local success, match = xpcall( + function () return w:hit(mods, key) end, + function (err) w:error(debug.traceback(err, 3)) end) + + if success and match then + return true + end + end) + end, + + tablist_tab_click = function (w) + w.tablist:add_signal("tab-clicked", function (_, index, mods, button) + if button == 1 then + w.tabs:switch(index) + return true + elseif button == 2 then + w:close_tab(w.tabs[index]) + return true + end + end) + end, + + apply_window_theme = function (w) + local s, i = w.sbar, w.ibar + + -- Set foregrounds + for wi, v in pairs({ + [s.l.uri] = theme.uri_sbar_fg, + [s.l.hist] = theme.hist_sbar_fg, + [s.l.loaded] = theme.sbar_loaded_fg, + [s.r.buf] = theme.buf_sbar_fg, + [s.r.tabi] = theme.tabi_sbar_fg, + [s.r.scroll] = theme.scroll_sbar_fg, + [i.prompt] = theme.prompt_ibar_fg, + [i.input] = theme.input_ibar_fg, + }) do wi.fg = v end + + -- Set backgrounds + for wi, v in pairs({ + [s.l.ebox] = theme.sbar_bg, + [s.r.ebox] = theme.sbar_bg, + [s.sep] = theme.sbar_bg, + [s.ebox] = theme.sbar_bg, + [i.ebox] = theme.ibar_bg, + [i.input] = theme.input_ibar_bg, + }) do wi.bg = v end + + -- Set fonts + for wi, v in pairs({ + [s.l.uri] = theme.uri_sbar_font, + [s.l.hist] = theme.hist_sbar_font, + [s.l.loaded] = theme.sbar_loaded_font, + [s.r.buf] = theme.buf_sbar_font, + [s.r.ssl] = theme.ssl_sbar_font, + [s.r.tabi] = theme.tabi_sbar_font, + [s.r.scroll] = theme.scroll_sbar_font, + [i.prompt] = theme.prompt_ibar_font, + [i.input] = theme.input_ibar_font, + }) do wi.font = v end + end, + + set_default_size = function (w) + local size = globals.default_window_size or "800x600" + if string.match(size, "^%d+x%d+$") then + w.win:set_default_size(string.match(size, "^(%d+)x(%d+)$")) + else + warn("E: window.lua: invalid window size: %q", size) + end + end, + + set_window_icon = function (w) + local path = (luakit.dev_paths and os.exists("./extras/luakit.png")) or + os.exists("/usr/share/pixmaps/luakit.png") + if path then w.win.icon = path end + end, + + clear_urgency_hint = function (w) + w.win:add_signal("focus", function () + w.win.urgency_hint = false + end) + end, +} + +-- Helper functions which operate on the window widgets or structure. +window.methods = { + -- Wrapper around the bind plugin's hit method + hit = function (w, mods, key, opts) + local opts = lousy.util.table.join(opts or {}, { + enable_buffer = w:is_mode("normal"), + buffer = w.buffer, + }) + + local caught, newbuf = lousy.bind.hit(w, w.binds, mods, key, opts) + if w.win then -- Check binding didn't cause window to exit + w.buffer = newbuf + w:update_buf() + end + return caught + end, + + -- Wrapper around the bind plugin's match_cmd method + match_cmd = function (w, buffer) + return lousy.bind.match_cmd(w, get_mode("command").binds, buffer) + end, + + -- enter command or characters into command line + enter_cmd = function (w, cmd, opts) + w:set_mode("command") + w:set_input(cmd, opts) + end, + + -- run command as if typed into the command line + run_cmd = function (w, cmd, opts) + w:enter_cmd(cmd, opts) + w:activate() + end, + + -- insert a string into the command line at the current cursor position + insert_cmd = function (w, str) + if not str then return end + local i = w.ibar.input + local text = i.text + local pos = i.position + local left, right = string.sub(text, 1, pos), string.sub(text, pos+1) + i.text = left .. str .. right + i.position = pos + #str + end, + + -- Emulates pressing the Return key in input field + activate = function (w) + w.ibar.input:emit_signal("activate") + end, + + del_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 and pos > 1 then + local left, right = string.sub(text, 2, pos), string.sub(text, pos+1) + if not string.find(left, "%s") then + left = "" + elseif string.find(left, "%w+%s*$") then + left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1) + elseif string.find(left, "%W+%s*$") then + left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1) + end + i.text = string.sub(text, 1, 1) .. left .. right + i.position = #left + 1 + end + end, + + del_line = function (w) + local i = w.ibar.input + if not string.match(i.text, "^[:/?]$") then + i.text = string.sub(i.text, 1, 1) + i.position = -1 + end + end, + + del_backward_char = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + + if pos > 1 then + i.text = string.sub(text, 0, pos - 1) .. string.sub(text, pos + 1) + i.position = pos - 1 + end + end, + + del_forward_char = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + + i.text = string.sub(text, 0, pos) .. string.sub(text, pos + 2) + i.position = pos + end, + + beg_line = function (w) + local i = w.ibar.input + i.position = 1 + end, + + end_line = function (w) + local i = w.ibar.input + i.position = -1 + end, + + forward_char = function (w) + local i = w.ibar.input + i.position = i.position + 1 + end, + + backward_char = function (w) + local i = w.ibar.input + local pos = i.position + if pos > 1 then + i.position = pos - 1 + end + end, + + forward_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 then + local right = string.sub(text, pos+1) + if string.find(right, "%w+") then + local _, move = string.find(right, "%w+") + i.position = pos + move + end + end + end, + + backward_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 and pos > 1 then + local left = string.reverse(string.sub(text, 2, pos)) + if string.find(left, "%w+") then + local _, move = string.find(left, "%w+") + i.position = pos - move + end + end + end, + + -- Shows a notification until the next keypress of the user. + notify = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt(msg, { fg = theme.notif_fg, bg = theme.notif_bg }) + end, + + warning = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt(msg, { fg = theme.warning_fg, bg = theme.warning_bg }) + end, + + error = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt("Error: "..msg, { fg = theme.error_fg, bg = theme.error_bg }) + end, + + -- Set and display the prompt + set_prompt = function (w, text, opts) + local prompt, ebox, opts = w.ibar.prompt, w.ibar.ebox, opts or {} + prompt:hide() + -- Set theme + fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg + if prompt.fg ~= fg then prompt.fg = fg end + if ebox.bg ~= bg then ebox.bg = bg end + -- Set text or remain hidden + if text then + prompt.text = lousy.util.escape(text) + prompt:show() + end + end, + + -- Set display and focus the input bar + set_input = function (w, text, opts) + local input, opts = w.ibar.input, opts or {} + input:hide() + -- Set theme + fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg + if input.fg ~= fg then input.fg = fg end + if input.bg ~= bg then input.bg = bg end + -- Set text or remain hidden + if text then + input.text = "" + input:show() + input:focus() + input.text = text + input.position = opts.pos or -1 + end + end, + + -- GUI content update functions + update_tab_count = function (w) + w.sbar.r.tabi.text = string.format("[%d/%d]", w.tabs:current(), w.tabs:count()) + end, + + update_win_title = function (w) + local uri, title = w.view.uri, w.view.title + title = (title or "luakit") .. ((uri and " - " .. uri) or "") + local max = globals.max_title_len or 80 + if #title > max then title = string.sub(title, 1, max) .. "..." end + w.win.title = title + end, + + update_uri = function (w, link) + w.sbar.l.uri.text = lousy.util.escape((link and "Link: " .. link) + or (w.view and w.view.uri) or "about:blank") + end, + + update_progress = function (w) + local p = w.view.progress + local loaded = w.sbar.l.loaded + if not w.view:loading() or p == 1 then + loaded:hide() + else + loaded:show() + loaded.text = string.format("(%d%%)", p * 100) + end + end, + + update_scroll = function (w) + local scroll, label = w.view.scroll, w.sbar.r.scroll + local y, max, text = scroll.y, scroll.ymax + if max == 0 then text = "All" + elseif y == 0 then text = "Top" + elseif y == max then text = "Bot" + else text = string.format("%2d%%", (y / max) * 100) + end + if label.text ~= text then label.text = text end + end, + + update_ssl = function (w) + local trusted = w.view:ssl_trusted() + local ssl = w.sbar.r.ssl + if trusted ~= nil and not w.checking_ssl then + ssl.fg = theme.notrust_fg + ssl.text = "(nocheck)" + ssl:show() + elseif trusted == true then + ssl.fg = theme.trust_fg + ssl.text = "(trust)" + ssl:show() + elseif trusted == false then + ssl.fg = theme.notrust_fg + ssl.text = "(notrust)" + ssl:show() + else + ssl:hide() + end + end, + + update_hist = function (w) + local hist = w.sbar.l.hist + local back, forward = w.view:can_go_back(), w.view:can_go_forward() + local s = (back and "+" or "") .. (forward and "-" or "") + if s ~= "" then + hist.text = '['..s..']' + hist:show() + else + hist:hide() + end + end, + + update_buf = function (w) + local buf = w.sbar.r.buf + if w.buffer then + buf.text = lousy.util.escape(string.format(" %-3s", w.buffer)) + buf:show() + else + buf:hide() + end + end, + + update_binds = function (w, mode) + -- Generate the list of active key & buffer binds for this mode + w.binds = lousy.util.table.join((get_mode(mode) or {}).binds or {}, get_mode('all').binds or {}) + -- Clear & hide buffer + w.buffer = nil + w:update_buf() + end, + + update_tablist = function (w) + local current = w.tabs:current() + local fg, bg, nfg, snfg = theme.tab_fg, theme.tab_bg, theme.tab_ntheme, theme.selected_ntheme + local lfg, bfg, gfg = theme.tab_loading_fg, theme.tab_notrust_fg, theme.tab_trust_fg + local escape = lousy.util.escape + local tabs, tfmt = {}, ' <span foreground="%s">%s</span> %s' + + for i, view in ipairs(w.tabs.children) do + -- Get tab number theme + local ntheme = nfg + if view:loading() then -- Show loading on all tabs + ntheme = lfg + elseif current == i then -- Show ssl trusted/untrusted on current tab + local trusted = view:ssl_trusted() + ntheme = snfg + if trusted == false or (trusted ~= nil and not w.checking_ssl) then + ntheme = bfg + elseif trusted then + ntheme = gfg + end + end + local title = view.title or view.uri or "(Untitled)" + tabs[i] = { + title = string.format(tfmt, ntheme or fg, i, escape(title)), + fg = (current == i and theme.tab_selected_fg) or fg, + bg = (current == i and theme.tab_selected_bg) or bg, + } + end + + if #tabs < 2 then tabs, current = {}, 0 end + w.tablist:update(tabs, current) + end, + + new_tab = function (w, arg, switch, order) + local view + -- Use blank tab first + if w.has_blank and w.tabs:count() == 1 and w.tabs[1].uri == "about:blank" then + view = w.tabs[1] + end + w.has_blank = nil + -- Make new webview widget + if not view then + view = webview.new(w) + -- Get tab order function + if not order and taborder then + order = (switch == false and taborder.default_bg) + or taborder.default + end + pos = w.tabs:insert((order and order(w, view)) or -1, view) + if switch ~= false then w.tabs:switch(pos) end + end + -- Load uri or webview history table + if type(arg) == "string" then view.uri = arg + elseif type(arg) == "table" then view.history = arg end + -- Update statusbar widgets + w:update_tab_count() + w:update_tablist() + return view + end, + + -- close the current tab + close_tab = function (w, view, blank_last) + view = view or w.view + -- Treat a blank last tab as an empty notebook (if blank_last=true) + if blank_last ~= false and w.tabs:count() == 1 then + if not view:loading() and view.uri == "about:blank" then return end + w:new_tab("about:blank", false) + w.has_blank = true + end + -- Save tab history + local tab = {hist = view.history,} + -- And relative location + local index = w.tabs:indexof(view) + if index ~= 1 then tab.after = w.tabs[index-1] end + table.insert(w.closed_tabs, tab) + view:destroy() + w:update_tab_count() + w:update_tablist() + end, + + close_win = function (w, force) + -- Ask plugins if it's OK to close last window + if not force and (#luakit.windows == 1) then + local emsg = luakit.emit_signal("can-close", w) + if emsg then + assert(type(emsg) == "string", "invalid exit error message") + w:error(string.format("Can't close luakit: %s (force close " + .. "with :q! or :wq!)", emsg)) + return false + end + end + + w:emit_signal("close") + + -- Close all tabs + while w.tabs:count() ~= 0 do + w:close_tab(nil, false) + end + + -- Destroy tablist + w.tablist:destroy() + + -- Remove from window index + window.bywidget[w.win] = nil + + -- Clear window struct + w = setmetatable(w, {}) + + -- Recursively remove widgets from window + local children = lousy.util.recursive_remove(w.win) + -- Destroy all widgets + for i, c in ipairs(lousy.util.table.join(children, {w.win})) do + if c.hide then c:hide() end + c:destroy() + end + + -- Remove all window table vars + for k, _ in pairs(w) do w[k] = nil end + + -- Quit if closed last window + if #luakit.windows == 0 then luakit.quit() end + end, + + -- Navigate current view or open new tab + navigate = function (w, uri, view) + view = view or w.view + if view then + local js = string.match(uri, "^javascript:(.+)$") + if js then + return view:eval_js(luakit.uri_decode(js)) + end + view.uri = uri + else + return w:new_tab(uri) + end + end, + + -- Save, restart luakit and reload session. + restart = function (w) + -- Generate luakit launch command. + local args = {({string.gsub(luakit.execpath, " ", "\\ ")})[1]} + if luakit.verbose then table.insert(args, "-v") end + -- Relaunch without libunique bindings? + if luakit.nounique then table.insert(args, "-U") end + + -- Get new config path + local conf + if luakit.confpath ~= "/etc/xdg/luakit/rc.lua" and os.exists(luakit.confpath) then + conf = luakit.confpath + table.insert(args, string.format("-c %q", conf)) + end + + -- Check config has valid syntax + local cmd = table.concat(args, " ") + if luakit.spawn_sync(cmd .. " -k") ~= 0 then + return w:error("Cannot restart, syntax error in configuration file"..((conf and ": "..conf) or ".")) + end + + -- Save session. + local wins = {} + for _, w in pairs(window.bywidget) do table.insert(wins, w) end + session.save(wins) + + -- Replace current process with new luakit instance. + luakit.exec(cmd) + end, + + -- Intelligent open command which can detect a uri or search argument. + search_open = function (w, arg) + local lstring = lousy.util.string + local match, find = string.match, string.find + + -- Detect blank uris + if not arg or match(arg, "^%s*$") then return "about:blank" end + + -- Strip whitespace and split by whitespace into args table + local args = lstring.split(lstring.strip(arg)) + + -- Guess if first argument is an address, search engine, file + if #args == 1 then + local uri = args[1] + if uri == "about:blank" then return uri end + + -- Check if search engine name + if search_engines[uri] then + return string.format(search_engines[uri], "") + end + + -- Navigate if . or / in uri (I.e. domains, IP's, scheme://) + if find(uri, "%.") or find(uri, "/") then return uri end + + -- Navigate if this is a javascript-uri + if find(uri, "^javascript:") then return uri end + + -- Valid hostnames to check + local hosts = { "localhost" } + if globals.load_etc_hosts ~= false then + hosts = lousy.util.get_etc_hosts() + end + + -- Check hostnames + for _, h in pairs(hosts) do + if h == uri or match(uri, "^"..h..":%d+$") then return uri end + end + + -- Check for file in filesystem + if globals.check_filepath ~= false then + if lfs.attributes(uri) then return "file://" .. uri end + end + end + + -- Find search engine (or use search_engines.default) + local engine = "default" + if args[1] and search_engines[args[1]] then + engine = args[1] + table.remove(args, 1) + end + + -- URI encode search terms + local terms = luakit.uri_encode(table.concat(args, " ")) + return string.format(search_engines[engine], terms) + end, + + -- Increase (or decrease) the last found number in the current uri + inc_uri = function (w, arg) + local uri = string.gsub(w.view.uri, "(%d+)([^0-9]*)$", function (num, rest) + return string.format("%0"..#num.."d", tonumber(num) + (arg or 1)) .. rest + end) + return uri + end, + + -- Tab traversing functions + next_tab = function (w, n) + w.tabs:switch((((n or 1) + w.tabs:current() -1) % w.tabs:count()) + 1) + end, + + prev_tab = function (w, n) + w.tabs:switch(((w.tabs:current() - (n or 1) -1) % w.tabs:count()) + 1) + end, + + goto_tab = function (w, n) + if n and (n == -1 or n > 0) then + return w.tabs:switch((n <= w.tabs:count() and n) or -1) + end + end, + + -- For each tab, switches to that tab and calls the given function passing + -- it the view contained in the tab. + each_tab = function (w, fn) + for index = 1, w.tabs:count() do + w:goto_tab(index) + fn(w.tabs[index]) + end + end, + + -- If argument is form-active or root-active, emits signal. Ignores all + -- other signals. + emit_form_root_active_signal = function (w, s) + if s == "form-active" then + w.view:emit_signal("form-active") + elseif s == "root-active" then + w.view:emit_signal("root-active") + end + end, +} + +-- Ordered list of class index functions. Other classes (E.g. webview) are able +-- to add their own index functions to this list. +window.indexes = { + -- Find function in window.methods first + function (w, k) return window.methods[k] end +} + +-- Create new window +function window.new(uris) + local w = window.build() + + -- Set window metatable + setmetatable(w, { + __index = function (_, k) + -- Check widget structure first + local v = rawget(w, k) + if v then return v end + -- Call each window index function + for _, index in ipairs(window.indexes) do + v = index(w, k) + if v then return v end + end + end, + }) + + -- Setup window widget for signals + lousy.signal.setup(w) + + -- Call window init functions + for _, func in pairs(window.init_funcs) do + func(w) + end + + -- Populate notebook with tabs + for _, uri in ipairs(uris or {}) do + w:new_tab(w:search_open(uri), false) + end + + -- Make sure something is loaded + if w.tabs:count() == 0 then + w:new_tab(w:search_open(globals.homepage), false) + end + + -- Set initial mode + w:set_mode() + + -- Show window + w.win:show() + + return w +end + +-- vim: et:sw=4:ts=8:sts=4:tw=80 |
