From fc24ecca542a10deefc789342f373e8d6a4a43cb Mon Sep 17 00:00:00 2001 From: Silvio Rhatto Date: Thu, 21 Sep 2017 21:39:54 -0300 Subject: Stable config --- config.dot/luakit.link/window.lua | 789 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 config.dot/luakit.link/window.lua (limited to 'config.dot/luakit.link/window.lua') diff --git a/config.dot/luakit.link/window.lua b/config.dot/luakit.link/window.lua new file mode 100644 index 0000000..ec50c12 --- /dev/null +++ b/config.dot/luakit.link/window.lua @@ -0,0 +1,789 @@ +------------------ +-- Window class -- +------------------ + +require "lfs" +local lousy = require("lousy") +local globals = require("globals") +local search_engines = globals.search_engines +local theme = lousy.theme.get() + +-- Window class table +local window = {} + +lousy.signal.setup(window, true) + +-- 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 +local function overlay() return widget{type="overlay"} end + +-- Build and pack window widgets +function window.build(w) + -- Create a table for widgets and state variables for a window + local ww = { + win = widget{type="window"}, + ebox = eventbox(), + layout = vbox(), + tabs = notebook(), + -- Status bar widgets + sbar = { + layout = hbox(), + ebox = eventbox(), + -- Left aligned widgets + l = { + layout = hbox(), + ebox = eventbox(), + }, + -- Fills space between the left and right aligned widgets + sep = eventbox(), + -- Right aligned widgets + r = { + layout = hbox(), + ebox = eventbox(), + }, + }, + + -- Vertical menu window widget (completion results, bookmarks, qmarks, ..) + menu = lousy.widget.menu(), + menu_tabs = overlay(), + + -- Input bar widgets + ibar = { + layout = hbox(), + ebox = eventbox(), + prompt = label(), + input = entry(), + prompt_text = "", + input_text = "", + }, + bar_layout = vbox(), + } + + -- Replace values in w + for k, v in pairs(ww) do w[k] = v end + + -- Tablist widget + w.tablist = lousy.widget.tablist(w.tabs, "horizontal") + + w.ebox.child = w.layout + w.layout:pack(w.tablist.widget) + w.menu_tabs.child = w.tabs + + w.win.child = w.ebox + w.layout:pack(w.menu_tabs, { expand = true, fill = true }) + + -- Pack left-aligned statusbar elements + local l = w.sbar.l + l.layout.homogeneous = false; + l.ebox.child = l.layout + + -- Pack right-aligned statusbar elements + local r = w.sbar.r + r.layout.homogeneous = false; + r.ebox.child = r.layout + + -- Pack status bar elements + local s = w.sbar + s.layout.homogeneous = false; + 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.bar_layout:pack(s.ebox) + + -- Pack menu widget + w.menu_tabs:pack(w.menu.widget, { halign = "fill", valign = "end" }) + w.menu:hide() + + -- Pack input bar + local i = w.ibar + i.layout.homogeneous = false; + i.layout:pack(i.prompt) + i.layout:pack(i.input, { expand = true, fill = true }) + i.ebox.child = i.layout + w.bar_layout:pack(i.ebox) + i.input.css = "border: 0;" + i.layout.css = "transition: 0.0s ease-in-out;" + i.input.css = "transition: 0.0s ease-in-out;" + + w.layout:pack(w.bar_layout) + + -- Other settings + i.input.show_frame = false + w.tabs.show_tabs = false + w.sbar.layout.margin_left = 3 + w.sbar.layout.margin_right = 3 + + -- Allow error messages to be copied + -- TODO: *only* allow copying when showing an error + w.ibar.prompt.selectable = true + + -- Allows indexing of window struct by window widget + window.bywidget[w.win] = w +end + +-- Table of functions to call on window creation. Normally used to add signal +-- handlers to the new windows widgets. +local init_funcs = { + -- Attach notebook widget signals + notebook_signals = function (w) + w.tabs:add_signal("switch-page", function () + w:set_mode() + w.view = nil + -- Update widgets after tab switch + luakit.idle_add(function () + -- Cancel if window already destroyed + if not w.win then return end + w.view:emit_signal("switched-page") + w:update_win_title() + end) + 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, _, 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({ + [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({ + [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 + msg.warn("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, + + hide_ui_on_fullscreen = function (w) + w.win:add_signal("property::fullscreen", function (win) + w.sbar.layout.visible = not win.fullscreen + w:update_sbar_visibility() + w.tablist.visible = not win.fullscreen + end) + end, + + check_before_closing_last_window = function (w) + w.win:add_signal("can-close", function () + return w:close_win() == nil and true or 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) + 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) + local get_mode = require("modes").get_mode + 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) + cmd = cmd:find("^%:") and cmd or (":" .. cmd) + w:enter_cmd(cmd, opts) + -- Don't append to the mode's history + local mode, hist = w.mode, w.mode.history + w.mode.history = nil + w:activate() + mode.history = hist + 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, + + update_sbar_visibility = function (w) + if w.ibar.prompt_text or w.ibar.input_text then + w.ibar.ebox:show() + w.sbar.ebox:hide() + else + w.ibar.ebox:hide() + if not w.win.fullscreen then + w.sbar.ebox:show() + end + end + end, + + -- Set and display the prompt + set_prompt = function (w, text, opts) + local input, prompt, layout = w.ibar.input, w.ibar.prompt, w.ibar.layout + opts = opts or {} + prompt:hide() + -- Set theme + local fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg + if input.fg ~= fg then input.fg = fg end + if prompt.fg ~= fg then prompt.fg = fg end + if layout.bg ~= bg then layout.bg = bg end + -- Set text or remain hidden + if text then + prompt.text = lousy.util.escape(text) + prompt:show() + end + w.ibar.prompt_text = text + w:update_sbar_visibility() + end, + + -- Set display and focus the input bar + set_input = function (w, text, opts) + local input = w.ibar.input + opts = opts or {} + input:hide() + -- Set theme + local 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 = text + input:show() + input:focus() + input.position = opts.pos or -1 + end + w.ibar.input_text = text + w:update_sbar_visibility() + end, + + set_ibar_theme = function (w, name) + name = name or "ok" + local th = theme[name] + w.ibar.input.fg = th.fg + w.ibar.prompt.fg = th.fg + w.ibar.layout.bg = th.bg + 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_buf = function () end, + + update_binds = function (w, mode) + -- Generate the list of active key & buffer binds for this mode + local get_mode = require("modes").get_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, + + new_tab = function (w, arg, opts) + opts = opts or {} + assert(type(opts) == "table") + local switch, order = opts.switch, opts.order + + -- Bit of a hack + local webview = require("webview") + + local view + if type(arg) == "widget" and arg.type == "webview" then + view = arg + local ww = webview.window(view) + ww:detach_tab(view) + else + -- Make new webview widget + view = webview.new({ private = opts.private }) + end + + w:attach_tab(view, switch, order) + if arg and not (type(arg) == "widget" and arg.type == "webview") then + webview.set_location(view, arg) + end + + return view + end, + + -- close the current tab + close_tab = function (w, view, blank_last) + view = view or w.view + w:emit_signal("close-tab", view) + w:detach_tab(view, blank_last) + view:destroy() + end, + + attach_tab = function (w, view, switch, order) + local taborder = package.loaded.taborder + -- Get tab order function + if not order and taborder then + order = (switch == false and taborder.default_bg) + or taborder.default + end + local pos = w.tabs:insert((order and order(w, view)) or -1, view) + if switch ~= false then w.tabs:switch(pos) end + end, + + detach_tab = function (w, view, blank_last) + view = view or w.view + view.parent:remove(view) + -- Treat a blank last tab as an empty notebook (if blank_last=true) + if blank_last ~= false and w.tabs:count() == 0 then + w:new_tab("luakit://newtab/", false) + end + end, + + can_quit = function (w) + -- Ask plugins if it's OK to close last window + local emsg = luakit.emit_signal("can-close") + 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 + else + return true + end + end, + + close_win = function (w, force) + if not force and (#luakit.windows == 1) and not w:can_quit() then + return false + 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 _, 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, arg, view) + view = view or w.view + if not view then return w:new_tab(arg) end + require("webview").set_location(view, arg) + end, + + -- Save, restart luakit and reload session. + restart = function (w, force) + if not force and not w:can_quit() then + return false + end + + -- Generate luakit launch command. + local args = {({string.gsub(luakit.execpath, " ", "\\ ")})[1]} + for _, arg in ipairs(luakit.options) do + table.insert(args, arg) + end + + -- Get new config path + local conf = assert(luakit.confpath) + + -- Check config has valid syntax + local cmd = table.concat(args, " ") + if luakit.spawn_sync(cmd .. " -k -c " .. conf) ~= 0 then + return w:error("Cannot restart, syntax error in configuration file: "..conf) + end + + -- Save session. + require("session").save() + + -- 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 (_, 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 "luakit://newtab/" end + + -- Strip whitespace and split by whitespace into args table + local args = lstring.split(lstring.strip(arg)) + + -- Guess if single argument is an address, file, etc + if #args == 1 and not search_engines[args[1]] then + local uri = args[1] + if uri == "about:blank" then return 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 + local e = search_engines[engine] or "%s" + + -- URI encode search terms + local terms = table.concat(args, " ") + return type(e) == "string" and string.format(e, luakit.uri_encode(terms)) or e(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 (_, k) return window.methods[k] end +} + +window.add_signal("build", window.build) + +-- Create new window +function window.new(args) + local w = {} + window.emit_signal("build", w) + + -- Set window metatable + setmetatable(w, { + __index = function (_, k) + -- Call each window index function + for _, index in ipairs(window.indexes) do + local 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(init_funcs) do + func(w) + end + window.emit_signal("init", w) + + -- Populate notebook with tabs + for _, arg in ipairs(args or {}) do + if type(arg) == "string" then + arg = w:search_open(arg) + end + w:new_tab(arg, false) + end + + -- Show window + w.win:show() + + -- Set initial mode + w:set_mode() + + -- Make sure something is loaded + if w.tabs:count() == 0 then + w:new_tab(w:search_open(globals.homepage), false) + end + + return w +end + +function window.ancestor(w) + repeat + w = w.parent + until w == nil or w.type == "window" + return w and window.bywidget[w] or nil +end + +return window + +-- vim: et:sw=4:ts=8:sts=4:tw=80 -- cgit v1.2.3