local awful = require "awful" local gtable = require "gears.table" local gdebug = require "gears.debug" local gfs = require "gears.filesystem" local h = require "misc.helpers" local cfg = require "misc.cfg" local vars = require "misc.vars" local wibox = require "wibox" local beautiful = require "beautiful" local rubato = require "lib.rubato" local al_prompt = require "lib.bling.widget.app_launcher.prompt" local btn = awful.button.names package.cpath = package.cpath .. ";" .. gfs.get_configuration_dir() .. "ui/fresnel/lenses/target/debug/lib?.so" local lenses = require "lenses" local max_entries = 10 local fresnel = {} fresnel._toggled = false fresnel._entries_exec = {} fresnel._entries_offset = 0 fresnel._entries_count = 0 fresnel._visible_entries = 0 fresnel._selected_index = 1 function fresnel:_exec_entry(entry_index) local exec = self._entries_exec[entry_index] if type(exec) ~= "userdata" then awful.spawn((exec[2] and cfg.terminal .. " -e " or "") .. exec[1]) end end function fresnel:_update(query, scrolled) query = query or "" scrolled = scrolled or false if not scrolled then self._selected_index = 1 self._entries_offset = 0 end local layout = self._widget.widget:get_children_by_id("entry_layout")[1] local status = self._widget.widget:get_children_by_id("status")[1] local all_providers = {} local entries_count = 0 self._entries_exec = {} layout:reset() for _, provider in h.opairs(lenses) do local entries = provider(query) table.sort(entries, function(a, b) return a.message < b.message end) all_providers = gtable.join(all_providers, entries) end self._entries_count = #all_providers for i, entry in ipairs(all_providers) do if i <= self._entries_offset then goto continue end if entries_count == max_entries then break end table.insert(self._entries_exec, entry.exec) local entry_widget = wibox.widget { widget = wibox.container.background, shape = vars.shape, { widget = wibox.container.margin, margins = vars.padding, { widget = wibox.container.constraint, strategy = "max", height = vars.char_height, { { widget = wibox.container.background, fg = vars.colors.dim.fg, { widget = wibox.widget.textbox, text = entry.provider .. " | " } }, { widget = wibox.widget.textbox, text = entry.message }, spacing = vars.padding, layout = wibox.layout.fixed.horizontal } } }, buttons = { awful.button { modifiers = {}, button = btn.LEFT, on_press = function() self:_exec_entry(i) end } }, _selected = false } if self._selected_index + self._entries_offset == i then entry_widget._selected = true entry_widget.bg = vars.colors.black end entry_widget:connect_signal("mouse::enter", function() if entry_widget._selected == true then return end entry_widget.bg = vars.colors.black end) entry_widget:connect_signal("mouse::leave", function() if entry_widget._selected == true then return end entry_widget.bg = vars.colors.bg end) layout:add(entry_widget) entries_count = entries_count + 1 self._visible_entries = entries_count ::continue:: end status.text = self._entries_offset + self._selected_index .. "/" .. self._entries_count end fresnel._text = "" fresnel._prompt = al_prompt { prompt = "", reset_on_stop = true, ul_cursor = "low", bg_cursor = vars.colors.black, changed_callback = function(text) if fresnel._text == text then return end if fresnel._toggled == false then return end fresnel:_update(text) fresnel._text = text end, keypressed_callback = function(mod, key) if key == "Escape" or key == " " and mod.Mod4 then fresnel:hide() elseif key == "Return" then fresnel:_exec_entry(fresnel._selected_index) fresnel:hide() elseif key == "Up" then local next_index = fresnel._selected_index - 1 if next_index < 1 and fresnel._entries_offset == 0 then return elseif next_index < 1 and fresnel._entries_offset > 0 then fresnel._entries_offset = fresnel._entries_offset - 1 else fresnel._selected_index = next_index end fresnel:_update(fresnel._text, true) elseif key == "Down" then local next_index = fresnel._selected_index + 1 if next_index > fresnel._visible_entries and fresnel._entries_offset + fresnel._visible_entries == fresnel._entries_count then return elseif next_index > fresnel._visible_entries and fresnel._entries_offset + fresnel._visible_entries < fresnel._entries_count then fresnel._entries_offset = fresnel._entries_offset + 1 else fresnel._selected_index = next_index end fresnel:_update(fresnel._text, true) end end } fresnel._widget = h.popup { visible = false, ontop = true, placement = function(d) awful.placement.top(d, { margins = { top = beautiful.useless_gap * 2 } }) end, minimum_width = screen[1].geometry.width / 2, maximum_width = screen[1].geometry.width / 2, widget = { { widget = wibox.container.background, bg = vars.colors.black, fg = vars.colors.dim.fg, shape = vars.shape, { widget = wibox.container.margin, margins = vars.padding, { { widget = wibox.widget.textbox, text = ">" }, { widget = wibox.container.margin, margins = { left = vars.padding, right = vars.padding }, { widget = wibox.container.constraint, strategy = "max", height = vars.char_height, { widget = wibox.container.background, fg = vars.colors.fg, fresnel._prompt.textbox } } }, { widget = wibox.widget.textbox, text = "0/0", id = "status" }, layout = wibox.layout.align.horizontal } } }, { widget = wibox.container.margin, margins = { top = vars.padding }, { spacing = vars.padding, layout = wibox.layout.fixed.vertical, id = "entry_layout" } }, layout = wibox.layout.align.vertical } } fresnel._widget.maximum_height = vars.big_padding * 2 + (vars.padding * 2 + vars.char_height) * (1 + 10) + vars.padding + vars.padding * 9 function fresnel:show() self._toggled = true self._timed.target = 1 self:_update() self._prompt:start() end function fresnel:hide() self._toggled = false self._timed.target = 0 self._prompt:stop() end fresnel._timed = rubato.timed { duration = vars.anim_duration, intro = vars.anim_intro, pos = 0, subscribed = function(pos) fresnel._widget.opacity = pos if pos == 0 then fresnel._widget.visible = false else fresnel._widget.visible = true end end } return fresnel