diff options
author | Silvio Rhatto <rhatto@riseup.net> | 2017-09-21 21:39:54 -0300 |
---|---|---|
committer | Silvio Rhatto <rhatto@riseup.net> | 2017-09-21 21:39:54 -0300 |
commit | fc24ecca542a10deefc789342f373e8d6a4a43cb (patch) | |
tree | f3e11c3a14e156a145778c200219c86058e9805c /config.dot | |
parent | 562adce657add8aaf2c80597fc54fa0a906c2287 (diff) | |
download | luakit-fc24ecca542a10deefc789342f373e8d6a4a43cb.tar.gz luakit-fc24ecca542a10deefc789342f373e8d6a4a43cb.tar.bz2 |
Stable config
Diffstat (limited to 'config.dot')
-rw-r--r-- | config.dot/luakit.link/globals.lua | 68 | ||||
-rw-r--r-- | config.dot/luakit.link/rc.lua | 68 | ||||
-rw-r--r-- | config.dot/luakit.link/userprefs.lua | 19 | ||||
-rw-r--r-- | config.dot/luakit.link/webview.lua | 360 | ||||
-rw-r--r-- | config.dot/luakit.link/webview_wm.lua | 21 | ||||
-rw-r--r-- | config.dot/luakit.link/window.lua | 789 |
6 files changed, 1287 insertions, 38 deletions
diff --git a/config.dot/luakit.link/globals.lua b/config.dot/luakit.link/globals.lua new file mode 100644 index 0000000..ff2cbf3 --- /dev/null +++ b/config.dot/luakit.link/globals.lua @@ -0,0 +1,68 @@ +-- Global variables for luakit + +local globals = { + homepage = "luakit://newtab", + scroll_step = 40, + zoom_step = 0.1, + max_cmd_history = 100, + max_srch_history = 100, + default_window_size = "800x600", + vertical_tab_width = 200, + + -- Disables loading of hostnames from /etc/hosts (for large host files) + -- load_etc_hosts = false, + -- Disables checking if a filepath exists in search_open function + -- check_filepath = false, + -- Specify your preferred terminal emulator + -- term = "urxvt", +} + +-- List of search engines. Each item must contain a single %s which is +-- replaced by URI encoded search terms. All other occurances of the percent +-- character (%) may need to be escaped by placing another % before or after +-- it to avoid collisions with lua's string.format characters. +-- See: http://www.lua.org/manual/5.1/manual.html#pdf-string.format +globals.search_engines = { + github = "https://github.com/search?q=%s", + imdb = "http://imdb.com/find?s=all&q=%s", + google = "https://google.com/search?q=%s", + duck = "https://duckduckgo.com/?q=%s&t=debian", + wikipedia = "https://en.wikipedia.org/wiki/Special:Search?search=%s", + debbugs = "https://bugs.debian.org/%s", + sourceforge = "https://sf.net/search/?words=%s", + debian = "https://packages.debian.org/search?keywords=%s", + searx = "https://searx.org/?q=%s", +} + +-- Set google as fallback search engine +globals.search_engines.default = globals.search_engines.duck +-- Use this instead to disable auto-searching +--search_engines.default = "%s" + +-- Per-domain webview properties +-- https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html +-- https://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html +globals.domain_props = { + ["all"] = { + enable_webgl = true, + -- enable_scripts = false, + -- enable_plugins = false, + -- zoom_text_only = true, + }, --[[ + ["youtube.com"] = { + enable_scripts = true, + enable_plugins = true, + }, ]] +} + +-- Cookie acceptance policy +-- Acceptable values: "always", "never", "no_third_party" +soup.accept_policy = "no_third_party" + +-- Cookie storage location +-- Remove this line or set to nil to disable cookie persistence +soup.cookies_storage = luakit.data_dir .. "/cookies.db" + +return globals + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/config.dot/luakit.link/rc.lua b/config.dot/luakit.link/rc.lua index b04d775..01240a1 100644 --- a/config.dot/luakit.link/rc.lua +++ b/config.dot/luakit.link/rc.lua @@ -1,21 +1,35 @@ ------------------------------------------------------------------------------- --- luakit configuration file, more information at https://luakit.github.io/ -- ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------- +-- luakit configuration file, more information at http://luakit.org/ -- +---------------------------------------------------------------------------------------- require "lfs" --- This might cause confusing behaviors with used along with session management --- and one want to be able to run simultaneous instances with different sessions ---require "unique_instance" +--if unique then +-- unique.new("org.luakit") +-- -- Check for a running luakit instance +-- if unique.is_running() then +-- if uris[1] then +-- for _, uri in ipairs(uris) do +-- if lfs.attributes(uri) then uri = os.abspath(uri) end +-- unique.send_message("tabopen " .. uri) +-- end +-- else +-- unique.send_message("winopen") +-- end +-- luakit.quit() +-- end +--end -- Set the number of web processes to use. A value of 0 means 'no limit'. luakit.process_limit = 4 --- Set the cookie storage location -soup.cookies_storage = luakit.data_dir .. "/cookies.db" -- Load library of useful functions for luakit local lousy = require "lousy" +-- Load users global config +-- ("$XDG_CONFIG_HOME/luakit/globals.lua" or "/etc/xdg/luakit/globals.lua") +local globals = require "globals" + -- Load users theme -- ("$XDG_CONFIG_HOME/luakit/theme.lua" or "/etc/xdg/luakit/theme.lua") lousy.theme.init(lousy.util.find_config("theme.lua")) @@ -52,9 +66,6 @@ end) local modes = require "modes" local binds = require "binds" -local settings = require "settings" -require "settings_chrome" - ---------------------------------- -- Optional user script loading -- ---------------------------------- @@ -120,7 +131,7 @@ local history = require "history" local history_chrome = require "history_chrome" local help_chrome = require "help_chrome" -local binds_chrome = require "binds_chrome" +local introspector_chrome = require "introspector_chrome" -- Add command completion local completion = require "completion" @@ -131,8 +142,8 @@ local open_editor = require "open_editor" -- NoScript plugin, toggle scripts and or plugins on a per-domain basis. -- `,ts` to toggle scripts, `,tp` to toggle plugins, `,tr` to reset. --- If you use this module, don't use any site-specific `enable_scripts` or --- `enable_plugins` settings, as these will conflict. +-- Remove all "enable_scripts" & "enable_plugins" lines from your +-- domain_props table (in config/globals.lua) as this module will conflict. --require "noscript" local follow_selected = require "follow_selected" @@ -151,6 +162,9 @@ local styles = require "styles" -- Hide scrollbars on all pages local hide_scrollbars = require "hide_scrollbars" +-- Automatically apply per-domain webview properties +local domain_props = require "domain_props" + -- Add a stylesheet when showing images local image_css = require "image_css" @@ -163,15 +177,13 @@ local tab_favicons = require "tab_favicons" -- Add :view-source command local view_source = require "view_source" --- Load sessions +-- Sessions +local session = require "session" local sessions = require "sessions" --- Custom keys +-- Keys local keys = require "keys" --- Globals -local userprefs = require "userprefs" - ----------------------------- -- End user script loading -- ----------------------------- @@ -188,4 +200,22 @@ local userprefs = require "userprefs" --end window.new(uris) +------------------------------------------- +-- Open URIs from other luakit instances -- +------------------------------------------- + +--if unique then +-- unique.add_signal("message", function (msg, screen) +-- local cmd, arg = string.match(msg, "^(%S+)%s*(.*)") +-- local ww = lousy.util.table.values(window.bywidget)[1] +-- if cmd == "tabopen" then +-- ww:new_tab(arg) +-- elseif cmd == "winopen" then +-- ww = window.new((arg ~= "") and { arg } or {}) +-- end +-- ww.win.screen = screen +-- ww.win.urgency_hint = true +-- end) +--end + -- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/config.dot/luakit.link/userprefs.lua b/config.dot/luakit.link/userprefs.lua deleted file mode 100644 index 35c865c..0000000 --- a/config.dot/luakit.link/userprefs.lua +++ /dev/null @@ -1,19 +0,0 @@ --- User preferences --- See https://github.com/luakit/luakit/issues/468 - --- Requirements -local settings = require "settings" - --- Homepage -settings.window.home_page = "luakit://newtab" - --- Search engines -settings.window.search_engines.imdb = "http://imdb.com/find?s=all&q=%s" -settings.window.search_engines.google = "https://google.com/search?q=%s" -settings.window.search_engines.duck = "https://duckduckgo.com/?q=%s&t=debian" -settings.window.search_engines.wikipedia = "https://en.wikipedia.org/wiki/Special:Search?search=%s" -settings.window.search_engines.debbugs = "https://bugs.debian.org/%s" -settings.window.search_engines.sourceforge = "https://sf.net/search/?words=%s" -settings.window.search_engines.debian = "https://packages.debian.org/search?keywords=%s" -settings.window.search_engines.searx = "https://searx.org/?q=%s" -settings.window.search_engines.default = settings.window.search_engines.duck diff --git a/config.dot/luakit.link/webview.lua b/config.dot/luakit.link/webview.lua new file mode 100644 index 0000000..e3fbfe6 --- /dev/null +++ b/config.dot/luakit.link/webview.lua @@ -0,0 +1,360 @@ +-------------------------- +-- WebKit WebView class -- +-------------------------- + +local window = require("window") +local lousy = require("lousy") +local globals = require("globals") + +-- Webview class table +local webview = {} + +lousy.signal.setup(webview, true) + +local web_module = require_web_module("webview_wm") + +web_module:add_signal("form-active", function (_, page_id) + for _, w in pairs(window.bywidget) do + if w.view.id == page_id then + w.view:emit_signal("form-active") + end + end +end) + +web_module:add_signal("navigate", function (_, page_id, uri) + msg.verbose("Got luakit:// -> file:// navigation: %s", uri) + for _, w in pairs(window.bywidget) do + if w.view.id == page_id then w.view.uri = uri end + end +end) + +local webview_state = setmetatable({}, { __mode = "k" }) + +-- Table of functions which are called on new webview widgets. +local init_funcs = { + -- Set useragent + set_useragent = function (view) + view.user_agent = globals.useragent + end, + + -- Update window and tab titles + title_update = function (view) + view:add_signal("property::title", function (v) + local w = webview.window(v) + if w and w.view == v then + w:update_win_title() + end + end) + end, + + -- Clicking a form field automatically enters insert mode. + form_insert_mode = function (view) + -- Emit root-active event in button release to prevent "missing" + -- buttons or links when the input bar hides. + view:add_signal("button-press", function (v, _, button, context) + if button == 1 then + v:emit_signal(context.editable and "form-active" or "root-active") + end + end) + view:add_signal("form-active", function (v) + local w = webview.window(v) + if not w.mode.passthrough then + w:set_mode("insert") + end + end) + view:add_signal("root-active", function (v) + local w = webview.window(v) + if w.mode.reset_on_focus ~= false then + w:set_mode() + end + end) + view:add_signal("load-status", function (v, status, _, err) + if status == "finished" or (status == "failed" and err == "Load request cancelled") then + web_module:emit_signal(v, "load-finished") + end + end) + end, + + -- Try to match a button event to a users button binding else let the + -- press hit the webview. + button_bind_match = function (view) + view:add_signal("button-release", function (v, mods, button, context) + local w = webview.window(v) + if w:hit(mods, button, { context = context }) then + return true + end + end) + end, + + -- Reset the mode on navigation + mode_reset_on_nav = function (view) + view:add_signal("load-status", function (v, status) + local w = webview.window(v) + if status == "provisional" and w and w.view == v then + if w.mode.reset_on_navigation ~= false then + w:set_mode() + end + end + end) + end, + + -- Action to take on mime type decision request. + mime_decision = function (view) + -- Return true to accept or false to reject from this signal. + view:add_signal("mime-type-decision", function (_, uri, mime) + msg.info("Requested link: %s (%s)", uri, mime) + -- i.e. block binary files like *.exe + --if mime == "application/octet-stream" then + -- return false + --end + end) + end, + + -- Action to take on window open request. + --window_decision = function (view, w) + -- view:add_signal("new-window-decision", function (v, uri, reason) + -- if reason == "link-clicked" then + -- window.new({uri}) + -- else + -- w:new_tab(uri) + -- end + -- return true + -- end) + --end, + + create_webview = function (view) + -- Return a newly created webview in a new tab + view:add_signal("create-web-view", function (v) + return webview.window(v):new_tab(nil, { private = v.private }) + end) + end, + + popup_fix_open_link_label = function (view) + view:add_signal("populate-popup", function (_, menu) + for _, item in ipairs(menu) do + if type(item) == "table" then + -- Optional underscore represents alt-key shortcut letter + item[1] = string.gsub(item[1], "New (_?)Window", "New %1Tab") + end + end + end) + end, +} + +-- These methods are present when you index a window instance and no window +-- method is found in `window.methods`. The window then checks if there is an +-- active webview and calls the following methods with the given view instance +-- as the first argument. All methods must take `view` & `w` as the first two +-- arguments. +webview.methods = { + -- Reload with or without ignoring cache + reload = function (view, _, bypass_cache) + if bypass_cache then + view:reload_bypass_cache() + else + view:reload() + end + end, + + -- Toggle source view + toggle_source = function (view, _, show) + if show == nil then + view.view_source = not view.view_source + else + view.view_source = show + end + view:reload() + end, + + -- Zoom functions + zoom_in = function (view, _, step) + step = step or globals.zoom_step or 0.1 + view.zoom_level = view.zoom_level + step + end, + + zoom_out = function (view, _, step) + step = step or globals.zoom_step or 0.1 + view.zoom_level = math.max(0.01, view.zoom_level) - step + end, + + zoom_set = function (view, _, level) + view.zoom_level = level or 1.0 + end, + + -- History traversing functions + back = function (view, _, n) + view:go_back(n or 1) + view:emit_signal("go-back-forward", -(n or 1)) + end, + + forward = function (view, _, n) + view:go_forward(n or 1) + view:emit_signal("go-back-forward", (n or 1)) + end, +} + +function webview.methods.scroll(view, w, new) + local s = view.scroll + for _, axis in ipairs{ "x", "y" } do + -- Relative px movement + if rawget(new, axis.."rel") then + s[axis] = s[axis] + new[axis.."rel"] + + -- Relative page movement + elseif rawget(new, axis .. "pagerel") then + s[axis] = s[axis] + math.ceil(s[axis.."page_size"] * new[axis.."pagerel"]) + + -- Absolute px movement + elseif rawget(new, axis) then + local n = new[axis] + if n == -1 then + local dir = axis == "x" and "Width" or "Height" + local js = string.format([=[ + Math.max(window.document.documentElement.scroll%s - window.inner%s, 0) + ]=], dir, dir) + w.view:eval_js(js, { callback = function (max) + s[axis] = max + end}) + else + s[axis] = n + end + + -- Absolute page movement + elseif rawget(new, axis.."page") then + s[axis] = math.ceil(s[axis.."page_size"] * new[axis.."page"]) + + -- Absolute percent movement + elseif rawget(new, axis .. "pct") then + local dir = axis == "x" and "Width" or "Height" + local js = string.format([=[ + Math.max(window.document.documentElement.scroll%s - window.inner%s, 0) + ]=], dir, dir) + w.view:eval_js(js, { callback = function (max) + s[axis] = math.ceil(max * (new[axis.."pct"]/100)) + end}) + end + end +end + +local wrap_widget_metatable +do + local wrapped = false + wrap_widget_metatable = function (view) + if wrapped then return end + wrapped = true + + local mt = getmetatable(view) + local oi = mt.__index + mt.__index = function (w, k) + if (k == "uri" or k == "session_state") and oi(w, "type") == "webview" then + local ws = webview_state[w] + if not next(ws.blockers) then return oi(w, k) end + local ql = ws.queued_location or {} + if k == "uri" then + return ql.uri or oi(w, k) + end + if k == "session_state" then + return ql.session_state or oi(w, k) + end + end + return oi(w, k) + end + end +end + +function webview.new(opts) + assert(opts) + local view = widget{type = "webview", private = opts.private} + + webview_state[view] = { blockers = {} } + wrap_widget_metatable(view) + + -- Call webview init functions + for _, func in pairs(init_funcs) do + func(view) + end + webview.emit_signal("init", view) + + return view +end + +luakit.idle_add(function () + local undoclose = package.loaded.undoclose + if not undoclose then return end + undoclose.add_signal("save", function (view) + if view.private then return false end + end) +end) + +function webview.window(view) + assert(type(view) == "widget" and view.type == "webview") + return window.ancestor(view) +end + +function webview.modify_load_block(view, name, enable) + assert(type(view) == "widget" and view.type == "webview") + assert(type(name) == "string") + + local ws = webview_state[view] + ws.blockers[name] = enable and true or nil + msg.verbose("%s %s %s", view, name, enable and "block" or "unblock") + + if not next(ws.blockers) and ws.queued_location then + msg.verbose("fully unblocked %s", view) + local queued = ws.queued_location + ws.queued_location = nil + webview.set_location(view, queued) + end +end + +function webview.set_location(view, arg) + assert(type(view) == "widget" and view.type == "webview") + assert(type(arg) == "string" or type(arg) == "table") + + -- Always execute JS URIs immediately, even when webview is blocked + if type(arg) == "string" and arg:match("^javascript:") then + local js = string.match(arg, "^javascript:(.+)$") + return view:eval_js(luakit.uri_decode(js)) + end + + if type(arg) == "string" then arg = { uri = arg } end + assert(arg.uri or arg.session_state) + + local ws = webview_state[view] + if next(ws.blockers) then + ws.queued_location = arg + if arg.uri then view:emit_signal("property::uri") end + return + end + + if arg.session_state then + view.session_state = arg.session_state + if view.uri == "about:blank" and arg.uri then + view.uri = arg.uri + end + else + view.uri = arg.uri + end +end + +-- Insert webview method lookup on window structure +table.insert(window.indexes, 1, function (w, k) + if k == "view" then + local view = w.tabs[w.tabs:current()] + if view and type(view) == "widget" and view.type == "webview" then + w.view = view + return view + end + end + -- Lookup webview method + local func = webview.methods[k] + if not func then return end + local view = w.view + if view then + return function (_, ...) return func(view, w, ...) end + end +end) + +return webview + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/config.dot/luakit.link/webview_wm.lua b/config.dot/luakit.link/webview_wm.lua new file mode 100644 index 0000000..0d7b5dd --- /dev/null +++ b/config.dot/luakit.link/webview_wm.lua @@ -0,0 +1,21 @@ +local ui = ipc_channel("webview_wm") + +ui:add_signal("load-finished", function(_, page) + local doc = page.document + + -- do nothing if loaded document is not HTML + if not doc.body then return end + + if page.uri:find("luakit://", 1, true) == 1 then + doc.body:add_event_listener("click", true, function (event) + if event.button ~= 0 then return end + if event.target.tag_name ~= "A" then return end + if (event.target.attr.href or ""):find("file://", 1, true) ~= 1 then return end + + ui:emit_signal("navigate", page.id, event.target.attr.href) + end) + end + +end) + +-- vim: et:sw=4:ts=8:sts=4:tw=80 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 |