diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index fae3b86..bade091 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -1,61 +1,39 @@ ---------------------------------------------------------------------------- ---- Modified Prompt module. --- @author Julien Danjou <julien@danjou.info> --- @copyright 2008 Julien Danjou ---------------------------------------------------------------------------- - -local akey = require("awful.key") -local keygrabber = require("awful.keygrabber") -local gobject = require("gears.object") -local gdebug = require('gears.debug') +------------------------------------------- +-- @author https://github.com/Kasper24 +-- @copyright 2021-2022 Kasper24 +------------------------------------------- +local awful = require("awful") local gtable = require("gears.table") -local gcolor = require("gears.color") local gstring = require("gears.string") -local gfs = require("gears.filesystem") local wibox = require("wibox") local beautiful = require("beautiful") -local io = io -local table = table -local math = math +local dpi = beautiful.xresources.apply_dpi +local tostring = tostring +local tonumber = tonumber +local ceil = math.ceil local ipairs = ipairs -local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) -local capi = { selection = selection } +local string = string +local capi = { + awesome = awesome, + root = root, + mouse = mouse, + tag = tag, + client = client +} -local prompt = { mt = {} } +local prompt = { + mt = {} +} ---- Private data -local data = {} -data.history = {} - -local function itera(inc,a, i) - i = i + inc - local v = a[i] - if v then return i,v end -end - -local function history_check_load(id, max) - if id and id ~= "" and not data.history[id] then - data.history[id] = { max = 50, table = {} } - - if max then - data.history[id].max = max - end - - local f = io.open(id, "r") - if not f then return end - - -- Read history file - for line in f:lines() do - if gtable.hasitem(data.history[id].table, line) == nil then - table.insert(data.history[id].table, line) - if #data.history[id].table >= data.history[id].max then - break - end - end - end - f:close() - end -end +local properties = { + "only_numbers", "round", "obscure", + "always_on", "reset_on_stop", + "stop_on_lost_focus", "stop_on_tag_changed", "stop_on_clicked_outside", + "icon_font", "icon_size", "icon_color", "icon", + "label_font", "label_size", "label_color", "label", + "text_font", "text_size", "text_color", "text", + "cursor_size", "cursor_color" +} local function is_word_char(c) if string.find(c, "[{[(,.:;_-+=@/ ]") then @@ -87,570 +65,406 @@ local function cword_end(s, pos) while i <= #s and not is_word_char(s:sub(i, i)) do i = i + 1 end - while i <= #s and is_word_char(s:sub(i, i)) do + while i <= #s and is_word_char(s:sub(i, i)) do i = i + 1 end return i end -local function history_save(id) - if data.history[id] then - gfs.make_parent_directories(id) - local f = io.open(id, "w") - if not f then - gdebug.print_warning("Failed to write the history to "..id) - return - end - for i = 1, math.min(#data.history[id].table, data.history[id].max) do - f:write(data.history[id].table[i] .. "\n") - end - f:close() - end -end - -local function history_items(id) - if data.history[id] then - return #data.history[id].table - else - return -1 - end -end - -local function history_add(id, command) - if data.history[id] and command ~= "" then - local index = gtable.hasitem(data.history[id].table, command) - if index == nil then - table.insert(data.history[id].table, command) - - -- Do not exceed our max_cmd - if #data.history[id].table > data.history[id].max then - table.remove(data.history[id].table, 1) - end - - history_save(id) - else - -- Bump this command to the end of history - table.remove(data.history[id].table, index) - table.insert(data.history[id].table, command) - history_save(id) - end - end -end - local function have_multibyte_char_at(text, position) return text:sub(position, position):wlen() == -1 end -local function prompt_text_with_cursor(args) - local char, spacer, text_start, text_end, ret - local text = args.text or "" - local _prompt = args.prompt or "" - local underline = args.cursor_ul or "none" +local function generate_markup(self, show_cursor) + local wp = self._private - if args.select_all then - if #text == 0 then char = " " else char = gstring.xml_escape(text) end - spacer = " " - text_start = "" - text_end = "" - elseif #text < args.cursor_pos then - char = " " - spacer = "" - text_start = gstring.xml_escape(text) - text_end = "" - else - local offset = 0 - if have_multibyte_char_at(text, args.cursor_pos) then - offset = 1 + local icon_size = dpi(ceil(wp.icon_size * 1024)) + local label_size = dpi(ceil(wp.label_size * 1024)) + local text_size = dpi(ceil(wp.text_size * 1024)) + local cursor_size = dpi(ceil(wp.cursor_size * 1024)) + + local text = tostring(wp.text) or "" + if wp.obscure == true then + text = text:gsub(".", "*") + end + + local markup = "" + if wp.icon ~= nil then + markup = string.format( + '%s ', + wp.icon.font, icon_size, wp.icon_color, wp.icon.icon) + end + + if show_cursor == true then + local char, spacer, text_start, text_end + + if #text < wp.cur_pos then + char = " " + spacer = "" + text_start = gstring.xml_escape(text) + text_end = "" + else + local offset = 0 + if have_multibyte_char_at(text, wp.cur_pos) then + offset = 1 + end + char = gstring.xml_escape(text:sub(wp.cur_pos, wp.cur_pos + offset)) + spacer = " " + text_start = gstring.xml_escape(text:sub(1, wp.cur_pos - 1)) + text_end = gstring.xml_escape(text:sub(wp.cur_pos + offset)) end - char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos + offset)) - spacer = " " - text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1)) - text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1 + offset)) + + markup = markup .. (string.format( + '%s' .. + '%s' .. + '%s' .. + '%s%s', + wp.label_font, label_size, wp.label_color, wp.label, + wp.text_font, text_size, wp.text_color, text_start, + cursor_size, wp.cursor_color, char, + wp.text_font, text_size, wp.text_color, text_end, + spacer)) + else + markup = markup .. string.format( + '%s' .. + '%s', + wp.label_font, label_size, wp.label_color, wp.label, + wp.text_font, text_size, wp.text_color, gstring.xml_escape(text)) end - local cursor_color = gcolor.ensure_pango_color(args.cursor_color) - local text_color = gcolor.ensure_pango_color(args.text_color) - - if args.highlighter then - text_start, text_end = args.highlighter(text_start, text_end) - end - - ret = _prompt .. text_start .. "" .. char .. "" .. text_end .. spacer - - return ret + self:set_markup(markup) end -local function update(self) - self.textbox:set_font(self.font) - self.textbox:set_markup(prompt_text_with_cursor{ - text = self.command, text_color = self.fg_cursor, cursor_color = self.bg_cursor, - cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all, - prompt = self.prompt, highlighter = self.highlighter }) +local function paste(self) + local wp = self._private + + awful.spawn.easy_async_with_shell("xclip -selection clipboard -o", function(stdout) + if stdout ~= nil then + local n = stdout:find("\n") + if n then + stdout = stdout:sub(1, n - 1) + end + + wp.text = wp.text:sub(1, wp.cur_pos - 1) .. stdout .. self.text:sub(wp.cur_pos) + wp.cur_pos = wp.cur_pos + #stdout + generate_markup(self, true) + end + end) end -local function exec(self, cb, command_to_history) - self.textbox:set_markup("") - history_add(self.history_path, command_to_history) - keygrabber.stop(self._private.grabber) - if cb then cb(self.command) end - if self.done_callback then - self.done_callback() +local function build_properties(prototype, prop_names) + for _, prop in ipairs(prop_names) do + if not prototype["set_" .. prop] then + prototype["set_" .. prop] = function(self, value) + if self._private[prop] ~= value then + self._private[prop] = value + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::" .. prop, value) + generate_markup(self, self._private.state) + end + return self + end + end + if not prototype["get_" .. prop] then + prototype["get_" .. prop] = function(self) + return self._private[prop] + end + end end end +function prompt:toggle_obscure() + self:set_obscure(not self._private.obscure) +end + +function prompt:set_text(text) + self._private.text = text + self._private.cur_pos = #text + 1 + generate_markup(self, self._private.state) +end + +function prompt:get_text() + return self._private.text +end + function prompt:start() - -- The cursor position - if self.reset_on_stop == true or self._private_cur_pos == nil then - self._private_cur_pos = (self.select_all and 1) or self.text:wlen() + 1 - end - if self.reset_on_stop == true then self.text = "" self.command = "" end + local wp = self._private + wp.state = true - self.textbox:set_font(self.font) - self.textbox:set_markup(prompt_text_with_cursor{ - text = self.reset_on_stop and self.text or self.command, text_color = self.fg_cursor, cursor_color = self.bg_cursor, - cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all, - prompt = self.prompt, highlighter = self.highlighter}) + capi.awesome.emit_signal("prompt::toggled_on", self) + generate_markup(self, true) - self._private.search_term = nil - - history_check_load(self.history_path, self.history_max) - local history_index = history_items(self.history_path) + 1 - - -- The completion element to use on completion request. - local ncomp = 1 - - local command_before_comp - local cur_pos_before_comp - - self._private.grabber = keygrabber.run(function(modifiers, key, event) + wp.grabber = awful.keygrabber.run(function(modifiers, key, event) -- Convert index array to hash table local mod = {} - for _, v in ipairs(modifiers) do mod[v] = true end + for _, v in ipairs(modifiers) do + mod[v] = true + end if event ~= "press" then - if self.keyreleased_callback then - self.keyreleased_callback(mod, key, self.command) - end + self:emit_signal("key::release", mod, key, wp.text) return end - -- Call the user specified callback. If it returns true as - -- the first result then return from the function. Treat the - -- second and third results as a new command and new prompt - -- to be set (if provided) - if self.keypressed_callback then - local user_catched, new_command, new_prompt = - self.keypressed_callback(mod, key, self.command) - if new_command or new_prompt then - if new_command then - self.command = new_command - end - if new_prompt then - self.prompt = new_prompt - end - update(self) - end - if user_catched then - if self.changed_callback then - self.changed_callback(self.command) - end - return - end - end - - local filtered_modifiers = {} - - -- User defined cases - if self.hooks[key] then - -- Remove caps and num lock - for _, m in ipairs(modifiers) do - if not gtable.hasitem(akey.ignore_modifiers, m) then - table.insert(filtered_modifiers, m) - end - end - - for _,v in ipairs(self.hooks[key]) do - if #filtered_modifiers == #v[1] then - local match = true - for _,v2 in ipairs(v[1]) do - match = match and mod[v2] - end - if match then - local cb - local ret, quit = v[3](self.command) - local original_command = self.command - - -- Support both a "simple" and a "complex" way to - -- control if the prompt should quit. - quit = quit == nil and (ret ~= true) or (quit~=false) - - -- Allow the callback to change the command - self.command = (ret ~= true) and ret or self.command - - -- Quit by default, but allow it to be disabled - if ret and type(ret) ~= "boolean" then - cb = self.exe_callback - if not quit then - self._private_cur_pos = ret:wlen() + 1 - update(self) - end - elseif quit then - -- No callback. - cb = function() end - end - - -- Execute the callback - if cb then - exec(self, cb, original_command) - end - - return - end - end - end - end - - -- Get out cases - if (mod.Control and (key == "c" or key == "g")) - or (not mod.Control and key == "Escape") then - self:stop() - return false - elseif (mod.Control and (key == "j" or key == "m")) - -- or (not mod.Control and key == "Return") - -- or (not mod.Control and key == "KP_Enter") - then - exec(self, self.exe_callback, self.command) - -- We already unregistered ourselves so we don't want to return - -- true, otherwise we may unregister someone else. - return - end + self:emit_signal("key::press", mod, key, wp.text) -- Control cases if mod.Control then - self.select_all = nil if key == "v" then - local selection = capi.selection() - if selection then - -- Remove \n - local n = selection:find("\n") - if n then - selection = selection:sub(1, n - 1) - end - self.command = self.command:sub(1, self._private_cur_pos - 1) .. selection .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = self._private_cur_pos + #selection - end + paste(self) elseif key == "a" then - self._private_cur_pos = 1 + wp.cur_pos = 1 elseif key == "b" then - if self._private_cur_pos > 1 then - self._private_cur_pos = self._private_cur_pos - 1 - if have_multibyte_char_at(self.command, self._private_cur_pos) then - self._private_cur_pos = self._private_cur_pos - 1 + if wp.cur_pos > 1 then + wp.cur_pos = wp.cur_pos - 1 + if have_multibyte_char_at(wp.text, wp.cur_pos) then + wp.cur_pos = wp.cur_pos - 1 end end elseif key == "d" then - if self._private_cur_pos <= #self.command then - self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(self._private_cur_pos + 1) - end - elseif key == "p" then - if history_index > 1 then - history_index = history_index - 1 - - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - end - elseif key == "n" then - if history_index < history_items(self.history_path) then - history_index = history_index + 1 - - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - elseif history_index == history_items(self.history_path) then - history_index = history_index + 1 - - self.command = "" - self._private_cur_pos = 1 + if wp.cur_pos <= #wp.text then + wp.text = wp.text:sub(1, wp.cur_pos - 1) .. wp.text:sub(wp.cur_pos + 1) end elseif key == "e" then - self._private_cur_pos = #self.command + 1 - elseif key == "r" then - self._private.search_term = self._private.search_term or self.command:sub(1, self._private_cur_pos - 1) - for i,v in (function(a,i) return itera(-1,a,i) end), data.history[self.history_path].table, history_index do - if v:find(self._private.search_term,1,true) ~= nil then - self.command=v - history_index=i - self._private_cur_pos=#self.command+1 - break - end - end - elseif key == "s" then - self._private.search_term = self._private.search_term or self.command:sub(1, self._private_cur_pos - 1) - for i,v in (function(a,i) return itera(1,a,i) end), data.history[self.history_path].table, history_index do - if v:find(self._private.search_term,1,true) ~= nil then - self.command=v - history_index=i - self._private_cur_pos=#self.command+1 - break - end - end + wp.cur_pos = #wp.text + 1 elseif key == "f" then - if self._private_cur_pos <= #self.command then - if have_multibyte_char_at(self.command, self._private_cur_pos) then - self._private_cur_pos = self._private_cur_pos + 2 + if wp.cur_pos <= #wp.text then + if have_multibyte_char_at(wp.text, wp.cur_pos) then + wp.cur_pos = wp.cur_pos + 2 else - self._private_cur_pos = self._private_cur_pos + 1 + wp.cur_pos = wp.cur_pos + 1 end end elseif key == "h" then - if self._private_cur_pos > 1 then + if wp.cur_pos > 1 then local offset = 0 - if have_multibyte_char_at(self.command, self._private_cur_pos - 1) then + if have_multibyte_char_at(wp.text, wp.cur_pos - 1) then offset = 1 end - self.command = self.command:sub(1, self._private_cur_pos - 2 - offset) .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = self._private_cur_pos - 1 - offset + wp.text = wp.text:sub(1, wp.cur_pos - 2 - offset) .. wp.text:sub(wp.cur_pos) + wp.cur_pos = wp.cur_pos - 1 - offset end elseif key == "k" then - self.command = self.command:sub(1, self._private_cur_pos - 1) + wp.text = wp.text:sub(1, wp.cur_pos - 1) elseif key == "u" then - self.command = self.command:sub(self._private_cur_pos, #self.command) - self._private_cur_pos = 1 - elseif key == "Prior" then - self._private.search_term = self.command:sub(1, self._private_cur_pos - 1) or "" - for i,v in (function(a,i) return itera(-1,a,i) end), data.history[self.history_path].table, history_index do - if v:find(self._private.search_term,1,true) == 1 then - self.command=v - history_index=i - break - end - end - elseif key == "Next" then - self._private.search_term = self.command:sub(1, self._private_cur_pos - 1) or "" - for i,v in (function(a,i) return itera(1,a,i) end), data.history[self.history_path].table, history_index do - if v:find(self._private.search_term,1,true) == 1 then - self.command=v - history_index=i - break - end - end + wp.text = wp.text:sub(wp.cur_pos, #wp.text) + wp.cur_pos = 1 elseif key == "w" or key == "BackSpace" then local wstart = 1 local wend = 1 local cword_start_pos = 1 local cword_end_pos = 1 - while wend < self._private_cur_pos do - wend = self.command:find("[{[(,.:;_-+=@/ ]", wstart) - if not wend then wend = #self.command + 1 end - if self._private_cur_pos >= wstart and self._private_cur_pos <= wend + 1 then + while wend < wp.cur_pos do + wend = wp.text:find("[{[(,.:;_-+=@/ ]", wstart) + if not wend then + wend = #wp.text + 1 + end + if wp.cur_pos >= wstart and wp.cur_pos <= wend + 1 then cword_start_pos = wstart - cword_end_pos = self._private_cur_pos - 1 + cword_end_pos = wp.cur_pos - 1 break end wstart = wend + 1 end - self.command = self.command:sub(1, cword_start_pos - 1) .. self.command:sub(cword_end_pos + 1) - self._private_cur_pos = cword_start_pos - elseif key == "Delete" then - -- delete from history only if: - -- we are not dealing with a new command - -- the user has not edited an existing entry - if self.command == data.history[self.history_path].table[history_index] then - table.remove(data.history[self.history_path].table, history_index) - if history_index <= history_items(self.history_path) then - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - elseif history_index > 1 then - history_index = history_index - 1 - - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - else - self.command = "" - self._private_cur_pos = 1 - end - end + wp.text = wp.text:sub(1, cword_start_pos - 1) .. wp.text:sub(cword_end_pos + 1) + wp.cur_pos = cword_start_pos end elseif mod.Mod1 or mod.Mod3 then if key == "b" then - self._private_cur_pos = cword_start(self.command, self._private_cur_pos) + wp.cur_pos = cword_start(wp.text, wp.cur_pos) elseif key == "f" then - self._private_cur_pos = cword_end(self.command, self._private_cur_pos) + wp.cur_pos = cword_end(wp.text, wp.cur_pos) elseif key == "d" then - self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(cword_end(self.command, self._private_cur_pos)) + wp.text = wp.text:sub(1, wp.cur_pos - 1) .. wp.text:sub(cword_end(wp.text, wp.cur_pos)) elseif key == "BackSpace" then - local wstart = cword_start(self.command, self._private_cur_pos) - self.command = self.command:sub(1, wstart - 1) .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = wstart + local wstart = cword_start(wp.text, wp.cur_pos) + wp.text = wp.text:sub(1, wstart - 1) .. wp.text:sub(wp.cur_pos) + wp.cur_pos = wstart end else - if self.completion_callback then - if key == "Tab" or key == "ISO_Left_Tab" then - if key == "ISO_Left_Tab" or mod.Shift then - if ncomp == 1 then return end - if ncomp == 2 then - self.command = command_before_comp - self.textbox:set_font(self.font) - self.textbox:set_markup(prompt_text_with_cursor{ - text = command_before_comp, text_color = self.fg_cursor, cursor_color = self.bg_cursor, - cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all, - prompt = self.prompt }) - self._private_cur_pos = cur_pos_before_comp - ncomp = 1 - return - end - - ncomp = ncomp - 2 - elseif ncomp == 1 then - command_before_comp = self.command - cur_pos_before_comp = self._private_cur_pos - end - local matches - self.command, self._private_cur_pos, matches = self.completion_callback(command_before_comp, cur_pos_before_comp, ncomp) - ncomp = ncomp + 1 - key = "" - -- execute if only one match found and autoexec flag set - if matches and #matches == 1 and args.autoexec then - exec(self, self.exe_callback) - return - end - elseif key ~= "Shift_L" and key ~= "Shift_R" then - ncomp = 1 - end - end - - -- Typin cases - if mod.Shift and key == "Insert" then - local selection = capi.selection() - if selection then - -- Remove \n - local n = selection:find("\n") - if n then - selection = selection:sub(1, n - 1) - end - self.command = self.command:sub(1, self._private_cur_pos - 1) .. selection .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = self._private_cur_pos + #selection + if key == "Escape" or key == "Return" then + if self.always_on == false then + self:stop() + return end + elseif mod.Shift and key == "Insert" then + paste(self) elseif key == "Home" then - self._private_cur_pos = 1 + wp.cur_pos = 1 elseif key == "End" then - self._private_cur_pos = #self.command + 1 + wp.cur_pos = #wp.text + 1 elseif key == "BackSpace" then - if self._private_cur_pos > 1 then + if wp.cur_pos > 1 then local offset = 0 - if have_multibyte_char_at(self.command, self._private_cur_pos - 1) then + if have_multibyte_char_at(wp.text, wp.cur_pos - 1) then offset = 1 end - self.command = self.command:sub(1, self._private_cur_pos - 2 - offset) .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = self._private_cur_pos - 1 - offset + wp.text = wp.text:sub(1, wp.cur_pos - 2 - offset) .. wp.text:sub(wp.cur_pos) + wp.cur_pos = wp.cur_pos - 1 - offset end elseif key == "Delete" then - self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(self._private_cur_pos + 1) + wp.text = wp.text:sub(1, wp.cur_pos - 1) .. wp.text:sub(wp.cur_pos + 1) elseif key == "Left" then - self._private_cur_pos = self._private_cur_pos - 1 + wp.cur_pos = wp.cur_pos - 1 elseif key == "Right" then - self._private_cur_pos = self._private_cur_pos + 1 - elseif key == "Prior" then - if history_index > 1 then - history_index = history_index - 1 - - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - end - elseif key == "Next" then - if history_index < history_items(self.history_path) then - history_index = history_index + 1 - - self.command = data.history[self.history_path].table[history_index] - self._private_cur_pos = #self.command + 2 - elseif history_index == history_items(self.history_path) then - history_index = history_index + 1 - - self.command = "" - self._private_cur_pos = 1 - end + wp.cur_pos = wp.cur_pos + 1 else + if wp.round and key == "." then + return + end + if wp.only_numbers and tonumber(wp.text .. key) == nil then + return + end + -- wlen() is UTF-8 aware but #key is not, -- so check that we have one UTF-8 char but advance the cursor of # position if key:wlen() == 1 then - if self.select_all then self.command = "" end - self.command = self.command:sub(1, self._private_cur_pos - 1) .. key .. self.command:sub(self._private_cur_pos) - self._private_cur_pos = self._private_cur_pos + #key + wp.text = wp.text:sub(1, wp.cur_pos - 1) .. key .. wp.text:sub(wp.cur_pos) + wp.cur_pos = wp.cur_pos + #key end end - if self._private_cur_pos < 1 then - self._private_cur_pos = 1 - elseif self._private_cur_pos > #self.command + 1 then - self._private_cur_pos = #self.command + 1 + if wp.cur_pos < 1 then + wp.cur_pos = 1 + elseif wp.cur_pos > #wp.text + 1 then + wp.cur_pos = #wp.text + 1 end - self.select_all = nil end - update(self) - if self.changed_callback then - self.changed_callback(self.command) + if wp.only_numbers and wp.text == "" then + wp.text = "0" + wp.cur_pos = #wp.text + 1 end + + generate_markup(self, true) + self:emit_signal("text::changed", wp.text) end) end function prompt:stop() - keygrabber.stop(self._private.grabber) - history_save(self.history_path) - if self.done_callback then self.done_callback() end - return false + local wp = self._private + wp.state = false + + if self.reset_on_stop == true or wp.cur_pos == nil then + wp.cur_pos = wp.text:wlen() + 1 + end + if self.reset_on_stop == true then + wp.text = "" + end + + awful.keygrabber.stop(wp.grabber) + generate_markup(self, false) + + self:emit_signal("stopped", wp.text) end -local function new(args) - args = args or {} +function prompt:toggle() + local wp = self._private - args.command = args.text or "" - args.prompt = args.prompt or "" - args.text = args.text or "" - args.font = args.font or beautiful.prompt_font or beautiful.font - args.bg_cursor = args.bg_cursor or beautiful.prompt_bg_cursor or beautiful.bg_focus or "white" - args.fg_cursor = args.fg_cursor or beautiful.prompt_fg_cursor or beautiful.fg_focus or "black" - args.ul_cursor = args.ul_cursor or nil - args.reset_on_stop = args.reset_on_stop == nil and true or args.reset_on_stop - args.select_all = args.select_all or nil - args.highlighter = args.highlighter or nil - args.hooks = args.hooks or {} - args.keypressed_callback = args.keypressed_callback or nil - args.changed_callback = args.changed_callback or nil - args.done_callback = args.done_callback or nil - args.history_max = args.history_max or nil - args.history_path = args.history_path or nil - args.completion_callback = args.completion_callback or nil - args.exe_callback = args.exe_callback or nil - args.textbox = args.textbox or wibox.widget.textbox() - - -- Build the hook map - local hooks = {} - for _,v in ipairs(args.hooks) do - if #v == 3 then - local _,key,callback = unpack(v) - if type(callback) == "function" then - hooks[key] = hooks[key] or {} - hooks[key][#hooks[key]+1] = v - else - gdebug.print_warning("The hook's 3rd parameter has to be a function.") - end - else - gdebug.print_warning("The hook has to have 3 parameters.") - end + if wp.state == true then + self:stop() + else + self:start() end - args.hooks = hooks +end - local ret = gobject({}) - ret._private = {} - gtable.crush(ret, prompt) - gtable.crush(ret, args) +local function new() + local widget = wibox.widget.textbox() + gtable.crush(widget, prompt, true) - return ret + local wp = widget._private + + wp.only_numbers = false + wp.round = false + wp.always_on = false + wp.reset_on_stop = false + wp.obscure = false + wp.stop_on_focus_lost = true + wp.stop_on_tag_changed = true + wp.stop_on_clicked_outside = true + + wp.icon_font = beautiful.font + wp.icon_size = 12 + wp.icon_color = beautiful.colors.on_background + wp.icon = nil + + wp.label_font = beautiful.font + wp.label_size = 12 + wp.label_color = beautiful.colors.on_background + wp.label = "" + + wp.text_font = beautiful.font + wp.text_size = 12 + wp.text_color = beautiful.colors.on_background + wp.text = "" + + wp.cursor_size = 4 + wp.cursor_color = beautiful.colors.on_background + + wp.cur_pos = #wp.text + 1 or 1 + wp.state = false + + widget:connect_signal("mouse::enter", function(self, find_widgets_result) + capi.root.cursor("xterm") + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = "xterm" + end + end) + + widget:connect_signal("mouse::leave", function() + capi.root.cursor("left_ptr") + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = "left_ptr" + end + + if wp.stop_on_focus_lost ~= false and wp.always_on == false and wp.state == true then + widget:stop() + end + end) + + widget:connect_signal("button::press", function(self, lx, ly, button, mods, find_widgets_result) + if wp.always_on then + return + end + + if button == 1 then + widget:toggle() + end + end) + + -- TODO make it work outside my config + capi.awesome.connect_signal("root::pressed", function() + if wp.stop_on_clicked_outside ~= false and wp.always_on == false and wp.state == true then + widget:stop() + end + end) + + capi.client.connect_signal("button::press", function() + if wp.stop_on_clicked_outside ~= false and wp.always_on == false and wp.state == true then + widget:stop() + end + end) + + capi.tag.connect_signal("property::selected", function() + if wp.stop_on_tag_changed ~= false and wp.always_on == false and wp.state == true then + widget:stop() + end + end) + + capi.awesome.connect_signal("prompt::toggled_on", function(prompt) + if wp.always_on == false and prompt ~= widget and wp.state == true then + widget:stop() + end + end) + + return widget end function prompt.mt:__call(...) return new(...) end -return setmetatable(prompt, prompt.mt) \ No newline at end of file +build_properties(prompt, properties) + +return setmetatable(prompt, prompt.mt)