| 1 | ------------------ |
| 2 | -- Window class -- |
| 3 | ------------------ |
| 4 | |
| 5 | -- Window class table |
| 6 | window = {} |
| 7 | |
| 8 | -- Widget construction aliases |
| 9 | local function entry() return widget{type="entry"} end |
| 10 | local function eventbox() return widget{type="eventbox"} end |
| 11 | local function hbox() return widget{type="hbox"} end |
| 12 | local function label() return widget{type="label"} end |
| 13 | local function notebook() return widget{type="notebook"} end |
| 14 | local function vbox() return widget{type="vbox"} end |
| 15 | |
| 16 | -- Build and pack window widgets |
| 17 | function window.build() |
| 18 | -- Create a table for widgets and state variables for a window |
| 19 | local w = { |
| 20 | win = widget{type="window"}, |
| 21 | ebox = eventbox(), |
| 22 | layout = vbox(), |
| 23 | tabs = notebook(), |
| 24 | -- Tab bar widgets |
| 25 | tbar = { |
| 26 | layout = hbox(), |
| 27 | ebox = eventbox(), |
| 28 | titles = { }, |
| 29 | }, |
| 30 | -- Status bar widgets |
| 31 | sbar = { |
| 32 | layout = hbox(), |
| 33 | ebox = eventbox(), |
| 34 | -- Left aligned widgets |
| 35 | l = { |
| 36 | layout = hbox(), |
| 37 | ebox = eventbox(), |
| 38 | uri = label(), |
| 39 | loaded = label(), |
| 40 | }, |
| 41 | -- Fills space between the left and right aligned widgets |
| 42 | sep = eventbox(), |
| 43 | -- Right aligned widgets |
| 44 | r = { |
| 45 | layout = hbox(), |
| 46 | ebox = eventbox(), |
| 47 | buf = label(), |
| 48 | ssl = label(), |
| 49 | tabi = label(), |
| 50 | scroll = label(), |
| 51 | }, |
| 52 | }, |
| 53 | -- Input bar widgets |
| 54 | ibar = { |
| 55 | layout = hbox(), |
| 56 | ebox = eventbox(), |
| 57 | prompt = label(), |
| 58 | input = entry(), |
| 59 | }, |
| 60 | } |
| 61 | |
| 62 | -- Assemble window |
| 63 | w.ebox:set_child(w.layout) |
| 64 | w.win:set_child(w.ebox) |
| 65 | |
| 66 | -- Pack tab bar |
| 67 | local t = w.tbar |
| 68 | t.ebox:set_child(t.layout, false, false, 0) |
| 69 | w.layout:pack_start(t.ebox, false, false, 0) |
| 70 | |
| 71 | -- Pack notebook |
| 72 | w.layout:pack_start(w.tabs, true, true, 0) |
| 73 | |
| 74 | -- Pack left-aligned statusbar elements |
| 75 | local l = w.sbar.l |
| 76 | l.layout:pack_start(l.uri, false, false, 0) |
| 77 | l.layout:pack_start(l.loaded, false, false, 0) |
| 78 | l.ebox:set_child(l.layout) |
| 79 | |
| 80 | -- Pack right-aligned statusbar elements |
| 81 | local r = w.sbar.r |
| 82 | r.layout:pack_start(r.buf, false, false, 0) |
| 83 | r.layout:pack_start(r.ssl, false, false, 0) |
| 84 | r.layout:pack_start(r.tabi, false, false, 0) |
| 85 | r.layout:pack_start(r.scroll, false, false, 0) |
| 86 | r.ebox:set_child(r.layout) |
| 87 | |
| 88 | -- Pack status bar elements |
| 89 | local s = w.sbar |
| 90 | s.layout:pack_start(l.ebox, false, false, 0) |
| 91 | s.layout:pack_start(s.sep, true, true, 0) |
| 92 | s.layout:pack_start(r.ebox, false, false, 0) |
| 93 | s.ebox:set_child(s.layout) |
| 94 | w.layout:pack_start(s.ebox, false, false, 0) |
| 95 | |
| 96 | -- Pack input bar |
| 97 | local i = w.ibar |
| 98 | i.layout:pack_start(i.prompt, false, false, 0) |
| 99 | i.layout:pack_start(i.input, true, true, 0) |
| 100 | i.ebox:set_child(i.layout) |
| 101 | w.layout:pack_start(i.ebox, false, false, 0) |
| 102 | |
| 103 | -- Other settings |
| 104 | i.input.show_frame = false |
| 105 | w.tabs.show_tabs = false |
| 106 | l.loaded:hide() |
| 107 | l.uri.selectable = true |
| 108 | r.ssl:hide() |
| 109 | |
| 110 | return w |
| 111 | end |
| 112 | |
| 113 | -- Table of functions to call on window creation. Normally used to add signal |
| 114 | -- handlers to the new windows widgets. |
| 115 | window.init_funcs = { |
| 116 | -- Attach notebook widget signals |
| 117 | notebook_signals = function (w) |
| 118 | w.tabs:add_signal("page-added", function (nbook, view, idx) |
| 119 | w:update_tab_count(idx) |
| 120 | w:update_tab_labels() |
| 121 | end) |
| 122 | w.tabs:add_signal("switch-page", function (nbook, view, idx) |
| 123 | w:set_mode() |
| 124 | w:update_tab_count(idx) |
| 125 | w:update_win_title(view) |
| 126 | w:update_uri(view) |
| 127 | w:update_progress(view) |
| 128 | w:update_tab_labels(idx) |
| 129 | w:update_buf() |
| 130 | w:update_ssl(view) |
| 131 | end) |
| 132 | end, |
| 133 | |
| 134 | last_win_check = function (w) |
| 135 | w.win:add_signal("destroy", function () |
| 136 | -- call the quit function if this was the last window left |
| 137 | if #luakit.windows == 0 then luakit.quit() end |
| 138 | end) |
| 139 | end, |
| 140 | |
| 141 | key_press_match = function (w) |
| 142 | w.win:add_signal("key-press", function (_, mods, key) |
| 143 | -- Reset command line completion |
| 144 | if w:get_mode() == "command" and key ~= "Tab" and w.compl_start then |
| 145 | w:update_uri() |
| 146 | w.compl_index = 0 |
| 147 | end |
| 148 | |
| 149 | if w:hit(mods, key) then |
| 150 | return true |
| 151 | end |
| 152 | end) |
| 153 | end, |
| 154 | |
| 155 | apply_window_theme = function (w) |
| 156 | local theme = lousy.theme.get() |
| 157 | local s, i = w.sbar, w.ibar |
| 158 | |
| 159 | -- Set foregrounds |
| 160 | for wi, v in pairs({ |
| 161 | [s.l.uri] = theme.uri_sbar_fg, |
| 162 | [s.l.loaded] = theme.loaded_sbar_fg, |
| 163 | [s.r.buf] = theme.buf_sbar_fg, |
| 164 | [s.r.tabi] = theme.tabi_sbar_fg, |
| 165 | [s.r.scroll] = theme.scroll_sbar_fg, |
| 166 | [i.prompt] = theme.prompt_ibar_fg, |
| 167 | [i.input] = theme.input_ibar_fg, |
| 168 | }) do wi.fg = v end |
| 169 | |
| 170 | -- Set backgrounds |
| 171 | for wi, v in pairs({ |
| 172 | [s.l.ebox] = theme.sbar_bg, |
| 173 | [s.r.ebox] = theme.sbar_bg, |
| 174 | [s.sep] = theme.sbar_bg, |
| 175 | [s.ebox] = theme.sbar_bg, |
| 176 | [i.ebox] = theme.ibar_bg, |
| 177 | [i.input] = theme.input_ibar_bg, |
| 178 | }) do wi.bg = v end |
| 179 | |
| 180 | -- Set fonts |
| 181 | for wi, v in pairs({ |
| 182 | [s.l.uri] = theme.uri_sbar_font, |
| 183 | [s.l.loaded] = theme.loaded_sbar_font, |
| 184 | [s.r.buf] = theme.buf_sbar_font, |
| 185 | [s.r.ssl] = theme.ssl_sbar_font, |
| 186 | [s.r.tabi] = theme.tabi_sbar_font, |
| 187 | [s.r.scroll] = theme.scroll_sbar_font, |
| 188 | [i.prompt] = theme.prompt_ibar_font, |
| 189 | [i.input] = theme.input_ibar_font, |
| 190 | }) do wi.font = v end |
| 191 | end, |
| 192 | } |
| 193 | |
| 194 | -- Helper functions which operate on the window widgets or structure. |
| 195 | window.methods = { |
| 196 | -- Return the widget in the currently active tab |
| 197 | get_current = function (w) return w.tabs:atindex(w.tabs:current()) end, |
| 198 | -- Check if given widget is the widget in the currently active tab |
| 199 | is_current = function (w, wi) return w.tabs:indexof(wi) == w.tabs:current() end, |
| 200 | |
| 201 | get_tab_title = function (w, view) |
| 202 | if not view then view = w:get_current() end |
| 203 | return view:get_prop("title") or view.uri or "(Untitled)" |
| 204 | end, |
| 205 | |
| 206 | new_tab = function (w, uri) |
| 207 | local view = webview.new(w, uri) |
| 208 | w.tabs:append(view) |
| 209 | w:update_tab_count() |
| 210 | return view |
| 211 | end, |
| 212 | |
| 213 | -- Wrapper around the bind plugin's hit method |
| 214 | hit = function (w, mods, key) |
| 215 | local caught, newbuf = lousy.bind.hit(w.binds or {}, mods, key, w.buffer, false, w) |
| 216 | if w.win then |
| 217 | w.buffer = newbuf |
| 218 | w:update_buf() |
| 219 | end |
| 220 | return caught |
| 221 | end, |
| 222 | |
| 223 | -- Wrapper around the bind plugin's match_cmd method |
| 224 | match_cmd = function (w, buffer) |
| 225 | return lousy.bind.match_cmd(binds.commands, buffer, w) |
| 226 | end, |
| 227 | |
| 228 | -- enter command or characters into command line |
| 229 | enter_cmd = function (w, cmd) |
| 230 | local i = w.ibar.input |
| 231 | w:set_mode("command") |
| 232 | i.text = cmd |
| 233 | i:set_position(-1) |
| 234 | end, |
| 235 | |
| 236 | -- insert a string into the command line at the current cursor position |
| 237 | insert_cmd = function (w, str) |
| 238 | if not str then return end |
| 239 | local i = w.ibar.input |
| 240 | local text = i.text |
| 241 | local pos = i:get_position() |
| 242 | local left, right = string.sub(text, 1, pos), string.sub(text, pos+1) |
| 243 | i.text = left .. str .. right |
| 244 | i:set_position(pos + #str + 1) |
| 245 | end, |
| 246 | |
| 247 | -- Command line completion of available commands |
| 248 | cmd_completion = function (w) |
| 249 | local i = w.ibar.input |
| 250 | local s = w.sbar.l.uri |
| 251 | local cmpl = {} |
| 252 | |
| 253 | -- Get last completion (is reset on key press other than <Tab>) |
| 254 | if not w.compl_start or w.compl_index == 0 then |
| 255 | w.compl_start = "^" .. string.sub(i.text, 2) |
| 256 | w.compl_index = 1 |
| 257 | end |
| 258 | |
| 259 | -- Get suitable commands |
| 260 | for _, b in ipairs(binds.commands) do |
| 261 | for _, c in pairs(b.commands) do |
| 262 | if c and string.match(c, w.compl_start) then |
| 263 | table.insert(cmpl, c) |
| 264 | end |
| 265 | end |
| 266 | end |
| 267 | |
| 268 | table.sort(cmpl) |
| 269 | |
| 270 | if #cmpl > 0 then |
| 271 | local text = "" |
| 272 | for index, comp in pairs(cmpl) do |
| 273 | if index == w.compl_index then |
| 274 | i.text = ":" .. comp .. " " |
| 275 | i:set_position(-1) |
| 276 | end |
| 277 | if text ~= "" then |
| 278 | text = text .. " | " |
| 279 | end |
| 280 | text = text .. comp |
| 281 | end |
| 282 | |
| 283 | -- cycle through all possible completions |
| 284 | if w.compl_index == #cmpl then |
| 285 | w.compl_index = 1 |
| 286 | else |
| 287 | w.compl_index = w.compl_index + 1 |
| 288 | end |
| 289 | s.text = lousy.util.escape(text) |
| 290 | end |
| 291 | end, |
| 292 | |
| 293 | del_word = function (w) |
| 294 | local i = w.ibar.input |
| 295 | local text = i.text |
| 296 | local pos = i:get_position() |
| 297 | if text and #text > 1 and pos > 1 then |
| 298 | local left, right = string.sub(text, 2, pos), string.sub(text, pos+1) |
| 299 | if not string.find(left, "%s") then |
| 300 | left = "" |
| 301 | elseif string.find(left, "%w+%s*$") then |
| 302 | left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1) |
| 303 | elseif string.find(left, "%W+%s*$") then |
| 304 | left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1) |
| 305 | end |
| 306 | i.text = string.sub(text, 1, 1) .. left .. right |
| 307 | i:set_position(#left + 2) |
| 308 | end |
| 309 | end, |
| 310 | |
| 311 | del_line = function (w) |
| 312 | local i = w.ibar.input |
| 313 | if i.text ~= ":" then |
| 314 | i.text = ":" |
| 315 | i:set_position(-1) |
| 316 | end |
| 317 | end, |
| 318 | |
| 319 | -- Search history adding |
| 320 | srch_hist_add = function (w, srch) |
| 321 | if not w.srch_hist then w.srch_hist = {} end |
| 322 | -- Check overflow |
| 323 | local max_hist = globals.max_srch_history or 100 |
| 324 | if #w.srch_hist > (max_hist + 5) then |
| 325 | while #w.srch_hist > max_hist do |
| 326 | table.remove(w.srch_hist, 1) |
| 327 | end |
| 328 | end |
| 329 | table.insert(w.srch_hist, srch) |
| 330 | end, |
| 331 | |
| 332 | -- Search history traversing |
| 333 | srch_hist_prev = function (w) |
| 334 | if not w.srch_hist then w.srch_hist = {} end |
| 335 | if not w.srch_hist_cursor then |
| 336 | w.srch_hist_cursor = #w.srch_hist + 1 |
| 337 | w.srch_hist_current = w.ibar.input.text |
| 338 | end |
| 339 | local c = w.srch_hist_cursor - 1 |
| 340 | if w.srch_hist[c] then |
| 341 | w.srch_hist_cursor = c |
| 342 | w.ibar.input.text = w.srch_hist[c] |
| 343 | w.ibar.input:set_position(-1) |
| 344 | end |
| 345 | end, |
| 346 | |
| 347 | srch_hist_next = function (w) |
| 348 | if not w.srch_hist then w.srch_hist = {} end |
| 349 | local c = (w.srch_hist_cursor or #w.srch_hist) + 1 |
| 350 | if w.srch_hist[c] then |
| 351 | w.srch_hist_cursor = c |
| 352 | w.ibar.input.text = w.srch_hist[c] |
| 353 | w.ibar.input:set_position(-1) |
| 354 | elseif w.srch_hist_current then |
| 355 | w.srch_hist_cursor = nil |
| 356 | w.ibar.input.text = w.srch_hist_current |
| 357 | w.ibar.input:set_position(-1) |
| 358 | end |
| 359 | end, |
| 360 | |
| 361 | -- Command history adding |
| 362 | cmd_hist_add = function (w, cmd) |
| 363 | if not w.cmd_hist then w.cmd_hist = {} end |
| 364 | -- Make sure history doesn't overflow |
| 365 | local max_hist = globals.max_cmd_hist or 100 |
| 366 | if #w.cmd_hist > (max_hist + 5) then |
| 367 | while #w.cmd_hist > max_hist do |
| 368 | table.remove(w.cmd_hist, 1) |
| 369 | end |
| 370 | end |
| 371 | table.insert(w.cmd_hist, cmd) |
| 372 | end, |
| 373 | |
| 374 | -- Command history traversing |
| 375 | cmd_hist_prev = function (w) |
| 376 | if not w.cmd_hist then w.cmd_hist = {} end |
| 377 | if not w.cmd_hist_cursor then |
| 378 | w.cmd_hist_cursor = #w.cmd_hist + 1 |
| 379 | w.cmd_hist_current = w.ibar.input.text |
| 380 | end |
| 381 | local c = w.cmd_hist_cursor - 1 |
| 382 | if w.cmd_hist[c] then |
| 383 | w.cmd_hist_cursor = c |
| 384 | w.ibar.input.text = w.cmd_hist[c] |
| 385 | w.ibar.input:set_position(-1) |
| 386 | end |
| 387 | end, |
| 388 | |
| 389 | cmd_hist_next = function (w) |
| 390 | if not w.cmd_hist then w.cmd_hist = {} end |
| 391 | local c = (w.cmd_hist_cursor or #w.cmd_hist) + 1 |
| 392 | if w.cmd_hist[c] then |
| 393 | w.cmd_hist_cursor = c |
| 394 | w.ibar.input.text = w.cmd_hist[c] |
| 395 | w.ibar.input:set_position(-1) |
| 396 | elseif w.cmd_hist_current then |
| 397 | w.cmd_hist_cursor = nil |
| 398 | w.ibar.input.text = w.cmd_hist_current |
| 399 | w.ibar.input:set_position(-1) |
| 400 | end |
| 401 | end, |
| 402 | |
| 403 | -- GUI content update functions |
| 404 | update_tab_count = function (w, i, t) |
| 405 | w.sbar.r.tabi.text = string.format("[%d/%d]", i or w.tabs:current(), t or w.tabs:count()) |
| 406 | end, |
| 407 | |
| 408 | update_win_title = function (w, view) |
| 409 | if not view then view = w:get_current() end |
| 410 | local title = view:get_prop("title") |
| 411 | local uri = view.uri |
| 412 | if not title and not uri then |
| 413 | w.win.title = "luakit" |
| 414 | else |
| 415 | w.win.title = (title or "luakit") .. " - " .. (uri or "about:blank") |
| 416 | end |
| 417 | end, |
| 418 | |
| 419 | update_uri = function (w, view, uri) |
| 420 | if not view then view = w:get_current() end |
| 421 | w.sbar.l.uri.text = lousy.util.escape((uri or (view and view.uri) or "about:blank")) |
| 422 | end, |
| 423 | |
| 424 | update_progress = function (w, view, p) |
| 425 | if not view then view = w:get_current() end |
| 426 | if not p then p = view:get_prop("progress") end |
| 427 | local loaded = w.sbar.l.loaded |
| 428 | if not view:loading() or p == 1 then |
| 429 | loaded:hide() |
| 430 | else |
| 431 | loaded:show() |
| 432 | loaded.text = string.format("(%d%%)", p * 100) |
| 433 | end |
| 434 | end, |
| 435 | |
| 436 | update_scroll = function (w, view) |
| 437 | if not view then view = w:get_current() end |
| 438 | local scroll = w.sbar.r.scroll |
| 439 | if view then |
| 440 | local val, max = view:get_scroll_vert() |
| 441 | if max == 0 then val = "All" |
| 442 | elseif val == 0 then val = "Top" |
| 443 | elseif val == max then val = "Bot" |
| 444 | else val = string.format("%2d%%", (val/max) * 100) |
| 445 | end |
| 446 | if scroll.text ~= val then scroll.text = val end |
| 447 | scroll:show() |
| 448 | else |
| 449 | scroll:hide() |
| 450 | end |
| 451 | end, |
| 452 | |
| 453 | update_ssl = function (w, view) |
| 454 | if not view then view = w:get_current() end |
| 455 | local trusted = view:ssl_trusted() |
| 456 | local theme = lousy.theme.get() |
| 457 | local ssl = w.sbar.r.ssl |
| 458 | if trusted ~= nil and not w.checking_ssl then |
| 459 | ssl.fg = theme.notrust_fg |
| 460 | ssl.text = "(nocheck)" |
| 461 | ssl:show() |
| 462 | elseif trusted == true then |
| 463 | ssl.fg = theme.trust_fg |
| 464 | ssl.text = "(trust)" |
| 465 | ssl:show() |
| 466 | elseif trusted == false then |
| 467 | ssl.fg = theme.notrust_fg |
| 468 | ssl.text = "(notrust)" |
| 469 | ssl:show() |
| 470 | else |
| 471 | ssl:hide() |
| 472 | end |
| 473 | end, |
| 474 | |
| 475 | update_buf = function (w) |
| 476 | local buf = w.sbar.r.buf |
| 477 | if w.buffer then |
| 478 | buf.text = lousy.util.escape(string.format(" %-3s", w.buffer)) |
| 479 | buf:show() |
| 480 | else |
| 481 | buf:hide() |
| 482 | end |
| 483 | end, |
| 484 | |
| 485 | update_binds = function (w, mode) |
| 486 | -- Generate the list of active key & buffer binds for this mode |
| 487 | w.binds = lousy.util.table.join(binds.mode_binds[mode], binds.mode_binds.all) |
| 488 | -- Clear & hide buffer |
| 489 | w.buffer = nil |
| 490 | w:update_buf() |
| 491 | end, |
| 492 | |
| 493 | -- Tab label functions |
| 494 | -- TODO: Move these functions into a module (I.e. lousy.widget.tablist) |
| 495 | make_tab_label = function (w, pos) |
| 496 | local theme = lousy.theme.get() |
| 497 | local t = { |
| 498 | label = label(), |
| 499 | sep = eventbox(), |
| 500 | ebox = eventbox(), |
| 501 | layout = hbox(), |
| 502 | } |
| 503 | t.label.font = theme.tab_font |
| 504 | t.label:set_width(1) |
| 505 | t.layout:pack_start(t.label, true, true, 0) |
| 506 | t.layout:pack_start(t.sep, false, false, 0) |
| 507 | t.ebox:set_child(t.layout) |
| 508 | t.ebox:add_signal("button-release", function (e, m, b) |
| 509 | if b == 1 then |
| 510 | w.tabs:switch(pos) |
| 511 | return true |
| 512 | elseif b == 2 then |
| 513 | w:close_tab(w.tabs:atindex(pos)) |
| 514 | return true |
| 515 | end |
| 516 | end) |
| 517 | return t |
| 518 | end, |
| 519 | |
| 520 | destroy_tab_label = function (w, t) |
| 521 | if not t then t = table.remove(w.tbar.titles) end |
| 522 | -- Destroy widgets without their own windows first (I.e. labels) |
| 523 | for _, wi in ipairs{ t.label, t.sep, t.ebox, t.layout } do wi:destroy() end |
| 524 | end, |
| 525 | |
| 526 | update_tab_labels = function (w, current) |
| 527 | local tb = w.tbar |
| 528 | local count, current = w.tabs:count(), current or w.tabs:current() |
| 529 | tb.ebox:hide() |
| 530 | |
| 531 | -- Leave the tablist hidden if there is only one tab open |
| 532 | if count <= 1 then |
| 533 | return nil |
| 534 | end |
| 535 | |
| 536 | if count ~= #tb.titles then |
| 537 | -- Grow the number of labels |
| 538 | while count > #tb.titles do |
| 539 | local t = w:make_tab_label(#tb.titles + 1) |
| 540 | tb.layout:pack_start(t.ebox, true, true, 0) |
| 541 | table.insert(tb.titles, t) |
| 542 | end |
| 543 | -- Prune number of labels |
| 544 | while count < #tb.titles do |
| 545 | w:destroy_tab_label() |
| 546 | end |
| 547 | end |
| 548 | |
| 549 | if count ~= 0 then |
| 550 | for i = 1, count do |
| 551 | local view = w.tabs:atindex(i) |
| 552 | local t = tb.titles[i] |
| 553 | local title = " " ..i.. " "..w:get_tab_title(view) |
| 554 | t.label.text = lousy.util.escape(string.format("%-40s", title)) |
| 555 | w:apply_tablabel_theme(t, i == current) |
| 556 | end |
| 557 | end |
| 558 | tb.ebox:show() |
| 559 | end, |
| 560 | |
| 561 | -- Theme functions |
| 562 | apply_tablabel_theme = function (w, t, selected) |
| 563 | local theme = lousy.theme.get() |
| 564 | selected = (selected and "_selected") or "" |
| 565 | t.label.fg = theme[string.format("tab%s_fg", selected)] |
| 566 | t.ebox.bg = theme[string.format("tab%s_bg", selected)] |
| 567 | end, |
| 568 | |
| 569 | close_win = function (w) |
| 570 | -- Close all tabs |
| 571 | while w.tabs:count() ~= 0 do |
| 572 | w:close_tab() |
| 573 | end |
| 574 | |
| 575 | -- Recursively remove widgets from window |
| 576 | local children = lousy.util.recursive_remove(w.win) |
| 577 | -- Destroy all widgets |
| 578 | for i, c in ipairs(lousy.util.table.join(children, {w.win})) do |
| 579 | if c.hide then c:hide() end |
| 580 | c:destroy() |
| 581 | end |
| 582 | |
| 583 | -- Clear window struct |
| 584 | w = setmetatable(w, {}) |
| 585 | for k, _ in pairs(w) do w[k] = nil end |
| 586 | |
| 587 | -- Quit if closed last window |
| 588 | if #luakit.windows == 0 then luakit.quit() end |
| 589 | end, |
| 590 | } |
| 591 | |
| 592 | -- Ordered list of class index functions. Other classes (E.g. webview) are able |
| 593 | -- to add their own index functions to this list. |
| 594 | window.indexes = { |
| 595 | -- Find function in window.methods first |
| 596 | function (w, k) return window.methods[k] end |
| 597 | } |
| 598 | |
| 599 | -- Create new window |
| 600 | function window.new(uris) |
| 601 | local w = window.build() |
| 602 | |
| 603 | -- Set window metatable |
| 604 | setmetatable(w, { |
| 605 | __index = function (_, k) |
| 606 | -- Check widget structure first |
| 607 | local v = rawget(w, k) |
| 608 | if v then return v end |
| 609 | -- Call each window index function |
| 610 | for _, index in ipairs(window.indexes) do |
| 611 | v = index(w, k) |
| 612 | if v then return v end |
| 613 | end |
| 614 | end, |
| 615 | }) |
| 616 | |
| 617 | -- Call window init functions |
| 618 | for _, func in pairs(window.init_funcs) do |
| 619 | func(w) |
| 620 | end |
| 621 | |
| 622 | -- Populate notebook with tabs |
| 623 | for _, uri in ipairs(uris or {}) do |
| 624 | w:new_tab(uri) |
| 625 | end |
| 626 | |
| 627 | -- Make sure something is loaded |
| 628 | if w.tabs:count() == 0 then |
| 629 | w:new_tab(globals.homepage) |
| 630 | end |
| 631 | |
| 632 | -- Set initial mode |
| 633 | w:set_mode() |
| 634 | |
| 635 | return w |
| 636 | end |
| 637 | |
| 638 | -- vim: et:sw=4:ts=8:sts=4:tw=80 |