From 52acaf5d79281de8808b9f5870a5e5dabb4861bb Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 00:20:42 +0200 Subject: [PATCH 001/185] Some renaming and use _ for unused vars --- widget/app_launcher/init.lua | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4cc6a557..9ff00600 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -14,7 +14,6 @@ local table = table local math = math local ipairs = ipairs local pairs = pairs -local root = root local capi = { screen = screen, mouse = mouse } local path = ... @@ -277,7 +276,7 @@ local function search(self, text) if text == "" then self._private.matched_entries = self._private.all_entries else - for index, entry in pairs(self._private.all_entries) do + for _, entry in pairs(self._private.all_entries) do text = text:gsub( "%W", "" ) -- Check if there's a match by the app name or app command @@ -300,7 +299,7 @@ local function search(self, text) return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) end) end - for index, entry in pairs(self._private.matched_entries) do + for _, entry in pairs(self._private.matched_entries) do -- Only add the widgets for apps that are part of the first page if #self._private.grid.children + 1 <= self._private.max_apps_per_page then self._private.grid:add(create_app_widget(self, entry)) @@ -430,12 +429,12 @@ local function scroll_up(self) return end - local rows, columns = self._private.grid:get_dimension() + local rows, _ = self._private.grid:get_dimension() local pos = self._private.grid:get_widget_position(self._private.active_widget) - local is_bigger_than_first_app = pos.col > 1 or pos.row > 1 + local can_scroll_up = pos.col > 1 or pos.row > 1 -- Check if the current marked app is not the first - if is_bigger_than_first_app then + if can_scroll_up then unselect_app(self) if pos.row == 1 then select_app(self, rows, pos.col - 1) @@ -453,12 +452,12 @@ local function scroll_down(self) return end - local rows, columns = self._private.grid:get_dimension() + local rows, _ = self._private.grid:get_dimension() local pos = self._private.grid:get_widget_position(self._private.active_widget) - local is_less_than_max_app = self._private.grid:index(self._private.active_widget) < #self._private.grid.children + local can_scroll_down = self._private.grid:index(self._private.active_widget) < #self._private.grid.children -- Check if we can scroll down the app list - if is_less_than_max_app then + if can_scroll_down then -- Unmark the previous app unselect_app(self) if pos.row == rows then @@ -478,10 +477,9 @@ local function scroll_left(self) end local pos = self._private.grid:get_widget_position(self._private.active_widget) - local is_bigger_than_first_column = pos.col > 1 + local can_scroll_left = pos.col > 1 - -- Check if the current marked app is not the first - if is_bigger_than_first_column then + if can_scroll_left then unselect_app(self) select_app(self, pos.row, pos.col - 1) else @@ -495,16 +493,14 @@ local function scroll_right(self) return end - local rows, columns = self._private.grid:get_dimension() + local _, columns = self._private.grid:get_dimension() local pos = self._private.grid:get_widget_position(self._private.active_widget) - local is_less_than_max_column = pos.col < columns + local can_scrol_right = pos.col < columns - -- Check if we can scroll down the app list - if is_less_than_max_column then - -- Unmark the previous app + if can_scrol_right then unselect_app(self) - -- Scroll up to the max app if there are directly to the right of previous app + -- Check for a case where the last column has less rows than the previous column if self._private.grid:get_widgets_at(pos.row, pos.col + 1) == nil then local app = self._private.grid.children[#self._private.grid.children] pos = self._private.grid:get_widget_position(app) @@ -512,7 +508,6 @@ local function scroll_right(self) else select_app(self, pos.row, pos.col + 1) end - else page_forward(self, "right") end From 079038252469e0a70b51f28f46587c287864f767 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 00:42:18 +0200 Subject: [PATCH 002/185] Use forced_num_col so I don't need to do dumb shit to get the actual amount of rows --- widget/app_launcher/init.lua | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9ff00600..6aef106a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -413,12 +413,9 @@ local function page_forward(self, direction) if direction == "down" then select_app(self, 1, 1) else - local last_col_max_row = math.min(pos.row, #self._private.grid.children % self.apps_per_row) - if last_col_max_row ~= 0 then - select_app(self, last_col_max_row, 1) - else - select_app(self, pos.row, 1) - end + local rows, _ = self._private.grid:get_dimension() + local row = math.min(pos.row, rows) + select_app(self, row, 1) end end end @@ -903,11 +900,11 @@ local function new(args) layout = wibox.layout.grid, forced_width = grid_width, forced_height = grid_height, - orientation = "horizontal", + orientation = "vertical", homogeneous = true, expand = ret.expand_apps, spacing = ret.apps_spacing, - forced_num_rows = ret.apps_per_row, + forced_num_cols = ret.apps_per_column, buttons = { awful.button({}, 4, function() scroll_up(ret) end), From 611e382ff9fdedd76fc6a3907d6c9efe40467f6e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 00:44:24 +0200 Subject: [PATCH 003/185] I like page_forward being before backward --- widget/app_launcher/init.lua | 80 ++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 6aef106a..9dbd0ec3 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -330,15 +330,21 @@ local function search(self, text) end end -local function page_backward(self, direction) - if self._private.current_page > 1 then - self._private.current_page = self._private.current_page - 1 +local function page_forward(self, direction) + local min_app_index_to_include = 0 + local max_app_index_to_include = self._private.apps_per_page + + if self._private.current_page < self._private.pages_count then + min_app_index_to_include = self._private.apps_per_page * self._private.current_page + self._private.current_page = self._private.current_page + 1 + max_app_index_to_include = self._private.apps_per_page * self._private.current_page elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then - self._private.current_page = self._private.pages_count + self._private.current_page = 1 + min_app_index_to_include = 0 + max_app_index_to_include = self._private.apps_per_page elseif self.wrap_app_scrolling then - local rows, columns = self._private.grid:get_dimension() unselect_app(self) - select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) + select_app(self, 1, 1) return else return @@ -349,9 +355,6 @@ local function page_backward(self, direction) -- Remove the current page apps from the grid self._private.grid:reset() - local max_app_index_to_include = self._private.apps_per_page * self._private.current_page - local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - for index, entry in pairs(self._private.matched_entries) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then @@ -359,39 +362,26 @@ local function page_backward(self, direction) end end - local rows, columns = self._private.grid:get_dimension() - if self._private.current_page < self._private.pages_count then - if direction == "up" then - select_app(self, rows, columns) - else - -- Keep the same row from last page - select_app(self, pos.row, columns) - end - elseif self.wrap_page_scrolling then - if direction == "up" then - select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) + if self._private.current_page > 1 or self.wrap_page_scrolling then + if direction == "down" then + select_app(self, 1, 1) else - -- Keep the same row from last page - select_app(self, math.min(pos.row, #self._private.grid.children % self.apps_per_row), columns) + local rows, _ = self._private.grid:get_dimension() + local row = math.min(pos.row, rows) + select_app(self, row, 1) end end end -local function page_forward(self, direction) - local min_app_index_to_include = 0 - local max_app_index_to_include = self._private.apps_per_page - - if self._private.current_page < self._private.pages_count then - min_app_index_to_include = self._private.apps_per_page * self._private.current_page - self._private.current_page = self._private.current_page + 1 - max_app_index_to_include = self._private.apps_per_page * self._private.current_page +local function page_backward(self, direction) + if self._private.current_page > 1 then + self._private.current_page = self._private.current_page - 1 elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then - self._private.current_page = 1 - min_app_index_to_include = 0 - max_app_index_to_include = self._private.apps_per_page + self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then + local rows, columns = self._private.grid:get_dimension() unselect_app(self) - select_app(self, 1, 1) + select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) return else return @@ -402,6 +392,9 @@ local function page_forward(self, direction) -- Remove the current page apps from the grid self._private.grid:reset() + local max_app_index_to_include = self._private.apps_per_page * self._private.current_page + local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page + for index, entry in pairs(self._private.matched_entries) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then @@ -409,13 +402,20 @@ local function page_forward(self, direction) end end - if self._private.current_page > 1 or self.wrap_page_scrolling then - if direction == "down" then - select_app(self, 1, 1) + local rows, columns = self._private.grid:get_dimension() + if self._private.current_page < self._private.pages_count then + if direction == "up" then + select_app(self, rows, columns) else - local rows, _ = self._private.grid:get_dimension() - local row = math.min(pos.row, rows) - select_app(self, row, 1) + -- Keep the same row from last page + select_app(self, pos.row, columns) + end + elseif self.wrap_page_scrolling then + if direction == "up" then + select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) + else + -- Keep the same row from last page + select_app(self, math.min(pos.row, #self._private.grid.children % self.apps_per_row), columns) end end end From a8a865b3fa3940aab8b8e95017ccabf2e1744c66 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 00:47:12 +0200 Subject: [PATCH 004/185] Auto detect terminal --- docs/widgets/app_launcher.md | 1 - widget/app_launcher/awesome-sensible-terminal | 25 +++++++++++++++++++ widget/app_launcher/init.lua | 21 +++------------- 3 files changed, 28 insertions(+), 19 deletions(-) create mode 100755 widget/app_launcher/awesome-sensible-terminal diff --git a/docs/widgets/app_launcher.md b/docs/widgets/app_launcher.md index b0c1ba40..332fce4f 100644 --- a/docs/widgets/app_launcher.md +++ b/docs/widgets/app_launcher.md @@ -35,7 +35,6 @@ local app_launcher = bling.widget.app_launcher(args) ```lua local args = { - terminal = "alacritty" -- Set default terminal favorites = { "firefox", "wezterm" } -- Favorites are given priority and are bubbled to top of the list search_commands = true -- Search by app name AND commandline command skip_names = { "Discord" } -- List of apps to omit from launcher diff --git a/widget/app_launcher/awesome-sensible-terminal b/widget/app_launcher/awesome-sensible-terminal new file mode 100755 index 00000000..4e47296c --- /dev/null +++ b/widget/app_launcher/awesome-sensible-terminal @@ -0,0 +1,25 @@ +#!/bin/sh +# Based on i3-sensible-terminal + +# +# This code is released in public domain by Han Boetes +# +# This script tries to exec a terminal emulator by trying some known terminal +# emulators. +# +# We welcome patches that add distribution-specific mechanisms to find the +# preferred terminal emulator. On Debian, there is the x-terminal-emulator +# symlink for example. +# +# Invariants: +# 1. $TERMINAL must come first +# 2. Distribution-specific mechanisms come next, e.g. x-terminal-emulator +# 3. The terminal emulator with best accessibility comes first. +# 4. No order is guaranteed/desired for the remaining terminal emulators. +for terminal in "$TERMINAL" termite hyper wezterm alacritty kitty x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm lxterminal terminology st qterminal lilyterm tilix terminix konsole guake tilda; do + if command -v "$terminal" > /dev/null 2>&1; then + exec "$terminal" "$@" + fi +done + +awesome-client 'local naughty = require("naughty"); naughty.notification { message = "awesome-sensible-terminal could not find a terminal emulator. Please install one." }' diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9dbd0ec3..a96d415b 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -19,13 +19,8 @@ local path = ... local app_launcher = { mt = {} } -local terminal_commands_lookup = -{ - alacritty = "alacritty -e", - termite = "termite -e", - rxvt = "rxvt -e", - terminator = "terminator -e" -} +local AWESOME_SENSIBLE_TERMINAL_PATH = debug.getinfo(1).source:match("@?(.*/)") .. + "awesome-sensible-terminal" local function string_levenshtein(str1, str2) local len1 = string.len(str1) @@ -191,16 +186,7 @@ local function create_app_widget(self, entry) function app.spawn() if entry.terminal == true then - if self.terminal ~= nil then - local terminal_command = terminal_commands_lookup[self.terminal] or self.terminal - awful.spawn(terminal_command .. " " .. entry.executable) - else - awful.spawn.easy_async("gtk-launch " .. entry.executable, function(stdout, stderr) - if stderr then - awful.spawn(entry.executable) - end - end) - end + awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. entry.executable) else awful.spawn(entry.executable) end @@ -741,7 +727,6 @@ end local function new(args) args = args or {} - args.terminal = args.terminal or nil args.favorites = args.favorites or {} args.search_commands = args.search_commands == nil and true or args.search_commands args.skip_names = args.skip_names or {} From 6c575011cbc1d2cf30d5128b23e9417c4d9c1b9b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 00:53:50 +0200 Subject: [PATCH 005/185] Pull these out of the function --- widget/app_launcher/init.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index a96d415b..aa4e445f 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -19,6 +19,8 @@ local path = ... local app_launcher = { mt = {} } +local KILL_OLD_INOTIFY_SCRIPT = [[ ps x | grep "inotifywait -e modify /usr/share/applications" | grep -v grep | awk '{print $1}' | xargs kill ]] +local INOTIFY_SCRIPT = [[ bash -c "while (inotifywait -e modify /usr/share/applications -qq) do echo; done" ]] local AWESOME_SENSIBLE_TERMINAL_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "awesome-sensible-terminal" @@ -996,11 +998,8 @@ local function new(args) ) end - local kill_old_inotify_process_script = [[ ps x | grep "inotifywait -e modify /usr/share/applications" | grep -v grep | awk '{print $1}' | xargs kill ]] - local subscribe_script = [[ bash -c "while (inotifywait -e modify /usr/share/applications -qq) do echo; done" ]] - - awful.spawn.easy_async_with_shell(kill_old_inotify_process_script, function() - awful.spawn.with_line_callback(subscribe_script, {stdout = function(_) + awful.spawn.easy_async_with_shell(KILL_OLD_INOTIFY_SCRIPT, function() + awful.spawn.with_line_callback(INOTIFY_SCRIPT, {stdout = function(_) generate_apps(ret) end}) end) From 61a21a125a256cc73d9411e4bd93e146659d65e7 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 01:28:51 +0200 Subject: [PATCH 006/185] Small changes --- widget/app_launcher/init.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index aa4e445f..736d9a4a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -186,7 +186,7 @@ local function create_app_widget(self, entry) } } - function app.spawn() + function app:spawn() if entry.terminal == true then awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. entry.executable) else @@ -232,11 +232,10 @@ local function create_app_widget(self, entry) end end) - app:connect_signal("button::press", function(_self, lx, ly, button, mods, find_widgets_result) + app:connect_signal("button::press", function(app, _, __, button) if button == 1 then - local app = _self if self._private.active_widget == app or not self.select_before_spawn then - app.spawn() + app:spawn() else -- Unmark the previous app unselect_app(self) @@ -865,7 +864,7 @@ local function new(args) end if key == "Return" then if ret._private.active_widget ~= nil then - ret._private.active_widget.spawn() + ret._private.active_widget:spawn() end end if key == "Up" then From c6a245365b4ece4527d60af26a9b69e742343b9a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 01:35:00 +0200 Subject: [PATCH 007/185] Use _role suffix --- widget/app_launcher/init.lua | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 736d9a4a..cc5a90f9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -95,12 +95,12 @@ local function select_app(self, x, y) self._private.active_widget = widgets[1] if self._private.active_widget ~= nil then self._private.active_widget.selected = true - self._private.active_widget:get_children_by_id("background")[1].bg = self.app_selected_color - local name_widget = self._private.active_widget:get_children_by_id("name")[1] + self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color + local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] if name_widget then name_widget.markup = string.format("%s", self.app_name_selected_color, name_widget.text) end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name")[1] + local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] if generic_name_widget then generic_name_widget.markup = string.format("%s", self.app_name_selected_color, generic_name_widget.text) end @@ -111,12 +111,12 @@ end local function unselect_app(self) if self._private.active_widget ~= nil then self._private.active_widget.selected = false - self._private.active_widget:get_children_by_id("background")[1].bg = self.app_normal_color - local name_widget = self._private.active_widget:get_children_by_id("name")[1] + self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color + local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] if name_widget then name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name")[1] + local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] if generic_name_widget then generic_name_widget.markup = string.format("%s", self.app_name_normal_color, generic_name_widget.text) end @@ -128,6 +128,7 @@ local function create_app_widget(self, entry) local icon = self.app_show_icon == true and { widget = wibox.widget.imagebox, + id = "icon_role", halign = self.app_icon_halign, forced_width = self.app_icon_width, forced_height = self.app_icon_height, @@ -137,7 +138,7 @@ local function create_app_widget(self, entry) local name = self.app_show_name == true and { widget = wibox.widget.textbox, - id = "name", + id = "name_role", font = self.app_name_font, markup = string.format("%s", self.app_name_normal_color, entry.name) } or nil @@ -145,7 +146,7 @@ local function create_app_widget(self, entry) local generic_name = entry.generic_name ~= nil and self.app_show_generic_name == true and { widget = wibox.widget.textbox, - id = "generic_name", + id = "generic_name_role", font = self.app_name_font, markup = entry.generic_name ~= "" and " (" .. entry.generic_name .. ")" or "" } or nil @@ -153,7 +154,7 @@ local function create_app_widget(self, entry) local app = wibox.widget { widget = wibox.container.background, - id = "background", + id = "background_role", forced_width = self.app_width, forced_height = self.app_height, shape = self.app_shape, @@ -206,7 +207,7 @@ local function create_app_widget(self, entry) local app = _self if app.selected then - app:get_children_by_id("background")[1].bg = self.app_selected_hover_color + app:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color else local is_opaque = color.is_opaque(self.app_normal_color) local is_dark = color.is_dark(self.app_normal_color) @@ -214,7 +215,7 @@ local function create_app_widget(self, entry) local hover_color = (is_dark or is_opaque) and color.rgba_to_hex(color.multiply(app_normal_color, 2.5)) or color.rgba_to_hex(color.multiply(app_normal_color, 0.5)) - app:get_children_by_id("background")[1].bg = self.app_normal_hover_color + app:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color end end) @@ -226,9 +227,9 @@ local function create_app_widget(self, entry) local app = _self if app.selected then - app:get_children_by_id("background")[1].bg = self.app_selected_color + app:get_children_by_id("background_role")[1].bg = self.app_selected_color else - app:get_children_by_id("background")[1].bg = self.app_normal_color + app:get_children_by_id("background_role")[1].bg = self.app_normal_color end end) From 6f1198e75d862090a1468e97b15d677a8ad51a0b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 02:17:46 +0200 Subject: [PATCH 008/185] Add support for passing in a template for the app widget --- widget/app_launcher/init.lua | 241 +++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 110 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index cc5a90f9..f659d8e4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -94,146 +94,155 @@ local function select_app(self, x, y) if widgets then self._private.active_widget = widgets[1] if self._private.active_widget ~= nil then - self._private.active_widget.selected = true - self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color - local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", self.app_name_selected_color, name_widget.text) - end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", self.app_name_selected_color, generic_name_widget.text) + if self.app_template == nil then + self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color + local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] + if name_widget then + name_widget.markup = string.format("%s", self.app_name_selected_color, name_widget.text) + end + local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] + if generic_name_widget then + generic_name_widget.markup = string.format("%s", self.app_name_selected_color, generic_name_widget.text) + end end + self._private.active_widget:emit_signal("selected") + self._private.active_widget.selected = true end end end local function unselect_app(self) if self._private.active_widget ~= nil then - self._private.active_widget.selected = false + if self.app_template == nil then self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color - local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) - end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", self.app_name_normal_color, generic_name_widget.text) + local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] + if name_widget then + name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) + end + local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] + if generic_name_widget then + generic_name_widget.markup = string.format("%s", self.app_name_normal_color, generic_name_widget.text) + end end + self._private.active_widget:emit_signal("unselected") + self._private.active_widget.selected = false self._private.active_widget = nil end end -local function create_app_widget(self, entry) - local icon = self.app_show_icon == true and - { - widget = wibox.widget.imagebox, - id = "icon_role", - halign = self.app_icon_halign, - forced_width = self.app_icon_width, - forced_height = self.app_icon_height, - image = entry.icon - } or nil - - local name = self.app_show_name == true and - { - widget = wibox.widget.textbox, - id = "name_role", - font = self.app_name_font, - markup = string.format("%s", self.app_name_normal_color, entry.name) - } or nil +local function create_app_widget(self, app) + local app_widget = nil - local generic_name = entry.generic_name ~= nil and self.app_show_generic_name == true and - { - widget = wibox.widget.textbox, - id = "generic_name_role", - font = self.app_name_font, - markup = entry.generic_name ~= "" and " (" .. entry.generic_name .. ")" or "" - } or nil + if self.app_template == nil then + local icon = self.app_show_icon == true and + { + widget = wibox.widget.imagebox, + id = "icon_role", + halign = self.app_icon_halign, + forced_width = self.app_icon_width, + forced_height = self.app_icon_height, + image = app.icon + } or nil + + local name = self.app_show_name == true and + { + widget = wibox.widget.textbox, + id = "name_role", + font = self.app_name_font, + markup = string.format("%s", self.app_name_normal_color, app.name) + } or nil - local app = wibox.widget - { - widget = wibox.container.background, - id = "background_role", - forced_width = self.app_width, - forced_height = self.app_height, - shape = self.app_shape, - bg = self.app_normal_color, + local generic_name = app.generic_name ~= nil and self.app_show_generic_name == true and + { + widget = wibox.widget.textbox, + id = "generic_name_role", + font = self.app_name_font, + markup = app.generic_name ~= "" and " (" .. app.generic_name .. ")" or "" + } or nil + + app_widget = wibox.widget { - widget = wibox.container.margin, - margins = self.app_content_padding, + widget = wibox.container.background, + id = "background_role", + forced_width = self.app_width, + forced_height = self.app_height, + shape = self.app_shape, + bg = self.app_normal_color, { - -- Using this hack instead of container.place because that will fuck with the name/icon halign - layout = wibox.layout.align.vertical, - expand = "outside", - nil, + widget = wibox.container.margin, + margins = self.app_content_padding, { - layout = wibox.layout.fixed.vertical, - spacing = self.app_content_spacing, - icon, + -- Using this hack instead of container.place because that will fuck with the name/icon halign + layout = wibox.layout.align.vertical, + expand = "outside", + nil, { - widget = wibox.container.place, - halign = self.app_name_halign, + layout = wibox.layout.fixed.vertical, + spacing = self.app_content_spacing, + icon, { - layout = wibox.layout.fixed.horizontal, - spacing = self.app_name_generic_name_spacing, - name, - generic_name + widget = wibox.container.place, + halign = self.app_name_halign, + { + layout = wibox.layout.fixed.horizontal, + spacing = self.app_name_generic_name_spacing, + name, + generic_name + } } - } - }, - nil + }, + nil + } } } - } - function app:spawn() - if entry.terminal == true then - awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. entry.executable) - else - awful.spawn(entry.executable) - end + app_widget:connect_signal("mouse::enter", function() + local widget = capi.mouse.current_wibox + if widget then + widget.cursor = "hand2" + end - if self.hide_on_launch then - self:hide() - end - end + if app_widget.selected then + app_widget:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color + else + app_widget:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color + end + end) - app:connect_signal("mouse::enter", function(_self) - local widget = capi.mouse.current_wibox - if widget then - widget.cursor = "hand2" - end + app_widget:connect_signal("mouse::leave", function() + local widget = capi.mouse.current_wibox + if widget then + widget.cursor = "left_ptr" + end - local app = _self - if app.selected then - app:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color - else - local is_opaque = color.is_opaque(self.app_normal_color) - local is_dark = color.is_dark(self.app_normal_color) - local app_normal_color = color.hex_to_rgba(self.app_normal_color) - local hover_color = (is_dark or is_opaque) and - color.rgba_to_hex(color.multiply(app_normal_color, 2.5)) or - color.rgba_to_hex(color.multiply(app_normal_color, 0.5)) - app:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color - end - end) + if app_widget.selected then + app_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color + else + app_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color + end + end) + else + app_widget = self.app_template(app) - app:connect_signal("mouse::leave", function(_self) - local widget = capi.mouse.current_wibox - if widget then - widget.cursor = "left_ptr" + local icon = app_widget:get_children_by_id("icon_role")[1] + if icon then + icon.image = app.icon end - - local app = _self - if app.selected then - app:get_children_by_id("background_role")[1].bg = self.app_selected_color - else - app:get_children_by_id("background_role")[1].bg = self.app_normal_color + local name = app_widget:get_children_by_id("name_role")[1] + if name then + name.text = app.name end - end) + local generic_name = app_widget:get_children_by_id("generic_name_role")[1] + if generic_name then + generic_name.text = app.generic_name + end + local command = app_widget:get_children_by_id("command_role")[1] + if command then + command.text = app.executable + end + end - app:connect_signal("button::press", function(app, _, __, button) + app_widget:connect_signal("button::press", function(app, _, __, button) if button == 1 then if self._private.active_widget == app or not self.select_before_spawn then app:spawn() @@ -248,7 +257,19 @@ local function create_app_widget(self, entry) end end) - return app + function app_widget:spawn() + if app.terminal == true then + awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.executable) + else + awful.spawn(app.executable) + end + + if self.hide_on_launch then + self:hide() + end + end + + return app_widget end local function search(self, text) From d1347193475c6ee165a55f4461c3b75fed76e4a7 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 02:20:23 +0200 Subject: [PATCH 009/185] At last --- widget/app_launcher/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index f659d8e4..0c1fd6a4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -978,9 +978,6 @@ local function new(args) ret._private.pages_count = 0 ret._private.current_page = 1 - generate_apps(ret) - reset(ret) - if ret.rubato and ret.rubato.x then ret.rubato.x:subscribe(function(pos) ret._private.widget.x = pos @@ -1025,6 +1022,9 @@ local function new(args) end}) end) + generate_apps(ret) + reset(ret) + return ret end From 281fc8953a7a72d3db28041f9b3c85d2aaf15879 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 02:36:16 +0200 Subject: [PATCH 010/185] Add support app launcher template --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 0c1fd6a4..f70730bd 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -929,7 +929,7 @@ local function new(args) border_color = ret.border_color, shape = ret.shape, bg = ret.background, - widget = + widget = args.widget_template(ret._private.prompt.textbox, ret._private.grid) or { layout = wibox.layout.fixed.vertical, { From 4cb5972fb430149bfcb22bc3c3f76628616903aa Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 14:57:39 +0200 Subject: [PATCH 011/185] Not used --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index f70730bd..49e87b15 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -1017,7 +1017,7 @@ local function new(args) end awful.spawn.easy_async_with_shell(KILL_OLD_INOTIFY_SCRIPT, function() - awful.spawn.with_line_callback(INOTIFY_SCRIPT, {stdout = function(_) + awful.spawn.with_line_callback(INOTIFY_SCRIPT, {stdout = function() generate_apps(ret) end}) end) From 5b8282b82d2765a9ecfcdae5838a6447b929ff21 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:03:02 +0200 Subject: [PATCH 012/185] Refactor scrolling --- docs/widgets/app_launcher.md | 2 - widget/app_launcher/init.lua | 103 +++++++++++++---------------------- 2 files changed, 37 insertions(+), 68 deletions(-) diff --git a/docs/widgets/app_launcher.md b/docs/widgets/app_launcher.md index 332fce4f..89e2fdb6 100644 --- a/docs/widgets/app_launcher.md +++ b/docs/widgets/app_launcher.md @@ -62,8 +62,6 @@ local args = { screen = awful.screen -- Screen you want the launcher to launch to placement = awful.placement.top_left -- Where launcher should be placed ("awful.placement.centered"). rubato = { x = rubato_animation_x, y = rubato_animation_y } -- Rubato animation to apply to launcher - shrink_width = true -- Automatically shrink width of launcher to fit varying numbers of apps in list (works on apps_per_column) - shrink_height = true -- Automatically shrink height of launcher to fit varying numbers of apps in list (works on apps_per_row) background = "#FFFFFF" -- Set bg color border_width = dpi(0) -- Set border width of popup border_color = "#FFFFFF" -- Set border color of popup diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 49e87b15..26503319 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -114,7 +114,7 @@ end local function unselect_app(self) if self._private.active_widget ~= nil then if self.app_template == nil then - self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color + self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] if name_widget then name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) @@ -375,9 +375,14 @@ local function page_forward(self, direction) if direction == "down" then select_app(self, 1, 1) else - local rows, _ = self._private.grid:get_dimension() - local row = math.min(pos.row, rows) - select_app(self, row, 1) + local next_app = self._private.grid:get_widgets_at(pos.row, 1) + if next_app == nil then + local next_app = self._private.grid.children[#self._private.grid.children] + local next_app_pos = self._private.grid:get_widget_position(next_app) + select_app(self, next_app_pos.row, next_app_pos.col) + else + select_app(self, pos.row, 1) + end end end end @@ -420,12 +425,9 @@ local function page_backward(self, direction) select_app(self, pos.row, columns) end elseif self.wrap_page_scrolling then - if direction == "up" then - select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) - else - -- Keep the same row from last page - select_app(self, math.min(pos.row, #self._private.grid.children % self.apps_per_row), columns) - end + local next_app = self._private.grid.children[#self._private.grid.children] + local next_app_pos = self._private.grid:get_widget_position(next_app) + select_app(self, next_app_pos.row, next_app_pos.col) end end @@ -435,18 +437,12 @@ local function scroll_up(self) return end - local rows, _ = self._private.grid:get_dimension() - local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scroll_up = pos.col > 1 or pos.row > 1 - - -- Check if the current marked app is not the first + local can_scroll_up = self._private.grid:index(self._private.active_widget) > 1 if can_scroll_up then + local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -1) + local next_app_pos = self._private.grid:get_widget_position(next_app) unselect_app(self) - if pos.row == 1 then - select_app(self, rows, pos.col - 1) - else - select_app(self, pos.row - 1, pos.col) - end + select_app(self, next_app_pos.row, next_app_pos.col) else page_backward(self, "up") end @@ -458,19 +454,12 @@ local function scroll_down(self) return end - local rows, _ = self._private.grid:get_dimension() - local pos = self._private.grid:get_widget_position(self._private.active_widget) local can_scroll_down = self._private.grid:index(self._private.active_widget) < #self._private.grid.children - - -- Check if we can scroll down the app list if can_scroll_down then - -- Unmark the previous app + local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, 1) + local next_app_pos = self._private.grid:get_widget_position(next_app) unselect_app(self) - if pos.row == rows then - select_app(self, 1, pos.col + 1) - else - select_app(self, pos.row + 1, pos.col) - end + select_app(self, next_app_pos.row, next_app_pos.col) else page_forward(self, "down") end @@ -483,11 +472,12 @@ local function scroll_left(self) end local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scroll_left = pos.col > 1 - + local can_scroll_left = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil if can_scroll_left then + local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -self.apps_per_row) + local next_app_pos = self._private.grid:get_widget_position(next_app) unselect_app(self) - select_app(self, pos.row, pos.col - 1) + select_app(self, next_app_pos.row, next_app_pos.col) else page_backward(self, "left") end @@ -499,21 +489,13 @@ local function scroll_right(self) return end - local _, columns = self._private.grid:get_dimension() local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scrol_right = pos.col < columns - - if can_scrol_right then + local can_scroll_right = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil + if can_scroll_right then + local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, self.apps_per_row) + local next_app_pos = self._private.grid:get_widget_position(next_app) unselect_app(self) - - -- Check for a case where the last column has less rows than the previous column - if self._private.grid:get_widgets_at(pos.row, pos.col + 1) == nil then - local app = self._private.grid.children[#self._private.grid.children] - pos = self._private.grid:get_widget_position(app) - select_app(self, pos.row, pos.col) - else - select_app(self, pos.row, pos.col + 1) - end + select_app(self, next_app_pos.row, next_app_pos.col) else page_forward(self, "right") end @@ -767,18 +749,11 @@ local function new(args) args.wrap_page_scrolling = args.wrap_page_scrolling == nil and true or args.wrap_page_scrolling args.wrap_app_scrolling = args.wrap_app_scrolling == nil and true or args.wrap_app_scrolling - args.default_app_icon_name = args.default_app_icon_name or nil - args.default_app_icon_path = args.default_app_icon_path or nil - args.icon_theme = args.icon_theme or nil - args.icon_size = args.icon_size or nil - args.type = args.type or "dock" args.show_on_focused_screen = args.show_on_focused_screen == nil and true or args.show_on_focused_screen args.screen = args.screen or capi.screen.primary args.placement = args.placement or awful.placement.centered args.rubato = args.rubato or nil - args.shrink_width = args.shrink_width ~= nil and args.shrink_width or false - args.shrink_height = args.shrink_height ~= nil and args.shrink_height or false args.background = args.background or "#000000" args.border_width = args.border_width or beautiful.border_width or dpi(0) args.border_color = args.border_color or beautiful.border_color or "#FFFFFF" @@ -805,12 +780,17 @@ local function new(args) args.prompt_text_color = args.prompt_text_color or beautiful.bg_normal or "#000000" args.prompt_cursor_color = args.prompt_cursor_color or beautiful.bg_normal or "#000000" + args.default_app_icon_name = args.default_app_icon_name or nil + args.default_app_icon_path = args.default_app_icon_path or nil + args.icon_theme = args.icon_theme or nil + args.icon_size = args.icon_size or nil + args.apps_per_row = args.apps_per_row or 5 args.apps_per_column = args.apps_per_column or 3 args.apps_margin = args.apps_margin or dpi(30) args.apps_spacing = args.apps_spacing or dpi(30) - args.expand_apps = args.expand_apps == nil and true or args.expand_apps + args.app_width = args.app_width or dpi(300) args.app_height = args.app_height or dpi(120) args.app_shape = args.app_shape or nil @@ -843,14 +823,6 @@ local function new(args) gtable.crush(ret, app_launcher) gtable.crush(ret, args) - -- Calculate the grid width and height - local grid_width = ret.shrink_width == false - and dpi((ret.app_width * ret.apps_per_column) + ((ret.apps_per_column - 1) * ret.apps_spacing)) - or nil - local grid_height = ret.shrink_height == false - and dpi((ret.app_height * ret.apps_per_row) + ((ret.apps_per_row - 1) * ret.apps_spacing)) - or nil - -- These widgets need to be later accessed ret._private.prompt = prompt { @@ -906,13 +878,12 @@ local function new(args) ret._private.grid = wibox.widget { layout = wibox.layout.grid, - forced_width = grid_width, - forced_height = grid_height, - orientation = "vertical", + orientation = "horizontal", homogeneous = true, expand = ret.expand_apps, spacing = ret.apps_spacing, forced_num_cols = ret.apps_per_column, + forced_num_rows = ret.apps_per_row, buttons = { awful.button({}, 4, function() scroll_up(ret) end), @@ -929,7 +900,7 @@ local function new(args) border_color = ret.border_color, shape = ret.shape, bg = ret.background, - widget = args.widget_template(ret._private.prompt.textbox, ret._private.grid) or + widget = { layout = wibox.layout.fixed.vertical, { From 4cbedd62446ebdaba7759cc7b4f2f1da3091ef02 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:33:13 +0200 Subject: [PATCH 013/185] Make select and unselect part of the app_widget object --- widget/app_launcher/init.lua | 164 ++++++++++++++++------------------- 1 file changed, 77 insertions(+), 87 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 26503319..2659ea2e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -89,47 +89,6 @@ local function has_value(tab, val) return false end -local function select_app(self, x, y) - local widgets = self._private.grid:get_widgets_at(x, y) - if widgets then - self._private.active_widget = widgets[1] - if self._private.active_widget ~= nil then - if self.app_template == nil then - self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color - local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", self.app_name_selected_color, name_widget.text) - end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", self.app_name_selected_color, generic_name_widget.text) - end - end - self._private.active_widget:emit_signal("selected") - self._private.active_widget.selected = true - end - end -end - -local function unselect_app(self) - if self._private.active_widget ~= nil then - if self.app_template == nil then - self._private.active_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color - local name_widget = self._private.active_widget:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) - end - local generic_name_widget = self._private.active_widget:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", self.app_name_normal_color, generic_name_widget.text) - end - end - self._private.active_widget:emit_signal("unselected") - self._private.active_widget.selected = false - self._private.active_widget = nil - end -end - local function create_app_widget(self, app) local app_widget = nil @@ -247,12 +206,7 @@ local function create_app_widget(self, app) if self._private.active_widget == app or not self.select_before_spawn then app:spawn() else - -- Unmark the previous app - unselect_app(self) - - -- Mark this app - local pos = self._private.grid:get_widget_position(app) - select_app(self, pos.row, pos.col) + app:select() end end end) @@ -269,13 +223,53 @@ local function create_app_widget(self, app) end end + local _self = self + function app_widget:select() + if _self._private.active_widget then + _self._private.active_widget:unselect() + end + _self._private.active_widget = self + self:emit_signal("selected") + self.selected = true + + if _self.app_template == nil then + self:get_children_by_id("background_role")[1].bg = _self.app_selected_color + local name_widget = self:get_children_by_id("name_role")[1] + if name_widget then + name_widget.markup = string.format("%s", _self.app_name_selected_color, name_widget.text) + end + local generic_name_widget = self:get_children_by_id("generic_name_role")[1] + if generic_name_widget then + generic_name_widget.markup = string.format("%s", _self.app_name_selected_color, generic_name_widget.text) + end + end + end + + function app_widget:unselect() + self:emit_signal("unselected") + self.selected = false + _self._private.active_widget = nil + + if _self.app_template == nil then + self:get_children_by_id("background_role")[1].bg = _self.app_normal_color + local name_widget = self:get_children_by_id("name_role")[1] + if name_widget then + name_widget.markup = string.format("%s", _self.app_name_normal_color, name_widget.text) + end + local generic_name_widget = self:get_children_by_id("generic_name_role")[1] + if generic_name_widget then + generic_name_widget.markup = string.format("%s", _self.app_name_normal_color, generic_name_widget.text) + end + end + end + return app_widget end local function search(self, text) - unselect_app(self) + self._private.active_widget:unselect() - local pos = self._private.grid:get_widget_position(self._private.active_widget) + local old_pos = self._private.grid:get_widget_position(self._private.active_widget) -- Reset all the matched entries self._private.matched_entries = {} @@ -328,14 +322,17 @@ local function search(self, text) -- it will reselect the app whose index is the same as the app index that was previously selected -- and if matched_entries.length < current_index it will instead select the app with the greatest index if self.try_to_keep_index_after_searching then - if self._private.grid:get_widgets_at(pos.row, pos.col) == nil then + if self._private.grid:get_widgets_at(old_pos.row, old_pos.col) == nil then local app = self._private.grid.children[#self._private.grid.children] - pos = self._private.grid:get_widget_position(app) + app:select() + else + local app = self._private.grid:get_widgets_at(old_pos.row, old_pos.col)[1] + app:select() end - select_app(self, pos.row, pos.col) -- Otherwise select the first app on the list else - select_app(self, 1, 1) + local app = self._private.grid:get_widgets_at(1, 1)[1] + app:select() end end @@ -352,8 +349,8 @@ local function page_forward(self, direction) min_app_index_to_include = 0 max_app_index_to_include = self._private.apps_per_page elseif self.wrap_app_scrolling then - unselect_app(self) - select_app(self, 1, 1) + local app = self._private.grid:get_widgets_at(1, 1)[1] + app:select() return else return @@ -373,15 +370,15 @@ local function page_forward(self, direction) if self._private.current_page > 1 or self.wrap_page_scrolling then if direction == "down" then - select_app(self, 1, 1) + local app = self._private.grid:get_widgets_at(1, 1)[1] + app:select() else - local next_app = self._private.grid:get_widgets_at(pos.row, 1) - if next_app == nil then - local next_app = self._private.grid.children[#self._private.grid.children] - local next_app_pos = self._private.grid:get_widget_position(next_app) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = self._private.grid:get_widgets_at(pos.row, 1)[1] + if app == nil then + local app = self._private.grid.children[#self._private.grid.children] + app:select() else - select_app(self, pos.row, 1) + app:select() end end end @@ -393,9 +390,8 @@ local function page_backward(self, direction) elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then - local rows, columns = self._private.grid:get_dimension() - unselect_app(self) - select_app(self, math.min(rows, #self._private.grid.children % self.apps_per_row), columns) + local app = self._private.grid.children{#self._private.grid.children} + app:select() return else return @@ -419,15 +415,16 @@ local function page_backward(self, direction) local rows, columns = self._private.grid:get_dimension() if self._private.current_page < self._private.pages_count then if direction == "up" then - select_app(self, rows, columns) + local app = self._private.grid.children{#self._private.grid.children} + app:select() else -- Keep the same row from last page - select_app(self, pos.row, columns) + local app = self._private.grid:get_widgets_at(pos.row, columns)[1] + app:select() end elseif self.wrap_page_scrolling then - local next_app = self._private.grid.children[#self._private.grid.children] - local next_app_pos = self._private.grid:get_widget_position(next_app) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = self._private.grid.children[#self._private.grid.children] + app:select() end end @@ -439,10 +436,8 @@ local function scroll_up(self) local can_scroll_up = self._private.grid:index(self._private.active_widget) > 1 if can_scroll_up then - local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -1) - local next_app_pos = self._private.grid:get_widget_position(next_app) - unselect_app(self) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -1) + app:select() else page_backward(self, "up") end @@ -456,10 +451,8 @@ local function scroll_down(self) local can_scroll_down = self._private.grid:index(self._private.active_widget) < #self._private.grid.children if can_scroll_down then - local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, 1) - local next_app_pos = self._private.grid:get_widget_position(next_app) - unselect_app(self) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, 1) + app:select() else page_forward(self, "down") end @@ -474,10 +467,8 @@ local function scroll_left(self) local pos = self._private.grid:get_widget_position(self._private.active_widget) local can_scroll_left = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil if can_scroll_left then - local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -self.apps_per_row) - local next_app_pos = self._private.grid:get_widget_position(next_app) - unselect_app(self) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -self.apps_per_row) + app:select() else page_backward(self, "left") end @@ -492,10 +483,8 @@ local function scroll_right(self) local pos = self._private.grid:get_widget_position(self._private.active_widget) local can_scroll_right = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil if can_scroll_right then - local next_app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, self.apps_per_row) - local next_app_pos = self._private.grid:get_widget_position(next_app) - unselect_app(self) - select_app(self, next_app_pos.row, next_app_pos.col) + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, self.apps_per_row) + app:select() else page_forward(self, "right") end @@ -517,7 +506,8 @@ local function reset(self) end end - select_app(self, 1, 1) + local app = self._private.grid:get_widgets_at(1, 1)[1] + app:select() end local function generate_apps(self) From d272561ea1a5bbc08199f4978a2f6b44bb685dc9 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:41:51 +0200 Subject: [PATCH 014/185] Make the scroll function accesiable + DRY it --- widget/app_launcher/init.lua | 131 +++++++++++++++++------------------ 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 2659ea2e..e4a94c45 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -428,68 +428,6 @@ local function page_backward(self, direction) end end -local function scroll_up(self) - if #self._private.grid.children < 1 then - self._private.active_widget = nil - return - end - - local can_scroll_up = self._private.grid:index(self._private.active_widget) > 1 - if can_scroll_up then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -1) - app:select() - else - page_backward(self, "up") - end -end - -local function scroll_down(self) - if #self._private.grid.children < 1 then - self._private.active_widget = nil - return - end - - local can_scroll_down = self._private.grid:index(self._private.active_widget) < #self._private.grid.children - if can_scroll_down then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, 1) - app:select() - else - page_forward(self, "down") - end -end - -local function scroll_left(self) - if #self._private.grid.children < 1 then - self._private.active_widget = nil - return - end - - local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scroll_left = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil - if can_scroll_left then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, -self.apps_per_row) - app:select() - else - page_backward(self, "left") - end -end - -local function scroll_right(self) - if #self._private.grid.children < 1 then - self._private.active_widget = nil - return - end - - local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scroll_right = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil - if can_scroll_right then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, self.apps_per_row) - app:select() - else - page_forward(self, "right") - end -end - local function reset(self) self._private.grid:reset() self._private.matched_entries = self._private.all_entries @@ -600,6 +538,63 @@ local function generate_apps(self) end end +local function scroll(self, dir) + if #self._private.grid.children < 1 then + self._private.active_widget = nil + return + end + + local pos = self._private.grid:get_widget_position(self._private.active_widget) + local can_scroll = false + local step_size = 0 + local if_cant_scroll_func = nil + + if dir == "up" then + can_scroll = self._private.grid:index(self._private.active_widget) > 1 + step_size = -1 + if_cant_scroll_func = function() page_backward(self, "up") end + elseif dir == "down" then + can_scroll = self._private.grid:index(self._private.active_widget) < #self._private.grid.children + step_size = 1 + if_cant_scroll_func = function() page_forward(self, "down") end + elseif dir == "left" then + can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil + step_size = -self.apps_per_row + if_cant_scroll_func = function() page_backward(self, "left") end + elseif dir == "right" then + can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil + step_size = self.apps_per_row + if_cant_scroll_func = function() page_forward(self, "right") end + end + + if can_scroll then + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, step_size) + app:select() + else + if_cant_scroll_func() + end +end + +--- Scrolls up +function app_launcher:scroll_up() + scroll(self, "up") +end + +--- Scrolls down +function app_launcher:scroll_down() + scroll(self, "down") +end + +--- Scrolls to the left +function app_launcher:scroll_left() + scroll(self, "left") +end + +--- Scrolls to the right +function app_launcher:scroll_right() + scroll(self, "right") +end + --- Shows the app launcher function app_launcher:show() local screen = self.screen @@ -852,16 +847,16 @@ local function new(args) end end if key == "Up" then - scroll_up(ret) + ret:scroll_up() end if key == "Down" then - scroll_down(ret) + ret:scroll_down() end if key == "Left" then - scroll_left(ret) + ret:scroll_left() end if key == "Right" then - scroll_right(ret) + ret:scroll_right() end end } @@ -876,8 +871,8 @@ local function new(args) forced_num_rows = ret.apps_per_row, buttons = { - awful.button({}, 4, function() scroll_up(ret) end), - awful.button({}, 5, function() scroll_down(ret) end) + awful.button({}, 4, function() ret:scroll_up() end), + awful.button({}, 5, function() ret:scroll_down() end) } } ret._private.widget = awful.popup From cbf8960fe5ab39eff0ea36157b752f38e8b1e17f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:43:06 +0200 Subject: [PATCH 015/185] Formatting --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index e4a94c45..94198eb9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -864,8 +864,8 @@ local function new(args) { layout = wibox.layout.grid, orientation = "horizontal", - homogeneous = true, - expand = ret.expand_apps, + homogeneous = true, + expand = ret.expand_apps, spacing = ret.apps_spacing, forced_num_cols = ret.apps_per_column, forced_num_rows = ret.apps_per_row, From 98fc2c9829ae9250f4bc88c84404fb18dbcc304b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:44:21 +0200 Subject: [PATCH 016/185] Not needed anymore --- widget/app_launcher/init.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 94198eb9..cf216253 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -267,8 +267,6 @@ local function create_app_widget(self, app) end local function search(self, text) - self._private.active_widget:unselect() - local old_pos = self._private.grid:get_widget_position(self._private.active_widget) -- Reset all the matched entries From c822f16f1424954c9bb1bd4f242c3fb8ab3b45f1 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:45:13 +0200 Subject: [PATCH 017/185] Formatting --- widget/app_launcher/init.lua | 74 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index cf216253..1fcd883e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -426,6 +426,43 @@ local function page_backward(self, direction) end end +local function scroll(self, dir) + if #self._private.grid.children < 1 then + self._private.active_widget = nil + return + end + + local pos = self._private.grid:get_widget_position(self._private.active_widget) + local can_scroll = false + local step_size = 0 + local if_cant_scroll_func = nil + + if dir == "up" then + can_scroll = self._private.grid:index(self._private.active_widget) > 1 + step_size = -1 + if_cant_scroll_func = function() page_backward(self, "up") end + elseif dir == "down" then + can_scroll = self._private.grid:index(self._private.active_widget) < #self._private.grid.children + step_size = 1 + if_cant_scroll_func = function() page_forward(self, "down") end + elseif dir == "left" then + can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil + step_size = -self.apps_per_row + if_cant_scroll_func = function() page_backward(self, "left") end + elseif dir == "right" then + can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil + step_size = self.apps_per_row + if_cant_scroll_func = function() page_forward(self, "right") end + end + + if can_scroll then + local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, step_size) + app:select() + else + if_cant_scroll_func() + end +end + local function reset(self) self._private.grid:reset() self._private.matched_entries = self._private.all_entries @@ -536,43 +573,6 @@ local function generate_apps(self) end end -local function scroll(self, dir) - if #self._private.grid.children < 1 then - self._private.active_widget = nil - return - end - - local pos = self._private.grid:get_widget_position(self._private.active_widget) - local can_scroll = false - local step_size = 0 - local if_cant_scroll_func = nil - - if dir == "up" then - can_scroll = self._private.grid:index(self._private.active_widget) > 1 - step_size = -1 - if_cant_scroll_func = function() page_backward(self, "up") end - elseif dir == "down" then - can_scroll = self._private.grid:index(self._private.active_widget) < #self._private.grid.children - step_size = 1 - if_cant_scroll_func = function() page_forward(self, "down") end - elseif dir == "left" then - can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil - step_size = -self.apps_per_row - if_cant_scroll_func = function() page_backward(self, "left") end - elseif dir == "right" then - can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil - step_size = self.apps_per_row - if_cant_scroll_func = function() page_forward(self, "right") end - end - - if can_scroll then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, step_size) - app:select() - else - if_cant_scroll_func() - end -end - --- Scrolls up function app_launcher:scroll_up() scroll(self, "up") From f148d9278ce8fc2c39e8dbb0091b3edc0a63a43a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:45:31 +0200 Subject: [PATCH 018/185] hmm --- widget/app_launcher/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 1fcd883e..8a076c06 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -334,7 +334,7 @@ local function search(self, text) end end -local function page_forward(self, direction) +local function page_forward(self, dir) local min_app_index_to_include = 0 local max_app_index_to_include = self._private.apps_per_page @@ -367,7 +367,7 @@ local function page_forward(self, direction) end if self._private.current_page > 1 or self.wrap_page_scrolling then - if direction == "down" then + if dir == "down" then local app = self._private.grid:get_widgets_at(1, 1)[1] app:select() else @@ -382,7 +382,7 @@ local function page_forward(self, direction) end end -local function page_backward(self, direction) +local function page_backward(self, dir) if self._private.current_page > 1 then self._private.current_page = self._private.current_page - 1 elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then @@ -412,7 +412,7 @@ local function page_backward(self, direction) local rows, columns = self._private.grid:get_dimension() if self._private.current_page < self._private.pages_count then - if direction == "up" then + if dir == "up" then local app = self._private.grid.children{#self._private.grid.children} app:select() else From 458cd3c8e7a1eb90aabe7ab9f300a167d8ac618b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:48:13 +0200 Subject: [PATCH 019/185] No need for () --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 8a076c06..19a5d036 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -799,7 +799,7 @@ local function new(args) args.app_name_selected_color = args.app_name_selected_color or beautiful.bg_normal or "#000000" args.app_show_generic_name = args.app_show_generic_name ~= nil and args.app_show_generic_name or false - local ret = gobject({}) + local ret = gobject {} ret._private = {} ret._private.text = "" From 7a5585201e9010e36cd90c69bfdaab8dbda20354 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 15:58:18 +0200 Subject: [PATCH 020/185] whoops --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 19a5d036..890d76f3 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -388,7 +388,7 @@ local function page_backward(self, dir) elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then - local app = self._private.grid.children{#self._private.grid.children} + local app = self._private.grid.children[#self._private.grid.children] app:select() return else @@ -413,7 +413,7 @@ local function page_backward(self, dir) local rows, columns = self._private.grid:get_dimension() if self._private.current_page < self._private.pages_count then if dir == "up" then - local app = self._private.grid.children{#self._private.grid.children} + local app = self._private.grid.children[#self._private.grid.children] app:select() else -- Keep the same row from last page From 3d3529cddf0ea4d350847a61a07f2ff7a15f476f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 16:15:14 +0200 Subject: [PATCH 021/185] More formatting --- widget/app_launcher/init.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 890d76f3..afe65d80 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -742,6 +742,17 @@ local function new(args) args.border_color = args.border_color or beautiful.border_color or "#FFFFFF" args.shape = args.shape or nil + args.default_app_icon_name = args.default_app_icon_name or nil + args.default_app_icon_path = args.default_app_icon_path or nil + args.icon_theme = args.icon_theme or nil + args.icon_size = args.icon_size or nil + + args.apps_per_row = args.apps_per_row or 5 + args.apps_per_column = args.apps_per_column or 3 + args.apps_margin = args.apps_margin or dpi(30) + args.apps_spacing = args.apps_spacing or dpi(30) + args.expand_apps = args.expand_apps == nil and true or args.expand_apps + args.prompt_height = args.prompt_height or dpi(100) args.prompt_margins = args.prompt_margins or dpi(0) args.prompt_paddings = args.prompt_paddings or dpi(30) @@ -763,17 +774,6 @@ local function new(args) args.prompt_text_color = args.prompt_text_color or beautiful.bg_normal or "#000000" args.prompt_cursor_color = args.prompt_cursor_color or beautiful.bg_normal or "#000000" - args.default_app_icon_name = args.default_app_icon_name or nil - args.default_app_icon_path = args.default_app_icon_path or nil - args.icon_theme = args.icon_theme or nil - args.icon_size = args.icon_size or nil - - args.apps_per_row = args.apps_per_row or 5 - args.apps_per_column = args.apps_per_column or 3 - args.apps_margin = args.apps_margin or dpi(30) - args.apps_spacing = args.apps_spacing or dpi(30) - args.expand_apps = args.expand_apps == nil and true or args.expand_apps - args.app_width = args.app_width or dpi(300) args.app_height = args.app_height or dpi(120) args.app_shape = args.app_shape or nil From 4499ede6bdbd5ca08fe7eb6ee6c121112fec4206 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 16:36:31 +0200 Subject: [PATCH 022/185] Templates --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index afe65d80..c61a44a4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -883,7 +883,7 @@ local function new(args) border_color = ret.border_color, shape = ret.shape, bg = ret.background, - widget = + widget = args.widget_template ~= nil and args.widget_template(ret._private.prompt.textbox, ret._private.grid) or { layout = wibox.layout.fixed.vertical, { From ddd5b27ea97168055762b6ef931f849d114cc690 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 5 Feb 2023 16:47:09 +0200 Subject: [PATCH 023/185] It might be empty --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c61a44a4..36513daa 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -328,7 +328,7 @@ local function search(self, text) app:select() end -- Otherwise select the first app on the list - else + elseif #self._private.grid.children > 0 then local app = self._private.grid:get_widgets_at(1, 1)[1] app:select() end From 400bd988a89059fa57537f8b0e71fa4d11af51d3 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 7 Feb 2023 14:42:17 +0200 Subject: [PATCH 024/185] Fix accessing the wrong self --- widget/app_launcher/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 36513daa..3382468e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -211,6 +211,7 @@ local function create_app_widget(self, app) end end) + local _self = self function app_widget:spawn() if app.terminal == true then awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.executable) @@ -218,12 +219,11 @@ local function create_app_widget(self, app) awful.spawn(app.executable) end - if self.hide_on_launch then - self:hide() + if _self.hide_on_launch then + _self:hide() end end - local _self = self function app_widget:select() if _self._private.active_widget then _self._private.active_widget:unselect() From ee9c8b128345ea288312b959d014d52936e83024 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Feb 2023 18:14:35 +0200 Subject: [PATCH 025/185] Better naming --- widget/app_launcher/init.lua | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3382468e..122f86a1 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -89,8 +89,8 @@ local function has_value(tab, val) return false end -local function create_app_widget(self, app) - local app_widget = nil +local function app_widget(self, app) + local widget = nil if self.app_template == nil then local icon = self.app_show_icon == true and @@ -119,7 +119,7 @@ local function create_app_widget(self, app) markup = app.generic_name ~= "" and " (" .. app.generic_name .. ")" or "" } or nil - app_widget = wibox.widget + widget = wibox.widget { widget = wibox.container.background, id = "background_role", @@ -155,53 +155,53 @@ local function create_app_widget(self, app) } } - app_widget:connect_signal("mouse::enter", function() + widget:connect_signal("mouse::enter", function() local widget = capi.mouse.current_wibox if widget then widget.cursor = "hand2" end - if app_widget.selected then - app_widget:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color + if widget.selected then + widget:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color else - app_widget:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color + widget:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color end end) - app_widget:connect_signal("mouse::leave", function() + widget:connect_signal("mouse::leave", function() local widget = capi.mouse.current_wibox if widget then widget.cursor = "left_ptr" end - if app_widget.selected then - app_widget:get_children_by_id("background_role")[1].bg = self.app_selected_color + if widget.selected then + widget:get_children_by_id("background_role")[1].bg = self.app_selected_color else - app_widget:get_children_by_id("background_role")[1].bg = self.app_normal_color + widget:get_children_by_id("background_role")[1].bg = self.app_normal_color end end) else - app_widget = self.app_template(app) + widget = self.app_template(app) - local icon = app_widget:get_children_by_id("icon_role")[1] + local icon = widget:get_children_by_id("icon_role")[1] if icon then icon.image = app.icon end - local name = app_widget:get_children_by_id("name_role")[1] + local name = widget:get_children_by_id("name_role")[1] if name then name.text = app.name end - local generic_name = app_widget:get_children_by_id("generic_name_role")[1] + local generic_name = widget:get_children_by_id("generic_name_role")[1] if generic_name then generic_name.text = app.generic_name end - local command = app_widget:get_children_by_id("command_role")[1] + local command = widget:get_children_by_id("command_role")[1] if command then command.text = app.executable end end - app_widget:connect_signal("button::press", function(app, _, __, button) + widget:connect_signal("button::press", function(app, _, __, button) if button == 1 then if self._private.active_widget == app or not self.select_before_spawn then app:spawn() @@ -212,7 +212,7 @@ local function create_app_widget(self, app) end) local _self = self - function app_widget:spawn() + function widget:spawn() if app.terminal == true then awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.executable) else @@ -224,7 +224,7 @@ local function create_app_widget(self, app) end end - function app_widget:select() + function widget:select() if _self._private.active_widget then _self._private.active_widget:unselect() end @@ -245,7 +245,7 @@ local function create_app_widget(self, app) end end - function app_widget:unselect() + function widget:unselect() self:emit_signal("unselected") self.selected = false _self._private.active_widget = nil @@ -263,7 +263,7 @@ local function create_app_widget(self, app) end end - return app_widget + return widget end local function search(self, text) @@ -303,7 +303,7 @@ local function search(self, text) for _, entry in pairs(self._private.matched_entries) do -- Only add the widgets for apps that are part of the first page if #self._private.grid.children + 1 <= self._private.max_apps_per_page then - self._private.grid:add(create_app_widget(self, entry)) + self._private.grid:add(app_widget(self, entry)) end end @@ -362,7 +362,7 @@ local function page_forward(self, dir) for index, entry in pairs(self._private.matched_entries) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(create_app_widget(self, entry)) + self._private.grid:add(app_widget(self, entry)) end end @@ -406,7 +406,7 @@ local function page_backward(self, dir) for index, entry in pairs(self._private.matched_entries) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(create_app_widget(self, entry)) + self._private.grid:add(app_widget(self, entry)) end end @@ -473,7 +473,7 @@ local function reset(self) for index, entry in pairs(self._private.all_entries) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then - self._private.grid:add(create_app_widget(self, entry)) + self._private.grid:add(app_widget(self, entry)) else break end From 1e694218dfabb3cd2dc9291be6902a8e1065a464 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Feb 2023 18:16:43 +0200 Subject: [PATCH 026/185] Add method to dynamically update app launcher favorites --- widget/app_launcher/init.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 122f86a1..3a58d1e5 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -573,6 +573,12 @@ local function generate_apps(self) end end +-- Sets favorites +function app_launcher:set_favorites(favorites) + self.favorites = favorites + generate_apps(self) +end + --- Scrolls up function app_launcher:scroll_up() scroll(self, "up") From 3cfff61ca60d8d332ea77a57e00332d790ad77a4 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 02:26:18 +0200 Subject: [PATCH 027/185] Refactor the icon theme helper --- helpers/icon_theme.lua | 140 ++++++++++++----------------------- helpers/init.lua | 1 + widget/app_launcher/init.lua | 11 +-- 3 files changed, 53 insertions(+), 99 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index c4d45832..5948369b 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -1,85 +1,42 @@ +------------------------------------------- +-- @author https://github.com/Kasper24 +-- @copyright 2021-2022 Kasper24 +------------------------------------------- local lgi = require("lgi") local Gio = lgi.Gio +local DesktopAppInfo = Gio.DesktopAppInfo local Gtk = lgi.require("Gtk", "3.0") -local gobject = require("gears.object") -local gtable = require("gears.table") -local setmetatable = setmetatable -local ipairs = ipairs -local icon_theme = { mt = {} } +local ICON_SIZE = 48 +local GTK_THEME = Gtk.IconTheme.get_default() -local name_lookup = -{ - ["jetbrains-studio"] = "android-studio" -} +local _icon_theme = {} -local function get_icon_by_pid_command(self, client, apps) - local pid = client.pid - if pid ~= nil then - local handle = io.popen(string.format("ps -p %d -o comm=", pid)) - local pid_command = handle:read("*a"):gsub("^%s*(.-)%s*$", "%1") - handle:close() - - for _, app in ipairs(apps) do - local executable = app:get_executable() - if executable and executable:find(pid_command, 1, true) then - return self:get_gicon_path(app:get_icon()) +function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) + local desktop_app_info_filename = DesktopAppInfo.search(client.class)[1][1] + if desktop_app_info_filename then + local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) + if desktop_app_info then + local icon_name = desktop_app_info:get_string("Icon") + if icon_name then + return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) end end end -end -local function get_icon_by_icon_name(self, client, apps) - local icon_name = client.icon_name and client.icon_name:lower() or nil - if icon_name ~= nil then - for _, app in ipairs(apps) do - local name = app:get_name():lower() - if name and name:find(icon_name, 1, true) then - return self:get_gicon_path(app:get_icon()) - end - end - end + _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) end -local function get_icon_by_class(self, client, apps) - if client.class ~= nil then - local class = name_lookup[client.class] or client.class:lower() - - -- Try to remove dashes - local class_1 = class:gsub("[%-]", "") - - -- Try to replace dashes with dot - local class_2 = class:gsub("[%-]", ".") - - -- Try to match only the first word - local class_3 = class:match("(.-)-") or class - class_3 = class_3:match("(.-)%.") or class_3 - class_3 = class_3:match("(.-)%s+") or class_3 - - local possible_icon_names = { class, class_3, class_2, class_1 } - for _, app in ipairs(apps) do - local id = app:get_id():lower() - for _, possible_icon_name in ipairs(possible_icon_names) do - if id and id:find(possible_icon_name, 1, true) then - return self:get_gicon_path(app:get_icon()) - end - end - end +function _icon_theme.choose_icon(icons_names, icon_theme, icon_size) + if icon_theme then + GTK_THEME = Gtk.IconTheme.new() + Gtk.IconTheme.set_custom_theme(GTK_THEME, icon_theme); + end + if icon_size then + ICON_SIZE = icon_size end -end - -function icon_theme:get_client_icon_path(client) - local apps = Gio.AppInfo.get_all() - - return get_icon_by_pid_command(self, client, apps) or - get_icon_by_icon_name(self, client, apps) or - get_icon_by_class(self, client, apps) or - client.icon or - self:choose_icon({"window", "window-manager", "xfwm4-default", "window_list" }) -end -function icon_theme:choose_icon(icons_names) - local icon_info = self.gtk_theme:choose_icon(icons_names, self.icon_size, 0); + local icon_info = GTK_THEME:choose_icon(icons_names, ICON_SIZE, 0); if icon_info then local icon_path = icon_info:get_filename() if icon_path then @@ -90,12 +47,20 @@ function icon_theme:choose_icon(icons_names) return "" end -function icon_theme:get_gicon_path(gicon) +function _icon_theme.get_gicon_path(gicon, icon_theme, icon_size) if gicon == nil then return "" end - local icon_info = self.gtk_theme:lookup_by_gicon(gicon, self.icon_size, 0); + if icon_theme then + GTK_THEME = Gtk.IconTheme.new() + Gtk.IconTheme.set_custom_theme(GTK_THEME, icon_theme); + end + if icon_size then + ICON_SIZE = icon_size + end + + local icon_info = GTK_THEME:lookup_by_gicon(gicon, ICON_SIZE, 0); if icon_info then local icon_path = icon_info:get_filename() if icon_path then @@ -106,8 +71,16 @@ function icon_theme:get_gicon_path(gicon) return "" end -function icon_theme:get_icon_path(icon_name) - local icon_info = self.gtk_theme:lookup_icon(icon_name, self.icon_size, 0) +function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) + if icon_theme then + GTK_THEME = Gtk.IconTheme.new() + Gtk.IconTheme.set_custom_theme(GTK_THEME, icon_theme); + end + if icon_size then + ICON_SIZE = icon_size + end + + local icon_info = GTK_THEME:lookup_icon(icon_name, ICON_SIZE, 0) if icon_info then local icon_path = icon_info:get_filename() if icon_path then @@ -118,25 +91,4 @@ function icon_theme:get_icon_path(icon_name) return "" end -local function new(theme_name, icon_size) - local ret = gobject{} - gtable.crush(ret, icon_theme, true) - - ret.name = theme_name or nil - ret.icon_size = icon_size or 48 - - if theme_name then - ret.gtk_theme = Gtk.IconTheme.new() - Gtk.IconTheme.set_custom_theme(ret.gtk_theme, theme_name); - else - ret.gtk_theme = Gtk.IconTheme.get_default() - end - - return ret -end - -function icon_theme.mt:__call(...) - return new(...) -end - -return setmetatable(icon_theme, icon_theme.mt) \ No newline at end of file +return _icon_theme \ No newline at end of file diff --git a/helpers/init.lua b/helpers/init.lua index f2c898e7..7e7f5d71 100644 --- a/helpers/init.lua +++ b/helpers/init.lua @@ -2,6 +2,7 @@ return { client = require(... .. ".client"), color = require(... .. ".color"), filesystem = require(... .. ".filesystem"), + icon_theme = require(... .. ".icon_theme"), shape = require(... .. ".shape"), time = require(... .. ".time"), } diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3a58d1e5..7029a38b 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -8,6 +8,7 @@ local wibox = require("wibox") local beautiful = require("beautiful") local color = require(tostring(...):match(".*bling") .. ".helpers.color") local prompt = require(... .. ".prompt") +local helpers = require(tostring(path):match(".*bling") .. ".helpers") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -532,14 +533,12 @@ local function generate_apps(self) end) end - local icon_theme = require(tostring(path):match(".*bling") .. ".helpers.icon_theme")(self.icon_theme, self.icon_size) - for _, app in ipairs(apps) do if app.should_show(app) then local name = app_info.get_name(app) local commandline = app_info.get_commandline(app) local executable = app_info.get_executable(app) - local icon = icon_theme:get_gicon_path(app_info.get_icon(app)) + local icon = helpers.icon_theme.get_gicon_path(app_info.get_icon(app), self.icon_theme, self.icon_size) -- Check if this app should be skipped, depanding on the skip_names / skip_commands table if not has_value(self.skip_names, name) and not has_value(self.skip_commands, commandline) then @@ -547,11 +546,13 @@ local function generate_apps(self) if icon ~= "" or self.skip_empty_icons == false then if icon == "" then if self.default_app_icon_name ~= nil then - icon = icon_theme:get_icon_path(self.default_app_icon_name) + icon = helpers.icon_theme.get_icon_path(self.default_app_icon_name, self.icon_theme, self.icon_size) elseif self.default_app_icon_path ~= nil then icon = self.default_app_icon_path else - icon = icon_theme:choose_icon({"application-all", "application", "application-default-icon", "app"}) + icon = helpers.icon_theme.choose_icon( + {"application-all", "application", "application-default-icon", "app"}, + self.icon_theme, self.icon_size) end end From 8de50aefb15fd5ec0d1714e4f36732b26dc4b16f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 02:31:36 +0200 Subject: [PATCH 028/185] whoooopsi --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 7029a38b..d8cf4ff9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -8,7 +8,6 @@ local wibox = require("wibox") local beautiful = require("beautiful") local color = require(tostring(...):match(".*bling") .. ".helpers.color") local prompt = require(... .. ".prompt") -local helpers = require(tostring(path):match(".*bling") .. ".helpers") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -17,6 +16,7 @@ local ipairs = ipairs local pairs = pairs local capi = { screen = screen, mouse = mouse } local path = ... +local helpers = require(tostring(path):match(".*bling") .. ".helpers") local app_launcher = { mt = {} } From 85910592e134e9fcb0d85c1da9d2f106e319e3c2 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 03:14:50 +0200 Subject: [PATCH 029/185] Use get_id here, it's just suffixed with .desktop in the end --- widget/app_launcher/init.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index d8cf4ff9..3dfd9e9f 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -493,11 +493,11 @@ local function generate_apps(self) if self.sort_alphabetically then table.sort(apps, function(a, b) local app_a_score = app_info.get_name(a):lower() - if has_value(self.favorites, app_info.get_name(a)) then + if has_value(self.favorites, app_info.get_id(a)) then app_a_score = "aaaaaaaaaaa" .. app_a_score end local app_b_score = app_info.get_name(b):lower() - if has_value(self.favorites, app_info.get_name(b)) then + if has_value(self.favorites, app_info.get_id(b)) then app_b_score = "aaaaaaaaaaa" .. app_b_score end @@ -506,11 +506,11 @@ local function generate_apps(self) elseif self.reverse_sort_alphabetically then table.sort(apps, function(a, b) local app_a_score = app_info.get_name(a):lower() - if has_value(self.favorites, app_info.get_name(a)) then + if has_value(self.favorites, app_info.get_id(a)) then app_a_score = "zzzzzzzzzzz" .. app_a_score end local app_b_score = app_info.get_name(b):lower() - if has_value(self.favorites, app_info.get_name(b)) then + if has_value(self.favorites, app_info.get_id(b)) then app_b_score = "zzzzzzzzzzz" .. app_b_score end @@ -518,8 +518,8 @@ local function generate_apps(self) end) else table.sort(apps, function(a, b) - local app_a_favorite = has_value(self.favorites, app_info.get_name(a)) - local app_b_favorite = has_value(self.favorites, app_info.get_name(b)) + local app_a_favorite = has_value(self.favorites, app_info.get_id(a)) + local app_b_favorite = has_value(self.favorites, app_info.get_id(b)) if app_a_favorite and not app_b_favorite then return true From 1ef57e3a5ef4e632fe507ecc6adf45788c64c127 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 03:32:33 +0200 Subject: [PATCH 030/185] return it --- helpers/icon_theme.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index 5948369b..4c6adb64 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -24,7 +24,7 @@ function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) end end - _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) + return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) end function _icon_theme.choose_icon(icons_names, icon_theme, icon_size) From 680fbb06ac03fb98a75d80e957d827ffda9b9a01 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 03:34:41 +0200 Subject: [PATCH 031/185] Makes more sense in this way --- helpers/icon_theme.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index 4c6adb64..cdad7b92 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -12,21 +12,6 @@ local GTK_THEME = Gtk.IconTheme.get_default() local _icon_theme = {} -function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) - local desktop_app_info_filename = DesktopAppInfo.search(client.class)[1][1] - if desktop_app_info_filename then - local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) - if desktop_app_info then - local icon_name = desktop_app_info:get_string("Icon") - if icon_name then - return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) - end - end - end - - return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) -end - function _icon_theme.choose_icon(icons_names, icon_theme, icon_size) if icon_theme then GTK_THEME = Gtk.IconTheme.new() @@ -91,4 +76,19 @@ function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) return "" end +function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) + local desktop_app_info_filename = DesktopAppInfo.search(client.class)[1][1] + if desktop_app_info_filename then + local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) + if desktop_app_info then + local icon_name = desktop_app_info:get_string("Icon") + if icon_name then + return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) + end + end + end + + return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) +end + return _icon_theme \ No newline at end of file From 4445f0b0fbf057c65edb4ec0aad2853dcf116102 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 03:40:16 +0200 Subject: [PATCH 032/185] This can error out --- helpers/icon_theme.lua | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index cdad7b92..0b1d35ad 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -1,7 +1,3 @@ -------------------------------------------- --- @author https://github.com/Kasper24 --- @copyright 2021-2022 Kasper24 -------------------------------------------- local lgi = require("lgi") local Gio = lgi.Gio local DesktopAppInfo = Gio.DesktopAppInfo @@ -77,13 +73,16 @@ function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) end function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) - local desktop_app_info_filename = DesktopAppInfo.search(client.class)[1][1] - if desktop_app_info_filename then - local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) - if desktop_app_info then - local icon_name = desktop_app_info:get_string("Icon") - if icon_name then - return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) + local desktop_app_info_filename_arr = DesktopAppInfo.search(client.class)[1] + if desktop_app_info_filename_arr then + local desktop_app_info_filename = desktop_app_info_filename_arr[1] + if desktop_app_info_filename then + local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) + if desktop_app_info then + local icon_name = desktop_app_info:get_string("Icon") + if icon_name then + return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) + end end end end From c8552e508d327ca38aae8edad53d7255fce8e76e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 03:58:08 +0200 Subject: [PATCH 033/185] Why there isn't an easier way to get a desktop file from a client --- helpers/icon_theme.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index 0b1d35ad..46345738 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -1,3 +1,7 @@ +------------------------------------------- +-- @author https://github.com/Kasper24 +-- @copyright 2021-2022 Kasper24 +------------------------------------------- local lgi = require("lgi") local Gio = lgi.Gio local DesktopAppInfo = Gio.DesktopAppInfo @@ -72,8 +76,8 @@ function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) return "" end -function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) - local desktop_app_info_filename_arr = DesktopAppInfo.search(client.class)[1] +local function _get_client_icon_path(name, icon_theme, icon_size) + local desktop_app_info_filename_arr = DesktopAppInfo.search(name)[1] if desktop_app_info_filename_arr then local desktop_app_info_filename = desktop_app_info_filename_arr[1] if desktop_app_info_filename then @@ -86,8 +90,12 @@ function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) end end end +end - return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) +function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) + return _get_client_icon_path(client.class, icon_theme, icon_size) or + _get_client_icon_path(client.name, icon_theme, icon_size) or + _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) end return _icon_theme \ No newline at end of file From 530619c848c9d0a80caff3c8096b0bd866847be5 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 14 Feb 2023 05:07:59 +0200 Subject: [PATCH 034/185] I swear this actually works now --- helpers/icon_theme.lua | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index 46345738..af512591 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -5,7 +5,10 @@ local lgi = require("lgi") local Gio = lgi.Gio local DesktopAppInfo = Gio.DesktopAppInfo +local AppInfo = Gio.DesktopAppInfo local Gtk = lgi.require("Gtk", "3.0") +local string = string +local ipairs = ipairs local ICON_SIZE = 48 local GTK_THEME = Gtk.IconTheme.get_default() @@ -76,26 +79,35 @@ function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) return "" end -local function _get_client_icon_path(name, icon_theme, icon_size) - local desktop_app_info_filename_arr = DesktopAppInfo.search(name)[1] - if desktop_app_info_filename_arr then - local desktop_app_info_filename = desktop_app_info_filename_arr[1] - if desktop_app_info_filename then - local desktop_app_info = DesktopAppInfo.new(desktop_app_info_filename) - if desktop_app_info then - local icon_name = desktop_app_info:get_string("Icon") - if icon_name then - return _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) +function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) + local app_list = AppInfo.get_all() + + local class = string.lower(client.class) + local name = string.lower(client.name) + + for _, app in ipairs(app_list) do + local id = app:get_id() + local desktop_app_info = DesktopAppInfo.new(id) + if desktop_app_info then + local props = { + string.lower(desktop_app_info:get_string("Name") or ""), + string.lower(desktop_app_info:get_filename() or ""), + string.lower(desktop_app_info:get_startup_wm_class() or ""), + string.lower(desktop_app_info:get_string("Icon") or ""), + string.lower(desktop_app_info:get_string("Exec") or ""), + string.lower(desktop_app_info:get_string("Keywords") or "") + } + + for _, prop in ipairs(props) do + if prop ~= "" and (prop == class or prop == name) then + local icon = desktop_app_info:get_string("Icon") + return _icon_theme.get_icon_path(icon, icon_theme, icon_size) end end end end -end -function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) - return _get_client_icon_path(client.class, icon_theme, icon_size) or - _get_client_icon_path(client.name, icon_theme, icon_size) or - _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) + return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) end return _icon_theme \ No newline at end of file From 8530b170997a8138c655452ec7a73ec56a805869 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Feb 2023 03:19:05 +0200 Subject: [PATCH 035/185] Nemo WM_CLASS='nemo' while it's desktop name prop='Files', but hey at least I can match by id --- helpers/icon_theme.lua | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index af512591..22a7a66b 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -90,18 +90,21 @@ function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) local desktop_app_info = DesktopAppInfo.new(id) if desktop_app_info then local props = { - string.lower(desktop_app_info:get_string("Name") or ""), - string.lower(desktop_app_info:get_filename() or ""), - string.lower(desktop_app_info:get_startup_wm_class() or ""), - string.lower(desktop_app_info:get_string("Icon") or ""), - string.lower(desktop_app_info:get_string("Exec") or ""), - string.lower(desktop_app_info:get_string("Keywords") or "") + id:gsub(".desktop", ""), + desktop_app_info:get_string("Name"), + desktop_app_info:get_filename(), + desktop_app_info:get_startup_wm_class(), + desktop_app_info:get_string("Icon"), + desktop_app_info:get_string("Exec"), + desktop_app_info:get_string("Keywords") } for _, prop in ipairs(props) do - if prop ~= "" and (prop == class or prop == name) then + if prop ~= nil and (prop:lower() == class or prop:lower() == name) then local icon = desktop_app_info:get_string("Icon") - return _icon_theme.get_icon_path(icon, icon_theme, icon_size) + if icon ~= nil then + return _icon_theme.get_icon_path(icon, icon_theme, icon_size) + end end end end From eda37036351fb633ae9c62bb57ba185067caa943 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 02:25:43 +0200 Subject: [PATCH 036/185] Use the start_new func --- widget/app_launcher/init.lua | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3dfd9e9f..2c971bcc 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -624,28 +624,18 @@ function app_launcher:show() if animation.x then animation.x.ended:unsubscribe() animation.x:set(self._private.widget.goal_x) - gtimer { - timeout = 0.01, - call_now = false, - autostart = true, - single_shot = true, - callback = function() - screen.app_launcher.visible = true - end - } + gtimer.start_new(0.01, function() + screen.app_launcher.visible = true + return false + end) end if animation.y then animation.y.ended:unsubscribe() animation.y:set(self._private.widget.goal_y) - gtimer { - timeout = 0.01, - call_now = false, - autostart = true, - single_shot = true, - callback = function() - screen.app_launcher.visible = true - end - } + gtimer.start_new(0.01, function() + screen.app_launcher.visible = true + return false + end) end else screen.app_launcher.visible = true From c975a0de9aa7daeedf29954555007ae91c0db5ad Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:33:53 +0200 Subject: [PATCH 037/185] Reduce amount of configuration options --- widget/app_launcher/init.lua | 134 ++++++++--------------------------- 1 file changed, 31 insertions(+), 103 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 2c971bcc..5b10a8d7 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -94,43 +94,33 @@ local function app_widget(self, app) local widget = nil if self.app_template == nil then - local icon = self.app_show_icon == true and + local icon = wibox.widget { widget = wibox.widget.imagebox, id = "icon_role", - halign = self.app_icon_halign, - forced_width = self.app_icon_width, - forced_height = self.app_icon_height, + forced_width = dpi(70), + forced_height = dpi(70), image = app.icon - } or nil + } - local name = self.app_show_name == true and + local name = wibox.widget { widget = wibox.widget.textbox, id = "name_role", font = self.app_name_font, markup = string.format("%s", self.app_name_normal_color, app.name) - } or nil - - local generic_name = app.generic_name ~= nil and self.app_show_generic_name == true and - { - widget = wibox.widget.textbox, - id = "generic_name_role", - font = self.app_name_font, - markup = app.generic_name ~= "" and " (" .. app.generic_name .. ")" or "" - } or nil + } widget = wibox.widget { widget = wibox.container.background, id = "background_role", - forced_width = self.app_width, - forced_height = self.app_height, - shape = self.app_shape, + forced_width = dpi(300), + forced_height = dpi(120), bg = self.app_normal_color, { widget = wibox.container.margin, - margins = self.app_content_padding, + margins = dpi(10), { -- Using this hack instead of container.place because that will fuck with the name/icon halign layout = wibox.layout.align.vertical, @@ -138,18 +128,9 @@ local function app_widget(self, app) nil, { layout = wibox.layout.fixed.vertical, - spacing = self.app_content_spacing, + spacing = dpi(10), icon, - { - widget = wibox.container.place, - halign = self.app_name_halign, - { - layout = wibox.layout.fixed.horizontal, - spacing = self.app_name_generic_name_spacing, - name, - generic_name - } - } + name }, nil } @@ -750,30 +731,14 @@ local function new(args) args.apps_spacing = args.apps_spacing or dpi(30) args.expand_apps = args.expand_apps == nil and true or args.expand_apps - args.prompt_height = args.prompt_height or dpi(100) - args.prompt_margins = args.prompt_margins or dpi(0) - args.prompt_paddings = args.prompt_paddings or dpi(30) - args.prompt_shape = args.prompt_shape or nil args.prompt_color = args.prompt_color or beautiful.fg_normal or "#FFFFFF" - args.prompt_border_width = args.prompt_border_width or beautiful.border_width or dpi(0) - args.prompt_border_color = args.prompt_border_color or beautiful.border_color or args.prompt_color - args.prompt_text_halign = args.prompt_text_halign or "left" - args.prompt_text_valign = args.prompt_text_valign or "center" - args.prompt_icon_text_spacing = args.prompt_icon_text_spacing or dpi(10) - args.prompt_show_icon = args.prompt_show_icon == nil and true or args.prompt_show_icon args.prompt_icon_font = args.prompt_icon_font or beautiful.font args.prompt_icon_color = args.prompt_icon_color or beautiful.bg_normal or "#000000" args.prompt_icon = args.prompt_icon or "" - args.prompt_icon_markup = args.prompt_icon_markup or string.format("%s", args.prompt_icon_color, args.prompt_icon) - args.prompt_text = args.prompt_text or "Search: " - args.prompt_start_text = args.prompt_start_text or "" + args.prompt_label = args.prompt_label or "Search: " args.prompt_font = args.prompt_font or beautiful.font args.prompt_text_color = args.prompt_text_color or beautiful.bg_normal or "#000000" - args.prompt_cursor_color = args.prompt_cursor_color or beautiful.bg_normal or "#000000" - args.app_width = args.app_width or dpi(300) - args.app_height = args.app_height or dpi(120) - args.app_shape = args.app_shape or nil args.app_normal_color = args.app_normal_color or beautiful.bg_normal or "#000000" args.app_normal_hover_color = args.app_normal_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_normal_color), 2.5)) or @@ -782,19 +747,9 @@ local function new(args) args.app_selected_hover_color = args.app_selected_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 2.5)) or color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 0.5)) - args.app_content_padding = args.app_content_padding or dpi(10) - args.app_content_spacing = args.app_content_spacing or dpi(10) - args.app_show_icon = args.app_show_icon == nil and true or args.app_show_icon - args.app_icon_halign = args.app_icon_halign or "center" - args.app_icon_width = args.app_icon_width or dpi(70) - args.app_icon_height = args.app_icon_height or dpi(70) - args.app_show_name = args.app_show_name == nil and true or args.app_show_name - args.app_name_generic_name_spacing = args.app_name_generic_name_spacing or dpi(0) - args.app_name_halign = args.app_name_halign or "center" - args.app_name_font = args.app_name_font or beautiful.font args.app_name_normal_color = args.app_name_normal_color or beautiful.fg_normal or "#FFFFFF" args.app_name_selected_color = args.app_name_selected_color or beautiful.bg_normal or "#000000" - args.app_show_generic_name = args.app_show_generic_name ~= nil and args.app_show_generic_name or false + args.app_name_font = args.app_name_font or beautiful.font local ret = gobject {} ret._private = {} @@ -806,11 +761,10 @@ local function new(args) -- These widgets need to be later accessed ret._private.prompt = prompt { - prompt = ret.prompt_text, - text = ret.prompt_start_text, + prompt = ret.prompt_label, font = ret.prompt_font, reset_on_stop = ret.reset_on_hide, - bg_cursor = ret.prompt_cursor_color, + bg_cursor = beautiful.bg_normal or "#000000", history_path = ret.save_history == true and gfilesystem.get_cache_dir() .. "/history" or nil, changed_callback = function(text) if text == ret._private.text then @@ -884,33 +838,26 @@ local function new(args) { layout = wibox.layout.fixed.vertical, { - widget = wibox.container.margin, - margins = ret.prompt_margins, + widget = wibox.container.background, + forced_height = dpi(100), + bg = ret.prompt_color, + fg = ret.prompt_text_color, { - widget = wibox.container.background, - forced_height = ret.prompt_height, - shape = ret.prompt_shape, - bg = ret.prompt_color, - fg = ret.prompt_text_color, - border_width = ret.prompt_border_width, - border_color = ret.prompt_border_color, + widget = wibox.container.margin, + margins = dpi(30), { - widget = wibox.container.margin, - margins = ret.prompt_paddings, + widget = wibox.container.place, + halign = "left", + valign = "center", { - widget = wibox.container.place, - halign = ret.prompt_text_halign, - valign = ret.prompt_text_valign, + layout = wibox.layout.fixed.horizontal, + spacing = dpi(10), { - layout = wibox.layout.fixed.horizontal, - spacing = ret.prompt_icon_text_spacing, - { - widget = wibox.widget.textbox, - font = ret.prompt_icon_font, - markup = ret.prompt_icon_markup - }, - ret._private.prompt.textbox - } + widget = wibox.widget.textbox, + font = ret.prompt_icon_font, + markup = string.format("%s", args.prompt_icon_color, args.prompt_icon) + }, + ret._private.prompt.textbox } } } @@ -979,25 +926,6 @@ local function new(args) return ret end -function app_launcher.text(args) - args = args or {} - - args.prompt_height = args.prompt_height or dpi(50) - args.prompt_margins = args.prompt_margins or dpi(30) - args.prompt_paddings = args.prompt_paddings or dpi(15) - args.app_width = args.app_width or dpi(400) - args.app_height = args.app_height or dpi(40) - args.apps_spacing = args.apps_spacing or dpi(10) - args.apps_per_row = args.apps_per_row or 15 - args.apps_per_column = args.apps_per_column or 1 - args.app_name_halign = args.app_name_halign or "left" - args.app_show_icon = args.app_show_icon ~= nil and args.app_show_icon or false - args.app_show_generic_name = args.app_show_generic_name == nil and true or args.app_show_generic_name - args.apps_margin = args.apps_margin or { left = dpi(40), right = dpi(40), bottom = dpi(30) } - - return new(args) -end - function app_launcher.mt:__call(...) return new(...) end From 7a20f91fb61d7c730e4a9a8b35814e1fc9e0a0c2 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:36:03 +0200 Subject: [PATCH 038/185] Rename prompt_color to prompt_bg_color --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 5b10a8d7..aff26a1f 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -731,7 +731,7 @@ local function new(args) args.apps_spacing = args.apps_spacing or dpi(30) args.expand_apps = args.expand_apps == nil and true or args.expand_apps - args.prompt_color = args.prompt_color or beautiful.fg_normal or "#FFFFFF" + args.prompt_bg_color = args.prompt_bg_color or beautiful.fg_normal or "#FFFFFF" args.prompt_icon_font = args.prompt_icon_font or beautiful.font args.prompt_icon_color = args.prompt_icon_color or beautiful.bg_normal or "#000000" args.prompt_icon = args.prompt_icon or "" @@ -840,7 +840,7 @@ local function new(args) { widget = wibox.container.background, forced_height = dpi(100), - bg = ret.prompt_color, + bg = ret.prompt_bg_color, fg = ret.prompt_text_color, { widget = wibox.container.margin, From d4911c99c25bba9ad2f47033c205331637f02c93 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:36:09 +0200 Subject: [PATCH 039/185] set raw to true --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index aff26a1f..d6aaed6e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -755,8 +755,8 @@ local function new(args) ret._private = {} ret._private.text = "" - gtable.crush(ret, app_launcher) - gtable.crush(ret, args) + gtable.crush(ret, app_launcher, true) + gtable.crush(ret, args, true) -- These widgets need to be later accessed ret._private.prompt = prompt From 6e77e55d04a1ea7d809063767d1c6ed241e292a1 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:36:14 +0200 Subject: [PATCH 040/185] No need to recreate the timer --- widget/app_launcher/init.lua | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index d6aaed6e..914c6c17 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -758,7 +758,15 @@ local function new(args) gtable.crush(ret, app_launcher, true) gtable.crush(ret, args, true) - -- These widgets need to be later accessed + ret._private.search_timer = gtimer { + timeout = 0.05, + autostart = true, + single_shot = true, + callback = function() + search(ret, ret._private.text) + end + } + ret._private.prompt = prompt { prompt = ret.prompt_label, @@ -771,20 +779,8 @@ local function new(args) return end - if ret._private.search_timer ~= nil and ret._private.search_timer.started then - ret._private.search_timer:stop() - end - - ret._private.search_timer = gtimer { - timeout = 0.05, - autostart = true, - single_shot = true, - callback = function() - search(ret, text) - end - } - ret._private.text = text + ret._private.search_timer:again() end, keypressed_callback = function(mod, key, cmd) if key == "Escape" then From 1f5cdbf1296edd0401924a7a398f62c38186184e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:45:24 +0200 Subject: [PATCH 041/185] Add a default_value function to improve the syntax of setting defaults --- widget/app_launcher/init.lua | 105 +++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 914c6c17..51845984 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -25,6 +25,14 @@ local INOTIFY_SCRIPT = [[ bash -c "while (inotifywait -e modify /usr/share/appli local AWESOME_SENSIBLE_TERMINAL_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "awesome-sensible-terminal" +local function default_value(value, default) + if value == nil then + return default + else + return value + end +end + local function string_levenshtein(str1, str2) local len1 = string.len(str1) local len2 = string.len(str2) @@ -693,51 +701,50 @@ end local function new(args) args = args or {} - args.favorites = args.favorites or {} - args.search_commands = args.search_commands == nil and true or args.search_commands - args.skip_names = args.skip_names or {} - args.skip_commands = args.skip_commands or {} - args.skip_empty_icons = args.skip_empty_icons ~= nil and args.skip_empty_icons or false - args.sort_alphabetically = args.sort_alphabetically == nil and true or args.sort_alphabetically - args.reverse_sort_alphabetically = args.reverse_sort_alphabetically ~= nil and args.reverse_sort_alphabetically or false - args.select_before_spawn = args.select_before_spawn == nil and true or args.select_before_spawn - args.hide_on_left_clicked_outside = args.hide_on_left_clicked_outside == nil and true or args.hide_on_left_clicked_outside - args.hide_on_right_clicked_outside = args.hide_on_right_clicked_outside == nil and true or args.hide_on_right_clicked_outside - args.hide_on_launch = args.hide_on_launch == nil and true or args.hide_on_launch - args.try_to_keep_index_after_searching = args.try_to_keep_index_after_searching ~= nil and args.try_to_keep_index_after_searching or false - args.reset_on_hide = args.reset_on_hide == nil and true or args.reset_on_hide - args.save_history = args.save_history == nil and true or args.save_history - args.wrap_page_scrolling = args.wrap_page_scrolling == nil and true or args.wrap_page_scrolling - args.wrap_app_scrolling = args.wrap_app_scrolling == nil and true or args.wrap_app_scrolling - - args.type = args.type or "dock" - args.show_on_focused_screen = args.show_on_focused_screen == nil and true or args.show_on_focused_screen - args.screen = args.screen or capi.screen.primary - args.placement = args.placement or awful.placement.centered - args.rubato = args.rubato or nil - args.background = args.background or "#000000" - args.border_width = args.border_width or beautiful.border_width or dpi(0) - args.border_color = args.border_color or beautiful.border_color or "#FFFFFF" - args.shape = args.shape or nil - - args.default_app_icon_name = args.default_app_icon_name or nil - args.default_app_icon_path = args.default_app_icon_path or nil - args.icon_theme = args.icon_theme or nil - args.icon_size = args.icon_size or nil - - args.apps_per_row = args.apps_per_row or 5 - args.apps_per_column = args.apps_per_column or 3 - args.apps_margin = args.apps_margin or dpi(30) - args.apps_spacing = args.apps_spacing or dpi(30) - args.expand_apps = args.expand_apps == nil and true or args.expand_apps - - args.prompt_bg_color = args.prompt_bg_color or beautiful.fg_normal or "#FFFFFF" - args.prompt_icon_font = args.prompt_icon_font or beautiful.font - args.prompt_icon_color = args.prompt_icon_color or beautiful.bg_normal or "#000000" - args.prompt_icon = args.prompt_icon or "" - args.prompt_label = args.prompt_label or "Search: " - args.prompt_font = args.prompt_font or beautiful.font - args.prompt_text_color = args.prompt_text_color or beautiful.bg_normal or "#000000" + args.favorites = default_value(args.favorites, {}) + args.search_commands = default_value(args.search_commands, true) + args.skip_names = default_value(args.skip_names, {}) + args.skip_commands = default_value(args.skip_commands, {}) + args.skip_empty_icons = default_value(args.skip_empty_icons, false) + args.sort_alphabetically = default_value(args.sort_alphabetically, true) + args.reverse_sort_alphabetically = default_value(args.reverse_sort_alphabetically, false) + args.select_before_spawn = default_value(args.select_before_spawn, true) + args.hide_on_left_clicked_outside = default_value(args.hide_on_left_clicked_outside, true) + args.hide_on_right_clicked_outside = default_value(args.hide_on_right_clicked_outside, true) + args.hide_on_launch = default_value(args.hide_on_launch, true) + args.try_to_keep_index_after_searching = default_value(args.try_to_keep_index_after_searching, false) + args.reset_on_hide = default_value(args.reset_on_hide, true) + args.save_history = default_value(args.save_history, true) + args.wrap_page_scrolling = default_value(args.wrap_page_scrolling, true) + args.wrap_app_scrolling = default_value(args.wrap_app_scrolling, true) + + args.type = default_value(args.type, "dock") + args.show_on_focused_screen = default_value(args.show_on_focused_screen, true) + args.screen = default_value(args.screen, capi.screen.primary) + args.placement = default_value(args.placement, awful.placement.centered) + args.rubato = default_value(args.rubato, nil) + args.background = default_value(args.background, "#000000") + args.border_width = default_value(args.border_width, beautiful.border_width or dpi(0)) + args.border_color = default_value(args.border_color, beautiful.border_color or "#FFFFFF") + args.shape = default_value(args.shape, nil) + + args.default_app_icon_name = default_value(args.default_app_icon_name, nil) + args.default_app_icon_path = default_value(args.default_app_icon_path, nil) + args.icon_theme = default_value(args.icon_theme, nil) + args.icon_size = default_value(args.icon_size, nil) + + args.apps_per_row = default_value(args.apps_per_row, 5) + args.apps_per_column = default_value(args.apps_per_column, 3) + args.apps_spacing = default_value(args.apps_spacing, dpi(30)) + args.expand_apps = default_value(args.expand_apps, true) + + args.prompt_bg_color = default_value(args.prompt_bg_color, beautiful.fg_normal or "#FFFFFF") + args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) + args.prompt_icon_color = default_value(args.prompt_icon_color, beautiful.bg_normal or "#000000") + args.prompt_icon = default_value(args.prompt_icon, "") + args.prompt_label = default_value(args.prompt_label, "Search: ") + args.prompt_font = default_value(args.prompt_font, beautiful.font) + args.prompt_text_color = default_value(args.prompt_text_color, beautiful.bg_normal or "#000000") args.app_normal_color = args.app_normal_color or beautiful.bg_normal or "#000000" args.app_normal_hover_color = args.app_normal_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and @@ -747,9 +754,9 @@ local function new(args) args.app_selected_hover_color = args.app_selected_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 2.5)) or color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 0.5)) - args.app_name_normal_color = args.app_name_normal_color or beautiful.fg_normal or "#FFFFFF" - args.app_name_selected_color = args.app_name_selected_color or beautiful.bg_normal or "#000000" - args.app_name_font = args.app_name_font or beautiful.font + args.app_name_normal_color = default_value(args.app_name_normal_color, beautiful.fg_normal or "#FFFFFF") + args.app_name_selected_color = default_value(args.app_name_selected_color, beautiful.bg_normal or "#000000") + args.app_name_font = default_value(args.app_name_font, beautiful.font) local ret = gobject {} ret._private = {} @@ -860,7 +867,7 @@ local function new(args) }, { widget = wibox.container.margin, - margins = ret.apps_margin, + margins = dpi(30), ret._private.grid } } From a6c337669482e72fd04ff4feae06cd6090fb55a6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 17:47:54 +0200 Subject: [PATCH 042/185] Formatting --- widget/app_launcher/init.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 51845984..469e2e48 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -759,12 +759,15 @@ local function new(args) args.app_name_font = default_value(args.app_name_font, beautiful.font) local ret = gobject {} - ret._private = {} - ret._private.text = "" - gtable.crush(ret, app_launcher, true) gtable.crush(ret, args, true) + ret._private = {} + ret._private.text = "" + ret._private.max_apps_per_page = ret.apps_per_column * ret.apps_per_row + ret._private.apps_per_page = ret._private.max_apps_per_page + ret._private.pages_count = 0 + ret._private.current_page = 1 ret._private.search_timer = gtimer { timeout = 0.05, autostart = true, @@ -774,6 +777,7 @@ local function new(args) end } + ret._private.prompt = prompt { prompt = ret.prompt_label, @@ -873,12 +877,6 @@ local function new(args) } } - -- Private variables to be used to be used by the scrolling and searching functions - ret._private.max_apps_per_page = ret.apps_per_column * ret.apps_per_row - ret._private.apps_per_page = ret._private.max_apps_per_page - ret._private.pages_count = 0 - ret._private.current_page = 1 - if ret.rubato and ret.rubato.x then ret.rubato.x:subscribe(function(pos) ret._private.widget.x = pos From 21f0bd29689a11f37c36550e025075b85c7da279 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Feb 2023 18:01:01 +0200 Subject: [PATCH 043/185] Improve templates support --- widget/app_launcher/init.lua | 208 ++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 101 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 469e2e48..3a07e941 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -7,7 +7,7 @@ local gfilesystem = require("gears.filesystem") local wibox = require("wibox") local beautiful = require("beautiful") local color = require(tostring(...):match(".*bling") .. ".helpers.color") -local prompt = require(... .. ".prompt") +local prompt_widget = require(... .. ".prompt") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -563,6 +563,111 @@ local function generate_apps(self) end end +local function build_widget(self) + local widget = self.widget_template() + + self._private.prompt = widget:get_children_by_id("prompt_role")[1] or prompt_widget + { + prompt = self.prompt_label, + font = self.prompt_font, + reset_on_stop = self.reset_on_hide, + bg_cursor = beautiful.bg_normal or "#000000", + history_path = self.save_history == true and gfilesystem.get_cache_dir() .. "/history" or nil, + } + self._private.grid = widget:get_children_by_id("grid_role")[1] or wibox.widget + { + layout = wibox.layout.grid, + orientation = "horizontal", + homogeneous = true, + expand = self.expand_apps, + spacing = self.apps_spacing, + forced_num_cols = self.apps_per_column, + forced_num_rows = self.apps_per_row, + buttons = + { + awful.button({}, 4, function() self:scroll_up() end), + awful.button({}, 5, function() self:scroll_down() end) + } + } + self._private.widget = awful.popup + { + type = self.type, + visible = false, + ontop = true, + placement = self.placement, + border_width = self.border_width, + border_color = self.border_color, + shape = self.shape, + bg = self.background, + widget = self.widget_template() or + { + layout = wibox.layout.fixed.vertical, + { + widget = wibox.container.background, + forced_height = dpi(100), + bg = self.prompt_bg_color, + fg = self.prompt_text_color, + { + widget = wibox.container.margin, + margins = dpi(30), + { + widget = wibox.container.place, + halign = "left", + valign = "center", + { + layout = wibox.layout.fixed.horizontal, + spacing = dpi(10), + { + widget = wibox.widget.textbox, + font = self.prompt_icon_font, + markup = string.format("%s", args.prompt_icon_color, args.prompt_icon) + }, + self._private.prompt.textbox + } + } + } + }, + { + widget = wibox.container.margin, + margins = dpi(30), + self._private.grid + } + } + } + + self._private.prompt:connect_signal("text::changed", function(text) + if text == self._private.text then + return + end + + self._private.text = text + self._private.search_timer:again() + end) + + self._private.prompt:connect_signal("key::press", function(mod, key, cmd) + if key == "Escape" then + self:hide() + end + if key == "Return" then + if self._private.active_widget ~= nil then + self._private.active_widget:spawn() + end + end + if key == "Up" then + self:scroll_up() + end + if key == "Down" then + self:scroll_down() + end + if key == "Left" then + self:scroll_left() + end + if key == "Right" then + self:scroll_right() + end + end) +end + -- Sets favorites function app_launcher:set_favorites(favorites) self.favorites = favorites @@ -777,106 +882,6 @@ local function new(args) end } - - ret._private.prompt = prompt - { - prompt = ret.prompt_label, - font = ret.prompt_font, - reset_on_stop = ret.reset_on_hide, - bg_cursor = beautiful.bg_normal or "#000000", - history_path = ret.save_history == true and gfilesystem.get_cache_dir() .. "/history" or nil, - changed_callback = function(text) - if text == ret._private.text then - return - end - - ret._private.text = text - ret._private.search_timer:again() - end, - keypressed_callback = function(mod, key, cmd) - if key == "Escape" then - ret:hide() - end - if key == "Return" then - if ret._private.active_widget ~= nil then - ret._private.active_widget:spawn() - end - end - if key == "Up" then - ret:scroll_up() - end - if key == "Down" then - ret:scroll_down() - end - if key == "Left" then - ret:scroll_left() - end - if key == "Right" then - ret:scroll_right() - end - end - } - ret._private.grid = wibox.widget - { - layout = wibox.layout.grid, - orientation = "horizontal", - homogeneous = true, - expand = ret.expand_apps, - spacing = ret.apps_spacing, - forced_num_cols = ret.apps_per_column, - forced_num_rows = ret.apps_per_row, - buttons = - { - awful.button({}, 4, function() ret:scroll_up() end), - awful.button({}, 5, function() ret:scroll_down() end) - } - } - ret._private.widget = awful.popup - { - type = args.type, - visible = false, - ontop = true, - placement = ret.placement, - border_width = ret.border_width, - border_color = ret.border_color, - shape = ret.shape, - bg = ret.background, - widget = args.widget_template ~= nil and args.widget_template(ret._private.prompt.textbox, ret._private.grid) or - { - layout = wibox.layout.fixed.vertical, - { - widget = wibox.container.background, - forced_height = dpi(100), - bg = ret.prompt_bg_color, - fg = ret.prompt_text_color, - { - widget = wibox.container.margin, - margins = dpi(30), - { - widget = wibox.container.place, - halign = "left", - valign = "center", - { - layout = wibox.layout.fixed.horizontal, - spacing = dpi(10), - { - widget = wibox.widget.textbox, - font = ret.prompt_icon_font, - markup = string.format("%s", args.prompt_icon_color, args.prompt_icon) - }, - ret._private.prompt.textbox - } - } - } - }, - { - widget = wibox.container.margin, - margins = dpi(30), - ret._private.grid - } - } - } - if ret.rubato and ret.rubato.x then ret.rubato.x:subscribe(function(pos) ret._private.widget.x = pos @@ -921,6 +926,7 @@ local function new(args) end}) end) + build_widget(ret) generate_apps(ret) reset(ret) From b8b3e8a7124a68c9462879963151d3cf5fe6c4f8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:02:20 +0200 Subject: [PATCH 044/185] Refactor the prompt widget --- widget/app_launcher/prompt.lua | 826 +++++++++++++-------------------- 1 file changed, 320 insertions(+), 506 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index fae3b86c..bade091a 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 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 string = string +local capi = { + awesome = awesome, + root = root, + mouse = mouse, + tag = tag, + client = client +} + +local prompt = { + mt = {} +} + +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 +local function have_multibyte_char_at(text, position) + return text:sub(position, position):wlen() == -1 end -local function history_items(id) - if data.history[id] then - return #data.history[id].table - else - return -1 +local function generate_markup(self, show_cursor) + local wp = self._private + + 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 -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) + local markup = "" + if wp.icon ~= nil then + markup = string.format( + '%s ', + wp.icon.font, icon_size, wp.icon_color, wp.icon.icon) + end - -- Do not exceed our max_cmd - if #data.history[id].table > data.history[id].max then - table.remove(data.history[id].table, 1) - end + if show_cursor == true then + local char, spacer, text_start, text_end - history_save(id) + if #text < wp.cur_pos then + char = " " + spacer = "" + text_start = gstring.xml_escape(text) + text_end = "" 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) + 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 - 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" - - 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 = "" + 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 - local offset = 0 - if have_multibyte_char_at(text, args.cursor_pos) then - offset = 1 - 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', + 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 + self:set_markup(markup) +end - ret = _prompt .. text_start .. "" .. char .. "" .. text_end .. spacer +local function paste(self) + local wp = self._private - return ret -end + 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 -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 }) + 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: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 - - 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}) +function prompt:toggle_obscure() + self:set_obscure(not self._private.obscure) +end - self._private.search_term = nil +function prompt:set_text(text) + self._private.text = text + self._private.cur_pos = #text + 1 + generate_markup(self, self._private.state) +end - history_check_load(self.history_path, self.history_max) - local history_index = history_items(self.history_path) + 1 +function prompt:get_text() + return self._private.text +end - -- The completion element to use on completion request. - local ncomp = 1 +function prompt:start() + local wp = self._private + wp.state = true - local command_before_comp - local cur_pos_before_comp + capi.awesome.emit_signal("prompt::toggled_on", self) + generate_markup(self, true) - 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 + wp.cur_pos = wp.cur_pos + 1 + else + if wp.round and key == "." then + return 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 + if wp.only_numbers and tonumber(wp.text .. key) == nil then + return end - else + -- 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 {} - - 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 +function prompt:toggle() + local wp = self._private + + 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) + + 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 - return ret + 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) From eb7c1aa409310192c15972ee43cdf0d0321082f9 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:28:03 +0200 Subject: [PATCH 045/185] It's the 1st widget in the layout, no need to search for it --- widget/app_launcher/init.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3a07e941..e9a1ba53 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -122,7 +122,6 @@ local function app_widget(self, app) widget = wibox.widget { widget = wibox.container.background, - id = "background_role", forced_width = dpi(300), forced_height = dpi(120), bg = self.app_normal_color, @@ -152,9 +151,9 @@ local function app_widget(self, app) end if widget.selected then - widget:get_children_by_id("background_role")[1].bg = self.app_selected_hover_color + widget.bg = self.app_selected_hover_color else - widget:get_children_by_id("background_role")[1].bg = self.app_normal_hover_color + widget.bg = self.app_normal_hover_color end end) @@ -165,9 +164,9 @@ local function app_widget(self, app) end if widget.selected then - widget:get_children_by_id("background_role")[1].bg = self.app_selected_color + widget.bg = self.app_selected_color else - widget:get_children_by_id("background_role")[1].bg = self.app_normal_color + widget.bg = self.app_normal_color end end) else From e5deb85ac3de0f3b8d4f4dfffda8b8d04c1e3f7a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:28:11 +0200 Subject: [PATCH 046/185] Not used --- widget/app_launcher/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index e9a1ba53..1c9ed229 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -3,7 +3,6 @@ local awful = require("awful") local gobject = require("gears.object") local gtable = require("gears.table") local gtimer = require("gears.timer") -local gfilesystem = require("gears.filesystem") local wibox = require("wibox") local beautiful = require("beautiful") local color = require(tostring(...):match(".*bling") .. ".helpers.color") From 68482b16cd969e10bd836bc4ef6dba8eea197de8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:28:19 +0200 Subject: [PATCH 047/185] Just get it from helpers --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 1c9ed229..aa075cf6 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -5,7 +5,6 @@ local gtable = require("gears.table") local gtimer = require("gears.timer") local wibox = require("wibox") local beautiful = require("beautiful") -local color = require(tostring(...):match(".*bling") .. ".helpers.color") local prompt_widget = require(... .. ".prompt") local dpi = beautiful.xresources.apply_dpi local string = string @@ -16,6 +15,7 @@ local pairs = pairs local capi = { screen = screen, mouse = mouse } local path = ... local helpers = require(tostring(path):match(".*bling") .. ".helpers") +local color = helpers.color local app_launcher = { mt = {} } From 8812678c5a0aad78f51f55a67d2810b56265747e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:28:47 +0200 Subject: [PATCH 048/185] Use the new prompt widget and improve widget_template support --- widget/app_launcher/init.lua | 96 +++++++++++++++++------------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index aa075cf6..c3fba8b2 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -562,49 +562,41 @@ local function generate_apps(self) end local function build_widget(self) - local widget = self.widget_template() - - self._private.prompt = widget:get_children_by_id("prompt_role")[1] or prompt_widget - { - prompt = self.prompt_label, - font = self.prompt_font, - reset_on_stop = self.reset_on_hide, - bg_cursor = beautiful.bg_normal or "#000000", - history_path = self.save_history == true and gfilesystem.get_cache_dir() .. "/history" or nil, - } - self._private.grid = widget:get_children_by_id("grid_role")[1] or wibox.widget - { - layout = wibox.layout.grid, - orientation = "horizontal", - homogeneous = true, - expand = self.expand_apps, - spacing = self.apps_spacing, - forced_num_cols = self.apps_per_column, - forced_num_rows = self.apps_per_row, - buttons = + local widget = self.widget_template + if widget ~= nil then + self._private.prompt = widget:get_children_by_id("prompt_role")[1] + self._private.grid = widget:get_children_by_id("grid_role")[1] + else + self._private.prompt = wibox.widget { - awful.button({}, 4, function() self:scroll_up() end), - awful.button({}, 5, function() self:scroll_down() end) + widget = prompt_widget, + label = self.prompt_label, + font = self.prompt_font, + reset_on_stop = self.reset_on_hide, + bg_cursor = beautiful.bg_normal or "#000000", } - } - self._private.widget = awful.popup - { - type = self.type, - visible = false, - ontop = true, - placement = self.placement, - border_width = self.border_width, - border_color = self.border_color, - shape = self.shape, - bg = self.background, - widget = self.widget_template() or + self._private.grid = wibox.widget + { + layout = wibox.layout.grid, + orientation = "horizontal", + homogeneous = true, + expand = self.expand_apps, + spacing = self.apps_spacing, + forced_num_cols = self.apps_per_column, + forced_num_rows = self.apps_per_row, + buttons = + { + awful.button({}, 4, function() self:scroll_up() end), + awful.button({}, 5, function() self:scroll_down() end) + } + } + widget = wibox.widget { layout = wibox.layout.fixed.vertical, { widget = wibox.container.background, forced_height = dpi(100), bg = self.prompt_bg_color, - fg = self.prompt_text_color, { widget = wibox.container.margin, margins = dpi(30), @@ -612,16 +604,7 @@ local function build_widget(self) widget = wibox.container.place, halign = "left", valign = "center", - { - layout = wibox.layout.fixed.horizontal, - spacing = dpi(10), - { - widget = wibox.widget.textbox, - font = self.prompt_icon_font, - markup = string.format("%s", args.prompt_icon_color, args.prompt_icon) - }, - self._private.prompt.textbox - } + self._private.prompt } } }, @@ -631,9 +614,22 @@ local function build_widget(self) self._private.grid } } + end + + self._private.widget = awful.popup + { + type = self.type, + visible = false, + ontop = true, + placement = self.placement, + border_width = self.border_width, + border_color = self.border_color, + shape = self.shape, + bg = self.background, + widget = widget } - self._private.prompt:connect_signal("text::changed", function(text) + self._private.prompt:connect_signal("text::changed", function(_, text) if text == self._private.text then return end @@ -642,7 +638,7 @@ local function build_widget(self) self._private.search_timer:again() end) - self._private.prompt:connect_signal("key::press", function(mod, key, cmd) + self._private.prompt:connect_signal("key::press", function(_, mod, key, cmd) if key == "Escape" then self:hide() end @@ -817,7 +813,6 @@ local function new(args) args.hide_on_launch = default_value(args.hide_on_launch, true) args.try_to_keep_index_after_searching = default_value(args.try_to_keep_index_after_searching, false) args.reset_on_hide = default_value(args.reset_on_hide, true) - args.save_history = default_value(args.save_history, true) args.wrap_page_scrolling = default_value(args.wrap_page_scrolling, true) args.wrap_app_scrolling = default_value(args.wrap_app_scrolling, true) @@ -842,11 +837,12 @@ local function new(args) args.expand_apps = default_value(args.expand_apps, true) args.prompt_bg_color = default_value(args.prompt_bg_color, beautiful.fg_normal or "#FFFFFF") - args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) + args.prompt_icon_font = default_value(args.prompt_icon, "") args.prompt_icon_color = default_value(args.prompt_icon_color, beautiful.bg_normal or "#000000") args.prompt_icon = default_value(args.prompt_icon, "") + args.prompt_label_font = default_value(args.prompt_icon, "") args.prompt_label = default_value(args.prompt_label, "Search: ") - args.prompt_font = default_value(args.prompt_font, beautiful.font) + args.prompt_text_font = default_value(args.prompt_font, beautiful.font) args.prompt_text_color = default_value(args.prompt_text_color, beautiful.bg_normal or "#000000") args.app_normal_color = args.app_normal_color or beautiful.bg_normal or "#000000" From 594610a9f409795ababcf0cff352f136fb766127 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:28:52 +0200 Subject: [PATCH 049/185] Better defaults --- widget/app_launcher/prompt.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index bade091a..4ae2c255 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -378,8 +378,8 @@ local function new() 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_focus_lost = false + wp.stop_on_tag_changed = false wp.stop_on_clicked_outside = true wp.icon_font = beautiful.font From 5562030754a1a90412f40592e40e340567c34694 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:57:02 +0200 Subject: [PATCH 050/185] Use state to know when to show the cursor or not --- widget/app_launcher/prompt.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index 4ae2c255..87e20784 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -75,7 +75,7 @@ local function have_multibyte_char_at(text, position) return text:sub(position, position):wlen() == -1 end -local function generate_markup(self, show_cursor) +local function generate_markup(self) local wp = self._private local icon_size = dpi(ceil(wp.icon_size * 1024)) @@ -95,7 +95,7 @@ local function generate_markup(self, show_cursor) wp.icon.font, icon_size, wp.icon_color, wp.icon.icon) end - if show_cursor == true then + if self._private.state == true then local char, spacer, text_start, text_end if #text < wp.cur_pos then @@ -147,7 +147,7 @@ local function paste(self) 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) + generate_markup(self) end end) end @@ -160,7 +160,7 @@ local function build_properties(prototype, prop_names) self._private[prop] = value self:emit_signal("widget::redraw_needed") self:emit_signal("property::" .. prop, value) - generate_markup(self, self._private.state) + generate_markup(self) end return self end @@ -180,7 +180,7 @@ end function prompt:set_text(text) self._private.text = text self._private.cur_pos = #text + 1 - generate_markup(self, self._private.state) + generate_markup(self) end function prompt:get_text() @@ -192,7 +192,7 @@ function prompt:start() wp.state = true capi.awesome.emit_signal("prompt::toggled_on", self) - generate_markup(self, true) + generate_markup(self) wp.grabber = awful.keygrabber.run(function(modifiers, key, event) -- Convert index array to hash table @@ -335,7 +335,7 @@ function prompt:start() wp.cur_pos = #wp.text + 1 end - generate_markup(self, true) + generate_markup(self) self:emit_signal("text::changed", wp.text) end) end @@ -352,7 +352,7 @@ function prompt:stop() end awful.keygrabber.stop(wp.grabber) - generate_markup(self, false) + generate_markup(self) self:emit_signal("stopped", wp.text) end From c01b5868d12bdfe6623a1ffacd6ece865fb576fd Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 02:57:32 +0200 Subject: [PATCH 051/185] Support icon prop in table form or in regular form --- widget/app_launcher/prompt.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index 87e20784..7c220c2c 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -13,6 +13,7 @@ local tonumber = tonumber local ceil = math.ceil local ipairs = ipairs local string = string +local type = type local capi = { awesome = awesome, root = root, @@ -78,7 +79,6 @@ end local function generate_markup(self) local wp = self._private - 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)) @@ -90,9 +90,17 @@ local function generate_markup(self) local markup = "" if wp.icon ~= nil then - markup = string.format( - '%s ', - wp.icon.font, icon_size, wp.icon_color, wp.icon.icon) + if type(wp.icon) == "table" then + local icon_size = dpi(ceil(wp.icon.size * 1024)) + markup = string.format( + '%s ', + wp.icon.font, icon_size, wp.icon.color, wp.icon.icon) + else + local icon_size = dpi(ceil(wp.icon_size * 1024)) + markup = string.format( + '%s ', + wp.icon_font, icon_size, wp.icon_color, wp.icon) + end end if self._private.state == true then From 8b6052053602e9d2bd992ecd1db760af82d0170f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 03:39:42 +0200 Subject: [PATCH 052/185] Better defaults --- widget/app_launcher/init.lua | 138 +++++++++++++++-------------------- 1 file changed, 57 insertions(+), 81 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c3fba8b2..0b25e523 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -15,7 +15,6 @@ local pairs = pairs local capi = { screen = screen, mouse = mouse } local path = ... local helpers = require(tostring(path):match(".*bling") .. ".helpers") -local color = helpers.color local app_launcher = { mt = {} } @@ -101,23 +100,6 @@ local function app_widget(self, app) local widget = nil if self.app_template == nil then - local icon = wibox.widget - { - widget = wibox.widget.imagebox, - id = "icon_role", - forced_width = dpi(70), - forced_height = dpi(70), - image = app.icon - } - - local name = wibox.widget - { - widget = wibox.widget.textbox, - id = "name_role", - font = self.app_name_font, - markup = string.format("%s", self.app_name_normal_color, app.name) - } - widget = wibox.widget { widget = wibox.container.background, @@ -128,17 +110,30 @@ local function app_widget(self, app) widget = wibox.container.margin, margins = dpi(10), { - -- Using this hack instead of container.place because that will fuck with the name/icon halign - layout = wibox.layout.align.vertical, - expand = "outside", - nil, + layout = wibox.layout.fixed.vertical, + spacing = dpi(10), { - layout = wibox.layout.fixed.vertical, - spacing = dpi(10), - icon, - name + widget = wibox.container.place, + halign = "center", + valign = "center", + { + widget = wibox.widget.imagebox, + id = "icon_role", + forced_width = dpi(70), + forced_height = dpi(70), + image = app.icon + }, }, - nil + { + widget = wibox.container.place, + halign = "center", + valign = "center", + { + widget = wibox.widget.textbox, + id = "name_role", + markup = string.format("%s", self.app_name_normal_color, app.name) + } + } } } } @@ -148,12 +143,6 @@ local function app_widget(self, app) if widget then widget.cursor = "hand2" end - - if widget.selected then - widget.bg = self.app_selected_hover_color - else - widget.bg = self.app_normal_hover_color - end end) widget:connect_signal("mouse::leave", function() @@ -161,12 +150,6 @@ local function app_widget(self, app) if widget then widget.cursor = "left_ptr" end - - if widget.selected then - widget.bg = self.app_selected_color - else - widget.bg = self.app_normal_color - end end) else widget = self.app_template(app) @@ -221,15 +204,9 @@ local function app_widget(self, app) self.selected = true if _self.app_template == nil then - self:get_children_by_id("background_role")[1].bg = _self.app_selected_color + widget.bg = _self.app_selected_color local name_widget = self:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", _self.app_name_selected_color, name_widget.text) - end - local generic_name_widget = self:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", _self.app_name_selected_color, generic_name_widget.text) - end + name_widget.markup = string.format("%s", _self.app_name_selected_color, name_widget.text) end end @@ -239,15 +216,9 @@ local function app_widget(self, app) _self._private.active_widget = nil if _self.app_template == nil then - self:get_children_by_id("background_role")[1].bg = _self.app_normal_color + widget.bg = _self.app_normal_color local name_widget = self:get_children_by_id("name_role")[1] - if name_widget then - name_widget.markup = string.format("%s", _self.app_name_normal_color, name_widget.text) - end - local generic_name_widget = self:get_children_by_id("generic_name_role")[1] - if generic_name_widget then - generic_name_widget.markup = string.format("%s", _self.app_name_normal_color, generic_name_widget.text) - end + name_widget.markup = string.format("%s", _self.app_name_normal_color, name_widget.text) end end @@ -563,17 +534,22 @@ end local function build_widget(self) local widget = self.widget_template - if widget ~= nil then - self._private.prompt = widget:get_children_by_id("prompt_role")[1] - self._private.grid = widget:get_children_by_id("grid_role")[1] - else + if widget == nil then self._private.prompt = wibox.widget { widget = prompt_widget, - label = self.prompt_label, - font = self.prompt_font, reset_on_stop = self.reset_on_hide, - bg_cursor = beautiful.bg_normal or "#000000", + icon_font = self.prompt_icon_font, + icon_size = self.prompt_icon_size, + icon_color = self.prompt_icon_color, + icon = self.prompt_icon, + label_font = self.prompt_label_font, + label_size = self.prompt_label_size, + label_color = self.prompt_label_color, + label = self.prompt_label, + text_font = self.prompt_text_font, + text_size = self.prompt_text_size, + text_color = self.prompt_text_color, } self._private.grid = wibox.widget { @@ -595,7 +571,7 @@ local function build_widget(self) layout = wibox.layout.fixed.vertical, { widget = wibox.container.background, - forced_height = dpi(100), + forced_height = dpi(120), bg = self.prompt_bg_color, { widget = wibox.container.margin, @@ -614,6 +590,9 @@ local function build_widget(self) self._private.grid } } + else + self._private.prompt = widget:get_children_by_id("prompt_role")[1] + self._private.grid = widget:get_children_by_id("grid_role")[1] end self._private.widget = awful.popup @@ -836,26 +815,23 @@ local function new(args) args.apps_spacing = default_value(args.apps_spacing, dpi(30)) args.expand_apps = default_value(args.expand_apps, true) - args.prompt_bg_color = default_value(args.prompt_bg_color, beautiful.fg_normal or "#FFFFFF") - args.prompt_icon_font = default_value(args.prompt_icon, "") - args.prompt_icon_color = default_value(args.prompt_icon_color, beautiful.bg_normal or "#000000") + args.prompt_bg_color = default_value(args.prompt_bg_color, "#000000") + args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) + args.prompt_icon_size = default_value(args.prompt_icon_size, 12) + args.prompt_icon_color = default_value(args.prompt_icon_color, "#FFFFFF") args.prompt_icon = default_value(args.prompt_icon, "") - args.prompt_label_font = default_value(args.prompt_icon, "") + args.prompt_label_font = default_value(args.prompt_label_font, beautiful.font) + args.prompt_label_size = default_value(args.prompt_label_size, 12) + args.prompt_label_color = default_value(args.prompt_label_color, "#FFFFFF") args.prompt_label = default_value(args.prompt_label, "Search: ") - args.prompt_text_font = default_value(args.prompt_font, beautiful.font) - args.prompt_text_color = default_value(args.prompt_text_color, beautiful.bg_normal or "#000000") - - args.app_normal_color = args.app_normal_color or beautiful.bg_normal or "#000000" - args.app_normal_hover_color = args.app_normal_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and - color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_normal_color), 2.5)) or - color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_normal_color), 0.5)) - args.app_selected_color = args.app_selected_color or beautiful.fg_normal or "#FFFFFF" - args.app_selected_hover_color = args.app_selected_hover_color or (color.is_dark(args.app_normal_color) or color.is_opaque(args.app_normal_color)) and - color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 2.5)) or - color.rgba_to_hex(color.multiply(color.hex_to_rgba(args.app_selected_color), 0.5)) - args.app_name_normal_color = default_value(args.app_name_normal_color, beautiful.fg_normal or "#FFFFFF") - args.app_name_selected_color = default_value(args.app_name_selected_color, beautiful.bg_normal or "#000000") - args.app_name_font = default_value(args.app_name_font, beautiful.font) + args.prompt_text_font = default_value(args.prompt_text_font, beautiful.font) + args.prompt_text_size = default_value(args.prompt_text_size, 12) + args.prompt_text_color = default_value(args.prompt_text_color, "#FFFFFF") + + args.app_normal_color = default_value(args.app_normal_color, "#000000") + args.app_selected_color = default_value(args.app_selected_color, "#FFFFFF") + args.app_name_normal_color = default_value( args.app_name_normal_color, "#FFFFFF") + args.app_name_selected_color = default_value(args.app_name_selected_color, "#000000") local ret = gobject {} gtable.crush(ret, app_launcher, true) From 33576c2b245ce029389083de6a6d8d8c743e0158 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:08:51 +0200 Subject: [PATCH 053/185] Fix scrolling and grid when using widget_template --- widget/app_launcher/init.lua | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 0b25e523..5fab5a9e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -406,11 +406,11 @@ local function scroll(self, dir) if_cant_scroll_func = function() page_forward(self, "down") end elseif dir == "left" then can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil - step_size = -self.apps_per_row + step_size = -self._private.grid.forced_num_rows if_cant_scroll_func = function() page_backward(self, "left") end elseif dir == "right" then can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil - step_size = self.apps_per_row + step_size = self._private.grid.forced_num_cols if_cant_scroll_func = function() page_forward(self, "right") end end @@ -560,11 +560,6 @@ local function build_widget(self) spacing = self.apps_spacing, forced_num_cols = self.apps_per_column, forced_num_rows = self.apps_per_row, - buttons = - { - awful.button({}, 4, function() self:scroll_up() end), - awful.button({}, 5, function() self:scroll_down() end) - } } widget = wibox.widget { @@ -608,6 +603,14 @@ local function build_widget(self) widget = widget } + self._private.grid:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + if button == 4 then + self:scroll_up() + elseif button == 5 then + self:scroll_down() + end + end) + self._private.prompt:connect_signal("text::changed", function(_, text) if text == self._private.text then return @@ -639,6 +642,9 @@ local function build_widget(self) self:scroll_right() end end) + + self._private.max_apps_per_page = self._private.grid.forced_num_cols * self._private.grid.forced_num_rows + self._private.apps_per_page = self._private.max_apps_per_page end -- Sets favorites @@ -839,8 +845,6 @@ local function new(args) ret._private = {} ret._private.text = "" - ret._private.max_apps_per_page = ret.apps_per_column * ret.apps_per_row - ret._private.apps_per_page = ret._private.max_apps_per_page ret._private.pages_count = 0 ret._private.current_page = 1 ret._private.search_timer = gtimer { From 1a704556569793930c73911a6e782f7585a1ab23 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:16:31 +0200 Subject: [PATCH 054/185] Remove animation support (can be done manually) + some very weird code? --- widget/app_launcher/init.lua | 107 +++-------------------------------- 1 file changed, 8 insertions(+), 99 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 5fab5a9e..86aa9b0e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -675,106 +675,27 @@ end --- Shows the app launcher function app_launcher:show() - local screen = self.screen if self.show_on_focused_screen then - screen = awful.screen.focused() - end - - screen.app_launcher = self._private.widget - screen.app_launcher.screen = screen - self._private.prompt:start() - - local animation = self.rubato - if animation ~= nil then - if self._private.widget.goal_x == nil then - self._private.widget.goal_x = self._private.widget.x - end - if self._private.widget.goal_y == nil then - self._private.widget.goal_y = self._private.widget.y - self._private.widget.placement = nil - end - - if animation.x then - animation.x.ended:unsubscribe() - animation.x:set(self._private.widget.goal_x) - gtimer.start_new(0.01, function() - screen.app_launcher.visible = true - return false - end) - end - if animation.y then - animation.y.ended:unsubscribe() - animation.y:set(self._private.widget.goal_y) - gtimer.start_new(0.01, function() - screen.app_launcher.visible = true - return false - end) - end + self._private.widget.screen = awful.screen.focused() else - screen.app_launcher.visible = true + self._private.widget.screen = capi.screen.primary end - self:emit_signal("bling::app_launcher::visibility", true) + self._private.widget.visible = true + self._private.prompt:start() + self:emit_signal("visibility", true) end --- Hides the app launcher function app_launcher:hide() - local screen = self.screen - if self.show_on_focused_screen then - screen = awful.screen.focused() - end - - if screen.app_launcher == nil or screen.app_launcher.visible == false then - return - end - + self._private.widget.visible = false self._private.prompt:stop() - - local animation = self.rubato - if animation ~= nil then - if animation.x then - animation.x:set(animation.x:initial()) - end - if animation.y then - animation.y:set(animation.y:initial()) - end - - local anim_x_duration = (animation.x and animation.x.duration) or 0 - local anim_y_duration = (animation.y and animation.y.duration) or 0 - local turn_off_on_anim_x_end = (anim_x_duration >= anim_y_duration) and true or false - - if turn_off_on_anim_x_end then - animation.x.ended:subscribe(function() - if self.reset_on_hide == true then reset(self) end - screen.app_launcher.visible = false - screen.app_launcher = nil - animation.x.ended:unsubscribe() - end) - else - animation.y.ended:subscribe(function() - if self.reset_on_hide == true then reset(self) end - screen.app_launcher.visible = false - screen.app_launcher = nil - animation.y.ended:unsubscribe() - end) - end - else - if self.reset_on_hide == true then reset(self) end - screen.app_launcher.visible = false - screen.app_launcher = nil - end - - self:emit_signal("bling::app_launcher::visibility", false) + self:emit_signal("visibility", false) end --- Toggles the app launcher function app_launcher:toggle() - local screen = self.screen - if self.show_on_focused_screen then - screen = awful.screen.focused() - end - - if screen.app_launcher and screen.app_launcher.visible then + if self._private.widget.visible then self:hide() else self:show() @@ -805,7 +726,6 @@ local function new(args) args.show_on_focused_screen = default_value(args.show_on_focused_screen, true) args.screen = default_value(args.screen, capi.screen.primary) args.placement = default_value(args.placement, awful.placement.centered) - args.rubato = default_value(args.rubato, nil) args.background = default_value(args.background, "#000000") args.border_width = default_value(args.border_width, beautiful.border_width or dpi(0)) args.border_color = default_value(args.border_color, beautiful.border_color or "#FFFFFF") @@ -856,17 +776,6 @@ local function new(args) end } - if ret.rubato and ret.rubato.x then - ret.rubato.x:subscribe(function(pos) - ret._private.widget.x = pos - end) - end - if ret.rubato and ret.rubato.y then - ret.rubato.y:subscribe(function(pos) - ret._private.widget.y = pos - end) - end - if ret.hide_on_left_clicked_outside then awful.mouse.append_client_mousebinding( awful.button({ }, 1, function (c) From 87f50e76610820d0f8ea834f66f4c67f162ef733 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:33:23 +0200 Subject: [PATCH 055/185] Always_on for default prompt --- widget/app_launcher/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 86aa9b0e..1fc85452 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -538,6 +538,7 @@ local function build_widget(self) self._private.prompt = wibox.widget { widget = prompt_widget, + always_on = true, reset_on_stop = self.reset_on_hide, icon_font = self.prompt_icon_font, icon_size = self.prompt_icon_size, From 38e3d033cd15650b0736a210a8e5331b0b97744c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:51:00 +0200 Subject: [PATCH 056/185] Make use of the screen prop --- widget/app_launcher/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 1fc85452..d29711f5 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -593,6 +593,7 @@ local function build_widget(self) self._private.widget = awful.popup { + screen = self.screen, type = self.type, visible = false, ontop = true, @@ -678,8 +679,6 @@ end function app_launcher:show() if self.show_on_focused_screen then self._private.widget.screen = awful.screen.focused() - else - self._private.widget.screen = capi.screen.primary end self._private.widget.visible = true From 3fb0836fc7ec012c8d3a9e0aa354da89f6e6bebb Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:53:41 +0200 Subject: [PATCH 057/185] Not working --- widget/app_launcher/init.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index d29711f5..a602f66e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -557,7 +557,6 @@ local function build_widget(self) layout = wibox.layout.grid, orientation = "horizontal", homogeneous = true, - expand = self.expand_apps, spacing = self.apps_spacing, forced_num_cols = self.apps_per_column, forced_num_rows = self.apps_per_row, @@ -739,7 +738,6 @@ local function new(args) args.apps_per_row = default_value(args.apps_per_row, 5) args.apps_per_column = default_value(args.apps_per_column, 3) args.apps_spacing = default_value(args.apps_spacing, dpi(30)) - args.expand_apps = default_value(args.expand_apps, true) args.prompt_bg_color = default_value(args.prompt_bg_color, "#000000") args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) From 0ae3c65501e3a51f5533ad9d80bbfec682d27643 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:54:20 +0200 Subject: [PATCH 058/185] Hard code it --- widget/app_launcher/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index a602f66e..1c01a21a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -557,7 +557,7 @@ local function build_widget(self) layout = wibox.layout.grid, orientation = "horizontal", homogeneous = true, - spacing = self.apps_spacing, + spacing = dpi(30), forced_num_cols = self.apps_per_column, forced_num_rows = self.apps_per_row, } @@ -737,7 +737,6 @@ local function new(args) args.apps_per_row = default_value(args.apps_per_row, 5) args.apps_per_column = default_value(args.apps_per_column, 3) - args.apps_spacing = default_value(args.apps_spacing, dpi(30)) args.prompt_bg_color = default_value(args.prompt_bg_color, "#000000") args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) From f984ca541d8695c5773e210ece2e6e279da47cd2 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 04:55:00 +0200 Subject: [PATCH 059/185] Remove old props from docs --- docs/widgets/app_launcher.md | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/docs/widgets/app_launcher.md b/docs/widgets/app_launcher.md index 89e2fdb6..77e75e09 100644 --- a/docs/widgets/app_launcher.md +++ b/docs/widgets/app_launcher.md @@ -61,19 +61,16 @@ local args = { show_on_focused_screen = true -- Should app launcher show on currently focused screen screen = awful.screen -- Screen you want the launcher to launch to placement = awful.placement.top_left -- Where launcher should be placed ("awful.placement.centered"). - rubato = { x = rubato_animation_x, y = rubato_animation_y } -- Rubato animation to apply to launcher background = "#FFFFFF" -- Set bg color border_width = dpi(0) -- Set border width of popup border_color = "#FFFFFF" -- Set border color of popup - shape = function(cr, width, height) + shape = function(cr, width, height) -- Set shape for launcher gears.shape.rectangle(cr, width, height) - end -- Set shape for launcher + end + prompt_height = dpi(50) -- Prompt height prompt_margins = dpi(30) -- Prompt margins prompt_paddings = dpi(15) -- Prompt padding - shape = function(cr, width, height) - gears.shape.rectangle(cr, width, height) - end -- Set shape for prompt prompt_color = "#000000" -- Prompt background color prompt_border_width = dpi(0) -- Prompt border width prompt_border_color = "#000000" -- Prompt border color @@ -96,31 +93,10 @@ local args = { apps_per_row = 3 -- Set how many apps should appear in each row apps_per_column = 3 -- Set how many apps should appear in each column - apps_margin = {left = dpi(40), right = dpi(40), bottom = dpi(30)} -- Margin between apps - apps_spacing = dpi(10) -- Spacing between apps - expand_apps = true -- Should apps expand to fill width of launcher - app_width = dpi(400) -- Width of each app - app_height = dpi(40) -- Height of each app - app_shape = function(cr, width, height) - gears.shape.rectangle(cr, width, height) - end -- Shape of each app app_normal_color = "#000000" -- App normal color - app_normal_hover_color = "#111111" -- App normal hover color app_selected_color = "#FFFFFF" -- App selected color - app_selected_hover_color = "#EEEEEE" -- App selected hover color - app_content_padding = dpi(10) -- App content padding - app_content_spacing = dpi(10) -- App content spacing - app_show_icon = true -- Should show icon? - app_icon_halign = "center" -- App icon horizontal alignment - app_icon_width = dpi(70) -- App icon wigth - app_icon_height = dpi(70) -- App icon height - app_show_name = true -- Should show app name? - app_name_generic_name_spacing = dpi(0) -- Generic name spacing (If show_generic_name) - app_name_halign = "center" -- App name horizontal alignment - app_name_font = "Comic Sans" -- App name font app_name_normal_color = "#FFFFFF" -- App name normal color app_name_selected_color = "#000000" -- App name selected color - app_show_generic_name = true -- Should show generic app name? } ``` From 2c6570405b3113287dd498202d3208b2e6874759 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 05:09:26 +0200 Subject: [PATCH 060/185] Update docs --- docs/widgets/app_launcher.md | 44 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/docs/widgets/app_launcher.md b/docs/widgets/app_launcher.md index 77e75e09..446e691c 100644 --- a/docs/widgets/app_launcher.md +++ b/docs/widgets/app_launcher.md @@ -58,38 +58,28 @@ local args = { icon_size = 24 -- Set icon size type = "dock" -- awful.popup type ("dock", "desktop", "normal"...). See awesomewm docs for more detail - show_on_focused_screen = true -- Should app launcher show on currently focused screen - screen = awful.screen -- Screen you want the launcher to launch to - placement = awful.placement.top_left -- Where launcher should be placed ("awful.placement.centered"). - background = "#FFFFFF" -- Set bg color - border_width = dpi(0) -- Set border width of popup - border_color = "#FFFFFF" -- Set border color of popup - shape = function(cr, width, height) -- Set shape for launcher + show_on_focused_screen = true -- Should the app launcher popup show on currently focused screen + screen = awful.screen -- Screen you want the launcher popup to open on + placement = awful.placement.top_left -- Where launcher popup should be placed + bg = "#FFFFFF" -- Set launcher popup bg color + border_width = dpi(0) -- Set launcher popup border width of popup + border_color = "#FFFFFF" -- Set launcher popup border color of popup + shape = function(cr, width, height) -- Set launcher popup shape gears.shape.rectangle(cr, width, height) end - prompt_height = dpi(50) -- Prompt height - prompt_margins = dpi(30) -- Prompt margins - prompt_paddings = dpi(15) -- Prompt padding - prompt_color = "#000000" -- Prompt background color - prompt_border_width = dpi(0) -- Prompt border width - prompt_border_color = "#000000" -- Prompt border color - prompt_text_halign = "center" -- Prompt text horizontal alignment - prompt_text_valign = "center" -- Prompt text vertical alignment - prompt_icon_text_spacing = dpi(10) -- Prompt icon text spacing - prompt_show_icon = true -- Should prompt show icon (?) + prompt_bg_color = "#000000" -- Prompt background color prompt_icon_font = "Comic Sans" -- Prompt icon font - prompt_icon_color = "#000000" -- Prompt icon color + prompt_icon_size = 15 -- Prompt icon size + prompt_icon_color = "#FFFFFF" -- Prompt icon color prompt_icon = "" -- Prompt icon - prompt_icon_markup = string.format( - "%s", - args.prompt_icon_color, args.prompt_icon - ) -- Prompt icon markup - prompt_text = "Search:" -- Prompt text - prompt_start_text = "manager" -- Set string for prompt to start with - prompt_font = "Comic Sans" -- Prompt font - prompt_text_color = "#FFFFFF" -- Prompt text color - prompt_cursor_color = "#000000" -- Prompt cursor color + prompt_label_font = "Comic Sans" -- Prompt labe font + prompt_label_size = 15 -- Prompt labe font + prompt_label_color = "#FFFFFF" -- Prompt labe font + prompt_label = "Search" -- Prompt labe font + prompt_text_font = "Comic Sans" -- Prompt text font + prompt_text_size = 15 -- Prompt text font + prompt_text_color = "#FFFFFF" -- Prompt text font apps_per_row = 3 -- Set how many apps should appear in each row apps_per_column = 3 -- Set how many apps should appear in each column From 9d5729cc032426d71bb4ad42ff2448af0f54cac8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 05:09:34 +0200 Subject: [PATCH 061/185] Make it consistent with the rest of awesome API --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 1c01a21a..9c90fa0d 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -600,7 +600,7 @@ local function build_widget(self) border_width = self.border_width, border_color = self.border_color, shape = self.shape, - bg = self.background, + bg = self.bg, widget = widget } @@ -725,7 +725,7 @@ local function new(args) args.show_on_focused_screen = default_value(args.show_on_focused_screen, true) args.screen = default_value(args.screen, capi.screen.primary) args.placement = default_value(args.placement, awful.placement.centered) - args.background = default_value(args.background, "#000000") + args.bg = default_value(args.bg, "#000000") args.border_width = default_value(args.border_width, beautiful.border_width or dpi(0)) args.border_color = default_value(args.border_color, beautiful.border_color or "#FFFFFF") args.shape = default_value(args.shape, nil) From e47fbd28d507fd749de8c72dbc018f139b48b5b8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 20 Feb 2023 05:11:45 +0200 Subject: [PATCH 062/185] Just use set_text --- widget/app_launcher/prompt.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index 7c220c2c..eb5621f4 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -352,11 +352,8 @@ function prompt:stop() 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 = "" + self:set_text("") end awful.keygrabber.stop(wp.grabber) From 2649d0ea0c03c1207631e00cb5f36bd4b5075e3a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 21 Feb 2023 04:30:34 +0200 Subject: [PATCH 063/185] Rename entry/entries to app/apps --- widget/app_launcher/init.lua | 66 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9c90fa0d..e4c9bca0 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -228,56 +228,56 @@ end local function search(self, text) local old_pos = self._private.grid:get_widget_position(self._private.active_widget) - -- Reset all the matched entries - self._private.matched_entries = {} + -- Reset all the matched apps + self._private.matched_apps = {} -- Remove all the grid widgets self._private.grid:reset() if text == "" then - self._private.matched_entries = self._private.all_entries + self._private.matched_apps = self._private.all_apps else - for _, entry in pairs(self._private.all_entries) do + for _, app in pairs(self._private.all_apps) do text = text:gsub( "%W", "" ) -- Check if there's a match by the app name or app command - if string.find(entry.name:lower(), text:lower(), 1, true) ~= nil or - self.search_commands and string.find(entry.commandline, text:lower(), 1, true) ~= nil + if string.find(app.name:lower(), text:lower(), 1, true) ~= nil or + self.search_commands and string.find(app.commandline, text:lower(), 1, true) ~= nil then - table.insert(self._private.matched_entries, { - name = entry.name, - generic_name = entry.generic_name, - commandline = entry.commandline, - executable = entry.executable, - terminal = entry.terminal, - icon = entry.icon + table.insert(self._private.matched_apps, { + name = app.name, + generic_name = app.generic_name, + commandline = app.commandline, + executable = app.executable, + terminal = app.terminal, + icon = app.icon }) end end -- Sort by string similarity - table.sort(self._private.matched_entries, function(a, b) + table.sort(self._private.matched_apps, function(a, b) return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) end) end - for _, entry in pairs(self._private.matched_entries) do + for _, app in pairs(self._private.matched_apps) do -- Only add the widgets for apps that are part of the first page if #self._private.grid.children + 1 <= self._private.max_apps_per_page then - self._private.grid:add(app_widget(self, entry)) + self._private.grid:add(app_widget(self, app)) end end - -- Recalculate the apps per page based on the current matched entries - self._private.apps_per_page = math.min(#self._private.matched_entries, self._private.max_apps_per_page) + -- Recalculate the apps per page based on the current matched apps + self._private.apps_per_page = math.min(#self._private.matched_apps, self._private.max_apps_per_page) -- Recalculate the pages count based on the current apps per page - self._private.pages_count = math.ceil(math.max(1, #self._private.matched_entries) / math.max(1, self._private.apps_per_page)) + self._private.pages_count = math.ceil(math.max(1, #self._private.matched_apps) / math.max(1, self._private.apps_per_page)) -- Page should be 1 after a search self._private.current_page = 1 -- This is an option to mimic rofi behaviour where after a search -- it will reselect the app whose index is the same as the app index that was previously selected - -- and if matched_entries.length < current_index it will instead select the app with the greatest index + -- and if matched_apps.length < current_index it will instead select the app with the greatest index if self.try_to_keep_index_after_searching then if self._private.grid:get_widgets_at(old_pos.row, old_pos.col) == nil then local app = self._private.grid.children[#self._private.grid.children] @@ -301,7 +301,7 @@ local function page_forward(self, dir) min_app_index_to_include = self._private.apps_per_page * self._private.current_page self._private.current_page = self._private.current_page + 1 max_app_index_to_include = self._private.apps_per_page * self._private.current_page - elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then + elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then self._private.current_page = 1 min_app_index_to_include = 0 max_app_index_to_include = self._private.apps_per_page @@ -318,10 +318,10 @@ local function page_forward(self, dir) -- Remove the current page apps from the grid self._private.grid:reset() - for index, entry in pairs(self._private.matched_entries) do + for index, app in pairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(app_widget(self, entry)) + self._private.grid:add(app_widget(self, app)) end end @@ -344,7 +344,7 @@ end local function page_backward(self, dir) if self._private.current_page > 1 then self._private.current_page = self._private.current_page - 1 - elseif self.wrap_page_scrolling and #self._private.matched_entries >= self._private.max_apps_per_page then + elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then local app = self._private.grid.children[#self._private.grid.children] @@ -362,10 +362,10 @@ local function page_backward(self, dir) local max_app_index_to_include = self._private.apps_per_page * self._private.current_page local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - for index, entry in pairs(self._private.matched_entries) do + for index, app in pairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(app_widget(self, entry)) + self._private.grid:add(app_widget(self, app)) end end @@ -424,15 +424,15 @@ end local function reset(self) self._private.grid:reset() - self._private.matched_entries = self._private.all_entries + self._private.matched_apps = self._private.all_apps self._private.apps_per_page = self._private.max_apps_per_page - self._private.pages_count = math.ceil(#self._private.all_entries / self._private.apps_per_page) + self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) self._private.current_page = 1 - for index, entry in pairs(self._private.all_entries) do + for index, app in pairs(self._private.all_apps) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then - self._private.grid:add(app_widget(self, entry)) + self._private.grid:add(app_widget(self, app)) else break end @@ -443,8 +443,8 @@ local function reset(self) end local function generate_apps(self) - self._private.all_entries = {} - self._private.matched_entries = {} + self._private.all_apps = {} + self._private.matched_apps = {} local app_info = Gio.AppInfo local apps = app_info.get_all() @@ -518,7 +518,7 @@ local function generate_apps(self) local terminal = Gio.DesktopAppInfo.get_string(desktop_app_info, "Terminal") == "true" and true or false local generic_name = Gio.DesktopAppInfo.get_string(desktop_app_info, "GenericName") or nil - table.insert(self._private.all_entries, { + table.insert(self._private.all_apps, { name = name, generic_name = generic_name, commandline = commandline, From 582b3c4db8923a432762455320b1fcd4e359bf6c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 21 Feb 2023 04:55:47 +0200 Subject: [PATCH 064/185] Adding more props to the app table that this is obslete now, it's better to just use the app.prop_name syntax --- widget/app_launcher/init.lua | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index e4c9bca0..c81eaac9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -153,23 +153,6 @@ local function app_widget(self, app) end) else widget = self.app_template(app) - - local icon = widget:get_children_by_id("icon_role")[1] - if icon then - icon.image = app.icon - end - local name = widget:get_children_by_id("name_role")[1] - if name then - name.text = app.name - end - local generic_name = widget:get_children_by_id("generic_name_role")[1] - if generic_name then - generic_name.text = app.generic_name - end - local command = widget:get_children_by_id("command_role")[1] - if command then - command.text = app.executable - end end widget:connect_signal("button::press", function(app, _, __, button) From 6cf48e9f484db8039c9b5bd036f10825de31b905 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 21 Feb 2023 05:00:18 +0200 Subject: [PATCH 065/185] Not used --- helpers/icon_theme.lua | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/helpers/icon_theme.lua b/helpers/icon_theme.lua index 22a7a66b..5491504d 100644 --- a/helpers/icon_theme.lua +++ b/helpers/icon_theme.lua @@ -79,38 +79,4 @@ function _icon_theme.get_icon_path(icon_name, icon_theme, icon_size) return "" end -function _icon_theme.get_client_icon_path(client, icon_theme, icon_size) - local app_list = AppInfo.get_all() - - local class = string.lower(client.class) - local name = string.lower(client.name) - - for _, app in ipairs(app_list) do - local id = app:get_id() - local desktop_app_info = DesktopAppInfo.new(id) - if desktop_app_info then - local props = { - id:gsub(".desktop", ""), - desktop_app_info:get_string("Name"), - desktop_app_info:get_filename(), - desktop_app_info:get_startup_wm_class(), - desktop_app_info:get_string("Icon"), - desktop_app_info:get_string("Exec"), - desktop_app_info:get_string("Keywords") - } - - for _, prop in ipairs(props) do - if prop ~= nil and (prop:lower() == class or prop:lower() == name) then - local icon = desktop_app_info:get_string("Icon") - if icon ~= nil then - return _icon_theme.get_icon_path(icon, icon_theme, icon_size) - end - end - end - end - end - - return _icon_theme.choose_icon({"window", "window-manager", "xfwm4-default", "window_list"}, icon_theme, icon_size) -end - return _icon_theme \ No newline at end of file From fd3ee7a786d219540d5a2008c508bae37ff63e55 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 21 Feb 2023 05:01:16 +0200 Subject: [PATCH 066/185] Add more props to the app table --- widget/app_launcher/init.lua | 46 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c81eaac9..38c2427e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -168,9 +168,9 @@ local function app_widget(self, app) local _self = self function widget:spawn() if app.terminal == true then - awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.executable) + awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) else - awful.spawn(app.executable) + awful.spawn(app.exec) end if _self.hide_on_launch then @@ -224,16 +224,9 @@ local function search(self, text) -- Check if there's a match by the app name or app command if string.find(app.name:lower(), text:lower(), 1, true) ~= nil or - self.search_commands and string.find(app.commandline, text:lower(), 1, true) ~= nil + self.search_commands and string.find(app.exec, text:lower(), 1, true) ~= nil then - table.insert(self._private.matched_apps, { - name = app.name, - generic_name = app.generic_name, - commandline = app.commandline, - executable = app.executable, - terminal = app.terminal, - icon = app.icon - }) + table.insert(self._private.matched_apps, app) end end @@ -475,15 +468,16 @@ local function generate_apps(self) end for _, app in ipairs(apps) do - if app.should_show(app) then - local name = app_info.get_name(app) - local commandline = app_info.get_commandline(app) - local executable = app_info.get_executable(app) - local icon = helpers.icon_theme.get_gicon_path(app_info.get_icon(app), self.icon_theme, self.icon_size) + if app:should_show() then + local id = app:get_id() + local desktop_app_info = Gio.DesktopAppInfo.new(id) + local name = desktop_app_info:get_string("Name") + local exec = desktop_app_info:get_string("Exec") -- Check if this app should be skipped, depanding on the skip_names / skip_commands table - if not has_value(self.skip_names, name) and not has_value(self.skip_commands, commandline) then + if not has_value(self.skip_names, name) and not has_value(self.skip_commands, exec) then -- Check if this app should be skipped becuase it's iconless depanding on skip_empty_icons + local icon = helpers.icon_theme.get_gicon_path(app_info.get_icon(app), self.icon_theme, self.icon_size) if icon ~= "" or self.skip_empty_icons == false then if icon == "" then if self.default_app_icon_name ~= nil then @@ -497,17 +491,17 @@ local function generate_apps(self) end end - local desktop_app_info = Gio.DesktopAppInfo.new(app_info.get_id(app)) - local terminal = Gio.DesktopAppInfo.get_string(desktop_app_info, "Terminal") == "true" and true or false - local generic_name = Gio.DesktopAppInfo.get_string(desktop_app_info, "GenericName") or nil - table.insert(self._private.all_apps, { + path = desktop_app_info:get_filename(), + id = id, name = name, - generic_name = generic_name, - commandline = commandline, - executable = executable, - terminal = terminal, - icon = icon + generic_name = desktop_app_info:get_string("GenericName"), + startup_wm_class = desktop_app_info:get_startup_wm_class(), + keywords = desktop_app_info:get_string("Keywords"), + icon = icon, + icon_name = desktop_app_info:get_string("Icon"), + terminal = desktop_app_info:get_string("Terminal") == "true" and true or false, + exec = exec, }) end end From b9576250eed6f02733be250425970e638969f389 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 21 Feb 2023 05:07:07 +0200 Subject: [PATCH 067/185] Add the desktop_app_info to the app table --- widget/app_launcher/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 38c2427e..9a6c4b7f 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -492,6 +492,7 @@ local function generate_apps(self) end table.insert(self._private.all_apps, { + desktop_app_info = desktop_app_info, path = desktop_app_info:get_filename(), id = id, name = name, From 04d7af46b3ae5d15418670edb0cdbdfa8f1b6b0b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 22 Feb 2023 05:17:18 +0200 Subject: [PATCH 068/185] Fix prompt disabling other keygrabbers --- widget/app_launcher/init.lua | 4 ++++ widget/app_launcher/prompt.lua | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9a6c4b7f..789fb3df 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -665,6 +665,10 @@ end --- Hides the app launcher function app_launcher:hide() + if self._private.widget.visible == false then + return + end + self._private.widget.visible = false self._private.prompt:stop() self:emit_signal("visibility", false) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index eb5621f4..b97fcc8c 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -350,13 +350,19 @@ end function prompt:stop() local wp = self._private + if wp.state == false then + return + end + wp.state = false if self.reset_on_stop == true then self:set_text("") end - awful.keygrabber.stop(wp.grabber) + if wp.grabber then + awful.keygrabber.stop(wp.grabber) + end generate_markup(self) self:emit_signal("stopped", wp.text) From 7f73fc326514287728540e39e05b845be7d98cff Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 19:53:53 +0200 Subject: [PATCH 069/185] Expose these methods in a nicer way for the app_template --- widget/app_launcher/init.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 789fb3df..67c31aaf 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -205,6 +205,10 @@ local function app_widget(self, app) end end + app.spawn = widget.spawn + app.select = widget.select + app.unselect = widget.unselect + return widget end From 0978c54321ca2296ab15bf0ce50fee0921015fd3 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 19:53:59 +0200 Subject: [PATCH 070/185] This should have been ipairs --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 67c31aaf..996b9d47 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -223,7 +223,7 @@ local function search(self, text) if text == "" then self._private.matched_apps = self._private.all_apps else - for _, app in pairs(self._private.all_apps) do + for _, app in ipairs(self._private.all_apps) do text = text:gsub( "%W", "" ) -- Check if there's a match by the app name or app command @@ -409,7 +409,7 @@ local function reset(self) self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) self._private.current_page = 1 - for index, app in pairs(self._private.all_apps) do + for index, app in ipairs(self._private.all_apps) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then self._private.grid:add(app_widget(self, app)) From d7f23096d4c3d45273f3ce1ca99c1ee0062cdc8e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 20:38:32 +0200 Subject: [PATCH 071/185] This is a table-array like as well, use ipairs --- widget/app_launcher/init.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 996b9d47..61a7f409 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -11,7 +11,6 @@ local string = string local table = table local math = math local ipairs = ipairs -local pairs = pairs local capi = { screen = screen, mouse = mouse } local path = ... local helpers = require(tostring(path):match(".*bling") .. ".helpers") @@ -239,7 +238,7 @@ local function search(self, text) return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) end) end - for _, app in pairs(self._private.matched_apps) do + for _, app in ipairs(self._private.matched_apps) do -- Only add the widgets for apps that are part of the first page if #self._private.grid.children + 1 <= self._private.max_apps_per_page then self._private.grid:add(app_widget(self, app)) @@ -298,7 +297,7 @@ local function page_forward(self, dir) -- Remove the current page apps from the grid self._private.grid:reset() - for index, app in pairs(self._private.matched_apps) do + for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then self._private.grid:add(app_widget(self, app)) @@ -342,7 +341,7 @@ local function page_backward(self, dir) local max_app_index_to_include = self._private.apps_per_page * self._private.current_page local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - for index, app in pairs(self._private.matched_apps) do + for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then self._private.grid:add(app_widget(self, app)) From 24cdcb9385f54f1539fff7707201a8d3b812cc7b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 20:39:21 +0200 Subject: [PATCH 072/185] No need for the case_insensitive function, can just :lower() the string + use plain matching --- widget/app_launcher/init.lua | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 61a7f409..53a1901c 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -71,24 +71,9 @@ local function string_levenshtein(str1, str2) return matrix[len1][len2] end -local function case_insensitive_pattern(pattern) - -- find an optional '%' (group 1) followed by any character (group 2) - local p = pattern:gsub("(%%?)(.)", function(percent, letter) - if percent ~= "" or not letter:match("%a") then - -- if the '%' matched, or `letter` is not a letter, return "as is" - return percent .. letter - else - -- else, return a case-insensitive character class of the matched letter - return string.format("[%s%s]", letter:lower(), letter:upper()) - end - end) - - return p -end - local function has_value(tab, val) - for index, value in pairs(tab) do - if val:find(case_insensitive_pattern(value)) then + for _, value in ipairs(tab) do + if val:lower():find(value:lower(), 1, true) then return true end end From 7f34d48a9747dd594fb6b54fedf262e0b69c9d7d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 20:39:47 +0200 Subject: [PATCH 073/185] Refactor the sorting function + split the sorting part into it's own function --- widget/app_launcher/init.lua | 68 +++++++++++++----------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 53a1901c..62d688d9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -406,55 +406,35 @@ local function reset(self) app:select() end +local function sort_apps(self) + table.sort(self._private.all_apps, function(a, b) + local is_a_favorite = has_value(self.favorites, a.id) + local is_b_favorite = has_value(self.favorites, b.id) + + -- Sort the favorite apps first + if is_a_favorite and not is_b_favorite then + return true + elseif not is_a_favorite and is_b_favorite then + return false + end + + -- Sort alphabetically if specified + if self.sort_alphabetically then + return a.name:lower() < b.name:lower() + elseif self.reverse_sort_alphabetically then + return b.name:lower() > a.name:lower() + else + return true + end + end) +end + local function generate_apps(self) self._private.all_apps = {} self._private.matched_apps = {} local app_info = Gio.AppInfo local apps = app_info.get_all() - if self.sort_alphabetically then - table.sort(apps, function(a, b) - local app_a_score = app_info.get_name(a):lower() - if has_value(self.favorites, app_info.get_id(a)) then - app_a_score = "aaaaaaaaaaa" .. app_a_score - end - local app_b_score = app_info.get_name(b):lower() - if has_value(self.favorites, app_info.get_id(b)) then - app_b_score = "aaaaaaaaaaa" .. app_b_score - end - - return app_a_score < app_b_score - end) - elseif self.reverse_sort_alphabetically then - table.sort(apps, function(a, b) - local app_a_score = app_info.get_name(a):lower() - if has_value(self.favorites, app_info.get_id(a)) then - app_a_score = "zzzzzzzzzzz" .. app_a_score - end - local app_b_score = app_info.get_name(b):lower() - if has_value(self.favorites, app_info.get_id(b)) then - app_b_score = "zzzzzzzzzzz" .. app_b_score - end - - return app_a_score > app_b_score - end) - else - table.sort(apps, function(a, b) - local app_a_favorite = has_value(self.favorites, app_info.get_id(a)) - local app_b_favorite = has_value(self.favorites, app_info.get_id(b)) - - if app_a_favorite and not app_b_favorite then - return true - elseif app_b_favorite and not app_a_favorite then - return false - elseif app_a_favorite and app_b_favorite then - return app_info.get_name(a):lower() < app_info.get_name(b):lower() - else - return false - end - end) - end - for _, app in ipairs(apps) do if app:should_show() then local id = app:get_id() @@ -496,6 +476,8 @@ local function generate_apps(self) end end end + + sort_apps(self) end local function build_widget(self) From 097f007a34970cd5a8af0f8b3c0bf764e97bebb7 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 20:41:44 +0200 Subject: [PATCH 074/185] Refresh the app list when settings the favorites --- widget/app_launcher/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 62d688d9..5b72c5e0 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -599,7 +599,9 @@ end -- Sets favorites function app_launcher:set_favorites(favorites) self.favorites = favorites - generate_apps(self) + sort_apps(self) + -- Refresh the app list + search(self, self._private.text) end --- Scrolls up From 7207d9792abb4667a878fc1702919d4bffa0a0f8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 20:42:51 +0200 Subject: [PATCH 075/185] Get the text from self, no need to pass it as well --- widget/app_launcher/init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 5b72c5e0..b84f6557 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -196,7 +196,8 @@ local function app_widget(self, app) return widget end -local function search(self, text) +local function search(self) + local text = self._private.text local old_pos = self._private.grid:get_widget_position(self._private.active_widget) -- Reset all the matched apps @@ -601,7 +602,7 @@ function app_launcher:set_favorites(favorites) self.favorites = favorites sort_apps(self) -- Refresh the app list - search(self, self._private.text) + search(self) end --- Scrolls up @@ -723,7 +724,7 @@ local function new(args) autostart = true, single_shot = true, callback = function() - search(ret, ret._private.text) + search(ret) end } From 7e33e62486fcb7d7f95d369b2497286a577a759d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 21:24:15 +0200 Subject: [PATCH 076/185] Replace xclip dep using gtk clipboard --- widget/app_launcher/prompt.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua index b97fcc8c..15ac3133 100644 --- a/widget/app_launcher/prompt.lua +++ b/widget/app_launcher/prompt.lua @@ -2,6 +2,9 @@ -- @author https://github.com/Kasper24 -- @copyright 2021-2022 Kasper24 ------------------------------------------- +local lgi = require('lgi') +local Gtk = lgi.require('Gtk', '3.0') +local Gdk = lgi.require('Gdk', '3.0') local awful = require("awful") local gtable = require("gears.table") local gstring = require("gears.string") @@ -146,13 +149,8 @@ end 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.clipboard:request_text(function(clipboard, text) + if text then 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) @@ -413,6 +411,7 @@ local function new() wp.cur_pos = #wp.text + 1 or 1 wp.state = false + wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) widget:connect_signal("mouse::enter", function(self, find_widgets_result) capi.root.cursor("xterm") From 87656129f7287e9db2d7b81b72a5ee5bd5febed3 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 22:17:01 +0200 Subject: [PATCH 077/185] ::press gets called consisntely when holding down a key and that will cause lag when scrolling --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index b84f6557..fa779b40 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -570,7 +570,7 @@ local function build_widget(self) self._private.search_timer:again() end) - self._private.prompt:connect_signal("key::press", function(_, mod, key, cmd) + self._private.prompt:connect_signal("key::release", function(_, mod, key, cmd) if key == "Escape" then self:hide() end From 44af848a9c47a693226dccc732aa9e3538a0fcd4 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 22:18:13 +0200 Subject: [PATCH 078/185] Define app once --- widget/app_launcher/init.lua | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index fa779b40..583e2090 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -291,18 +291,16 @@ local function page_forward(self, dir) end if self._private.current_page > 1 or self.wrap_page_scrolling then + local app = nil if dir == "down" then - local app = self._private.grid:get_widgets_at(1, 1)[1] - app:select() - else - local app = self._private.grid:get_widgets_at(pos.row, 1)[1] + app = self._private.grid:get_widgets_at(1, 1)[1] + elseif dir == "right" then + app = self._private.grid:get_widgets_at(pos.row, 1)[1] if app == nil then - local app = self._private.grid.children[#self._private.grid.children] - app:select() - else - app:select() + app = self._private.grid.children[#self._private.grid.children] end end + app:select() end end @@ -334,20 +332,19 @@ local function page_backward(self, dir) end end - local rows, columns = self._private.grid:get_dimension() + local app = nil if self._private.current_page < self._private.pages_count then if dir == "up" then - local app = self._private.grid.children[#self._private.grid.children] - app:select() + app = self._private.grid.children[#self._private.grid.children] else -- Keep the same row from last page - local app = self._private.grid:get_widgets_at(pos.row, columns)[1] - app:select() + local _, columns = self._private.grid:get_dimension() + app = self._private.grid:get_widgets_at(pos.row, columns)[1] end elseif self.wrap_page_scrolling then - local app = self._private.grid.children[#self._private.grid.children] - app:select() + app = self._private.grid.children[#self._private.grid.children] end + app:select() end local function scroll(self, dir) From 8c8d96702ea6ebb1b30247fcc66da48564b3e2fe Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 22:27:06 +0200 Subject: [PATCH 079/185] Check if the returned table is nil --- widget/app_launcher/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 583e2090..fd576ca1 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -295,7 +295,10 @@ local function page_forward(self, dir) if dir == "down" then app = self._private.grid:get_widgets_at(1, 1)[1] elseif dir == "right" then - app = self._private.grid:get_widgets_at(pos.row, 1)[1] + app = self._private.grid:get_widgets_at(pos.row, 1) + if app then + app = app[1] + end if app == nil then app = self._private.grid.children[#self._private.grid.children] end From c449681cbe92d353cefee66c7b801ce6c6d3d228 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 23:01:34 +0200 Subject: [PATCH 080/185] Convert the reset function into a public method + fix not respecting the reset_on_hide prop --- widget/app_launcher/init.lua | 49 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index fd576ca1..c8233872 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -387,26 +387,6 @@ local function scroll(self, dir) end end -local function reset(self) - self._private.grid:reset() - self._private.matched_apps = self._private.all_apps - self._private.apps_per_page = self._private.max_apps_per_page - self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) - self._private.current_page = 1 - - for index, app in ipairs(self._private.all_apps) do - -- Only add the apps that are part of the first page - if index <= self._private.apps_per_page then - self._private.grid:add(app_widget(self, app)) - else - break - end - end - - local app = self._private.grid:get_widgets_at(1, 1)[1] - app:select() -end - local function sort_apps(self) table.sort(self._private.all_apps, function(a, b) local is_a_favorite = has_value(self.favorites, a.id) @@ -642,6 +622,10 @@ function app_launcher:hide() return end + if self.reset_on_hide == true then + self:reset() + end + self._private.widget.visible = false self._private.prompt:stop() self:emit_signal("visibility", false) @@ -656,6 +640,29 @@ function app_launcher:toggle() end end +-- Reset the app page into the initial state +function app_launcher:reset() + self._private.grid:reset() + self._private.matched_apps = self._private.all_apps + self._private.apps_per_page = self._private.max_apps_per_page + self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) + self._private.current_page = 1 + + for index, app in ipairs(self._private.all_apps) do + -- Only add the apps that are part of the first page + if index <= self._private.apps_per_page then + self._private.grid:add(app_widget(self, app)) + else + break + end + end + + local app = self._private.grid:get_widgets_at(1, 1)[1] + app:select() + + self._private.prompt:set_text("") +end + -- Returns a new app launcher local function new(args) args = args or {} @@ -763,7 +770,7 @@ local function new(args) build_widget(ret) generate_apps(ret) - reset(ret) + ret:reset() return ret end From a4df7debbc34cfbf350db00ff553c915dea8bd54 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 23:03:56 +0200 Subject: [PATCH 081/185] Add more helper functions + remove useless comments --- widget/app_launcher/init.lua | 118 ++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c8233872..ded64357 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -198,12 +198,12 @@ end local function search(self) local text = self._private.text - local old_pos = self._private.grid:get_widget_position(self._private.active_widget) + local old_pos = self:get_grid():get_widget_position(self._private.active_widget) -- Reset all the matched apps self._private.matched_apps = {} -- Remove all the grid widgets - self._private.grid:reset() + self:get_grid():reset() if text == "" then self._private.matched_apps = self._private.all_apps @@ -226,8 +226,8 @@ local function search(self) end for _, app in ipairs(self._private.matched_apps) do -- Only add the widgets for apps that are part of the first page - if #self._private.grid.children + 1 <= self._private.max_apps_per_page then - self._private.grid:add(app_widget(self, app)) + if #self:get_grid().children + 1 <= self._private.max_apps_per_page then + self:get_grid():add(app_widget(self, app)) end end @@ -244,16 +244,16 @@ local function search(self) -- it will reselect the app whose index is the same as the app index that was previously selected -- and if matched_apps.length < current_index it will instead select the app with the greatest index if self.try_to_keep_index_after_searching then - if self._private.grid:get_widgets_at(old_pos.row, old_pos.col) == nil then - local app = self._private.grid.children[#self._private.grid.children] + if self:get_grid():get_widgets_at(old_pos.row, old_pos.col) == nil then + local app = self:get_grid().children[#self:get_grid().children] app:select() else - local app = self._private.grid:get_widgets_at(old_pos.row, old_pos.col)[1] + local app = self:get_grid():get_widgets_at(old_pos.row, old_pos.col)[1] app:select() end -- Otherwise select the first app on the list - elseif #self._private.grid.children > 0 then - local app = self._private.grid:get_widgets_at(1, 1)[1] + elseif #self:get_grid().children > 0 then + local app = self:get_grid():get_widgets_at(1, 1)[1] app:select() end end @@ -271,36 +271,36 @@ local function page_forward(self, dir) min_app_index_to_include = 0 max_app_index_to_include = self._private.apps_per_page elseif self.wrap_app_scrolling then - local app = self._private.grid:get_widgets_at(1, 1)[1] + local app = self:get_grid():get_widgets_at(1, 1)[1] app:select() return else return end - local pos = self._private.grid:get_widget_position(self._private.active_widget) + local pos = self:get_grid():get_widget_position(self._private.active_widget) -- Remove the current page apps from the grid - self._private.grid:reset() + self:get_grid():reset() for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(app_widget(self, app)) + self:get_grid():add(app_widget(self, app)) end end if self._private.current_page > 1 or self.wrap_page_scrolling then local app = nil if dir == "down" then - app = self._private.grid:get_widgets_at(1, 1)[1] + app = self:get_grid():get_widgets_at(1, 1)[1] elseif dir == "right" then - app = self._private.grid:get_widgets_at(pos.row, 1) + app = self:get_grid():get_widgets_at(pos.row, 1) if app then app = app[1] end if app == nil then - app = self._private.grid.children[#self._private.grid.children] + app = self:get_grid().children[#self:get_grid().children] end end app:select() @@ -313,17 +313,17 @@ local function page_backward(self, dir) elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then - local app = self._private.grid.children[#self._private.grid.children] + local app = self:get_grid().children[#self:get_grid().children] app:select() return else return end - local pos = self._private.grid:get_widget_position(self._private.active_widget) + local pos = self:get_grid():get_widget_position(self._private.active_widget) -- Remove the current page apps from the grid - self._private.grid:reset() + self:get_grid():reset() local max_app_index_to_include = self._private.apps_per_page * self._private.current_page local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page @@ -331,56 +331,56 @@ local function page_backward(self, dir) for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self._private.grid:add(app_widget(self, app)) + self:get_grid():add(app_widget(self, app)) end end local app = nil if self._private.current_page < self._private.pages_count then if dir == "up" then - app = self._private.grid.children[#self._private.grid.children] + app = self:get_grid().children[#self:get_grid().children] else -- Keep the same row from last page - local _, columns = self._private.grid:get_dimension() - app = self._private.grid:get_widgets_at(pos.row, columns)[1] + local _, columns = self:get_grid():get_dimension() + app = self:get_grid():get_widgets_at(pos.row, columns)[1] end elseif self.wrap_page_scrolling then - app = self._private.grid.children[#self._private.grid.children] + app = self:get_grid().children[#self:get_grid().children] end app:select() end local function scroll(self, dir) - if #self._private.grid.children < 1 then + if #self:get_grid().children < 1 then self._private.active_widget = nil return end - local pos = self._private.grid:get_widget_position(self._private.active_widget) + local pos = self:get_grid():get_widget_position(self._private.active_widget) local can_scroll = false local step_size = 0 local if_cant_scroll_func = nil if dir == "up" then - can_scroll = self._private.grid:index(self._private.active_widget) > 1 + can_scroll = self:get_grid():index(self._private.active_widget) > 1 step_size = -1 if_cant_scroll_func = function() page_backward(self, "up") end elseif dir == "down" then - can_scroll = self._private.grid:index(self._private.active_widget) < #self._private.grid.children + can_scroll = self:get_grid():index(self._private.active_widget) < #self:get_grid().children step_size = 1 if_cant_scroll_func = function() page_forward(self, "down") end elseif dir == "left" then - can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col - 1) ~= nil - step_size = -self._private.grid.forced_num_rows + can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col - 1) ~= nil + step_size = -self:get_grid().forced_num_rows if_cant_scroll_func = function() page_backward(self, "left") end elseif dir == "right" then - can_scroll = self._private.grid:get_widgets_at(pos.row, pos.col + 1) ~= nil - step_size = self._private.grid.forced_num_cols + can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col + 1) ~= nil + step_size = self:get_grid().forced_num_cols if_cant_scroll_func = function() page_forward(self, "right") end end if can_scroll then - local app = gtable.cycle_value(self._private.grid.children, self._private.active_widget, step_size) + local app = gtable.cycle_value(self:get_grid().children, self._private.active_widget, step_size) app:select() else if_cant_scroll_func() @@ -533,7 +533,7 @@ local function build_widget(self) widget = widget } - self._private.grid:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + self:get_grid():connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) if button == 4 then self:scroll_up() elseif button == 5 then @@ -541,7 +541,7 @@ local function build_widget(self) end end) - self._private.prompt:connect_signal("text::changed", function(_, text) + self:get_prompt():connect_signal("text::changed", function(_, text) if text == self._private.text then return end @@ -550,7 +550,7 @@ local function build_widget(self) self._private.search_timer:again() end) - self._private.prompt:connect_signal("key::release", function(_, mod, key, cmd) + self:get_prompt():connect_signal("key::release", function(_, mod, key, cmd) if key == "Escape" then self:hide() end @@ -573,11 +573,10 @@ local function build_widget(self) end end) - self._private.max_apps_per_page = self._private.grid.forced_num_cols * self._private.grid.forced_num_rows + self._private.max_apps_per_page = self:get_grid().forced_num_cols * self:get_grid().forced_num_rows self._private.apps_per_page = self._private.max_apps_per_page end --- Sets favorites function app_launcher:set_favorites(favorites) self.favorites = favorites sort_apps(self) @@ -585,40 +584,34 @@ function app_launcher:set_favorites(favorites) search(self) end ---- Scrolls up function app_launcher:scroll_up() scroll(self, "up") end ---- Scrolls down function app_launcher:scroll_down() scroll(self, "down") end ---- Scrolls to the left function app_launcher:scroll_left() scroll(self, "left") end ---- Scrolls to the right function app_launcher:scroll_right() scroll(self, "right") end ---- Shows the app launcher function app_launcher:show() if self.show_on_focused_screen then - self._private.widget.screen = awful.screen.focused() + self:get_widget().screen = awful.screen.focused() end - self._private.widget.visible = true - self._private.prompt:start() + self:get_widget().visible = true + self:get_prompt():start() self:emit_signal("visibility", true) end ---- Hides the app launcher function app_launcher:hide() - if self._private.widget.visible == false then + if self:get_widget().visible == false then return end @@ -626,23 +619,21 @@ function app_launcher:hide() self:reset() end - self._private.widget.visible = false - self._private.prompt:stop() + self:get_widget().visible = false + self:get_prompt():stop() self:emit_signal("visibility", false) end ---- Toggles the app launcher function app_launcher:toggle() - if self._private.widget.visible then + if self:get_widget().visible then self:hide() else self:show() end end --- Reset the app page into the initial state function app_launcher:reset() - self._private.grid:reset() + self:get_grid():reset() self._private.matched_apps = self._private.all_apps self._private.apps_per_page = self._private.max_apps_per_page self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) @@ -651,19 +642,30 @@ function app_launcher:reset() for index, app in ipairs(self._private.all_apps) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then - self._private.grid:add(app_widget(self, app)) + self:get_grid():add(app_widget(self, app)) else break end end - local app = self._private.grid:get_widgets_at(1, 1)[1] + local app = self:get_grid():get_widgets_at(1, 1)[1] app:select() - self._private.prompt:set_text("") + self:get_prompt():set_text("") +end + +function app_launcher:get_widget() + return self._private.widget +end + +function app_launcher:get_prompt() + return self._private.prompt +end + +function app_launcher:get_grid() + return self._private.grid end --- Returns a new app launcher local function new(args) args = args or {} From f161aa2092e50d9898546aa264d26cee17f577b6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 24 Feb 2023 23:17:40 +0200 Subject: [PATCH 082/185] Expose every useful private local function as a public method + add sort_fn prop --- widget/app_launcher/init.lua | 372 +++++++++++++++++------------------ 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index ded64357..ccabf035 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -196,160 +196,6 @@ local function app_widget(self, app) return widget end -local function search(self) - local text = self._private.text - local old_pos = self:get_grid():get_widget_position(self._private.active_widget) - - -- Reset all the matched apps - self._private.matched_apps = {} - -- Remove all the grid widgets - self:get_grid():reset() - - if text == "" then - self._private.matched_apps = self._private.all_apps - else - for _, app in ipairs(self._private.all_apps) do - text = text:gsub( "%W", "" ) - - -- Check if there's a match by the app name or app command - if string.find(app.name:lower(), text:lower(), 1, true) ~= nil or - self.search_commands and string.find(app.exec, text:lower(), 1, true) ~= nil - then - table.insert(self._private.matched_apps, app) - end - end - - -- Sort by string similarity - table.sort(self._private.matched_apps, function(a, b) - return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) - end) - end - for _, app in ipairs(self._private.matched_apps) do - -- Only add the widgets for apps that are part of the first page - if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget(self, app)) - end - end - - -- Recalculate the apps per page based on the current matched apps - self._private.apps_per_page = math.min(#self._private.matched_apps, self._private.max_apps_per_page) - - -- Recalculate the pages count based on the current apps per page - self._private.pages_count = math.ceil(math.max(1, #self._private.matched_apps) / math.max(1, self._private.apps_per_page)) - - -- Page should be 1 after a search - self._private.current_page = 1 - - -- This is an option to mimic rofi behaviour where after a search - -- it will reselect the app whose index is the same as the app index that was previously selected - -- and if matched_apps.length < current_index it will instead select the app with the greatest index - if self.try_to_keep_index_after_searching then - if self:get_grid():get_widgets_at(old_pos.row, old_pos.col) == nil then - local app = self:get_grid().children[#self:get_grid().children] - app:select() - else - local app = self:get_grid():get_widgets_at(old_pos.row, old_pos.col)[1] - app:select() - end - -- Otherwise select the first app on the list - elseif #self:get_grid().children > 0 then - local app = self:get_grid():get_widgets_at(1, 1)[1] - app:select() - end -end - -local function page_forward(self, dir) - local min_app_index_to_include = 0 - local max_app_index_to_include = self._private.apps_per_page - - if self._private.current_page < self._private.pages_count then - min_app_index_to_include = self._private.apps_per_page * self._private.current_page - self._private.current_page = self._private.current_page + 1 - max_app_index_to_include = self._private.apps_per_page * self._private.current_page - elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then - self._private.current_page = 1 - min_app_index_to_include = 0 - max_app_index_to_include = self._private.apps_per_page - elseif self.wrap_app_scrolling then - local app = self:get_grid():get_widgets_at(1, 1)[1] - app:select() - return - else - return - end - - local pos = self:get_grid():get_widget_position(self._private.active_widget) - - -- Remove the current page apps from the grid - self:get_grid():reset() - - for index, app in ipairs(self._private.matched_apps) do - -- Only add widgets that are between this range (part of the current page) - if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) - end - end - - if self._private.current_page > 1 or self.wrap_page_scrolling then - local app = nil - if dir == "down" then - app = self:get_grid():get_widgets_at(1, 1)[1] - elseif dir == "right" then - app = self:get_grid():get_widgets_at(pos.row, 1) - if app then - app = app[1] - end - if app == nil then - app = self:get_grid().children[#self:get_grid().children] - end - end - app:select() - end -end - -local function page_backward(self, dir) - if self._private.current_page > 1 then - self._private.current_page = self._private.current_page - 1 - elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then - self._private.current_page = self._private.pages_count - elseif self.wrap_app_scrolling then - local app = self:get_grid().children[#self:get_grid().children] - app:select() - return - else - return - end - - local pos = self:get_grid():get_widget_position(self._private.active_widget) - - -- Remove the current page apps from the grid - self:get_grid():reset() - - local max_app_index_to_include = self._private.apps_per_page * self._private.current_page - local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - - for index, app in ipairs(self._private.matched_apps) do - -- Only add widgets that are between this range (part of the current page) - if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) - end - end - - local app = nil - if self._private.current_page < self._private.pages_count then - if dir == "up" then - app = self:get_grid().children[#self:get_grid().children] - else - -- Keep the same row from last page - local _, columns = self:get_grid():get_dimension() - app = self:get_grid():get_widgets_at(pos.row, columns)[1] - end - elseif self.wrap_page_scrolling then - app = self:get_grid().children[#self:get_grid().children] - end - app:select() -end - local function scroll(self, dir) if #self:get_grid().children < 1 then self._private.active_widget = nil @@ -364,19 +210,19 @@ local function scroll(self, dir) if dir == "up" then can_scroll = self:get_grid():index(self._private.active_widget) > 1 step_size = -1 - if_cant_scroll_func = function() page_backward(self, "up") end + if_cant_scroll_func = function() self:page_backward("up") end elseif dir == "down" then can_scroll = self:get_grid():index(self._private.active_widget) < #self:get_grid().children step_size = 1 - if_cant_scroll_func = function() page_forward(self, "down") end + if_cant_scroll_func = function() self:page_forward("down") end elseif dir == "left" then can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col - 1) ~= nil step_size = -self:get_grid().forced_num_rows - if_cant_scroll_func = function() page_backward(self, "left") end + if_cant_scroll_func = function() self:page_backward("left") end elseif dir == "right" then can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col + 1) ~= nil step_size = self:get_grid().forced_num_cols - if_cant_scroll_func = function() page_forward(self, "right") end + if_cant_scroll_func = function() self:page_forward("right") end end if can_scroll then @@ -387,29 +233,6 @@ local function scroll(self, dir) end end -local function sort_apps(self) - table.sort(self._private.all_apps, function(a, b) - local is_a_favorite = has_value(self.favorites, a.id) - local is_b_favorite = has_value(self.favorites, b.id) - - -- Sort the favorite apps first - if is_a_favorite and not is_b_favorite then - return true - elseif not is_a_favorite and is_b_favorite then - return false - end - - -- Sort alphabetically if specified - if self.sort_alphabetically then - return a.name:lower() < b.name:lower() - elseif self.reverse_sort_alphabetically then - return b.name:lower() > a.name:lower() - else - return true - end - end) -end - local function generate_apps(self) self._private.all_apps = {} self._private.matched_apps = {} @@ -458,7 +281,7 @@ local function generate_apps(self) end end - sort_apps(self) + self:sort_apps() end local function build_widget(self) @@ -577,11 +400,95 @@ local function build_widget(self) self._private.apps_per_page = self._private.max_apps_per_page end +function app_launcher:sort_apps(sort_fn) + table.sort(self._private.all_apps, sort_fn or self.sort_fn or function(a, b) + local is_a_favorite = has_value(self.favorites, a.id) + local is_b_favorite = has_value(self.favorites, b.id) + + -- Sort the favorite apps first + if is_a_favorite and not is_b_favorite then + return true + elseif not is_a_favorite and is_b_favorite then + return false + end + + -- Sort alphabetically if specified + if self.sort_alphabetically then + return a.name:lower() < b.name:lower() + elseif self.reverse_sort_alphabetically then + return b.name:lower() > a.name:lower() + else + return true + end + end) +end + function app_launcher:set_favorites(favorites) self.favorites = favorites - sort_apps(self) - -- Refresh the app list - search(self) + self:sort_apps() + self:search() -- Refresh the app list +end + +function app_launcher:search() + local text = self._private.text + local old_pos = self:get_grid():get_widget_position(self._private.active_widget) + + -- Reset all the matched apps + self._private.matched_apps = {} + -- Remove all the grid widgets + self:get_grid():reset() + + if text == "" then + self._private.matched_apps = self._private.all_apps + else + for _, app in ipairs(self._private.all_apps) do + text = text:gsub( "%W", "" ) + + -- Check if there's a match by the app name or app command + if string.find(app.name:lower(), text:lower(), 1, true) ~= nil or + self.search_commands and string.find(app.exec, text:lower(), 1, true) ~= nil + then + table.insert(self._private.matched_apps, app) + end + end + + -- Sort by string similarity + table.sort(self._private.matched_apps, function(a, b) + return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) + end) + end + for _, app in ipairs(self._private.matched_apps) do + -- Only add the widgets for apps that are part of the first page + if #self:get_grid().children + 1 <= self._private.max_apps_per_page then + self:get_grid():add(app_widget(self, app)) + end + end + + -- Recalculate the apps per page based on the current matched apps + self._private.apps_per_page = math.min(#self._private.matched_apps, self._private.max_apps_per_page) + + -- Recalculate the pages count based on the current apps per page + self._private.pages_count = math.ceil(math.max(1, #self._private.matched_apps) / math.max(1, self._private.apps_per_page)) + + -- Page should be 1 after a search + self._private.current_page = 1 + + -- This is an option to mimic rofi behaviour where after a search + -- it will reselect the app whose index is the same as the app index that was previously selected + -- and if matched_apps.length < current_index it will instead select the app with the greatest index + if self.try_to_keep_index_after_searching then + if self:get_grid():get_widgets_at(old_pos.row, old_pos.col) == nil then + local app = self:get_grid().children[#self:get_grid().children] + app:select() + else + local app = self:get_grid():get_widgets_at(old_pos.row, old_pos.col)[1] + app:select() + end + -- Otherwise select the first app on the list + elseif #self:get_grid().children > 0 then + local app = self:get_grid():get_widgets_at(1, 1)[1] + app:select() + end end function app_launcher:scroll_up() @@ -600,6 +507,98 @@ function app_launcher:scroll_right() scroll(self, "right") end +function app_launcher:page_forward(dir) + local min_app_index_to_include = 0 + local max_app_index_to_include = self._private.apps_per_page + + if self._private.current_page < self._private.pages_count then + min_app_index_to_include = self._private.apps_per_page * self._private.current_page + self._private.current_page = self._private.current_page + 1 + max_app_index_to_include = self._private.apps_per_page * self._private.current_page + elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then + self._private.current_page = 1 + min_app_index_to_include = 0 + max_app_index_to_include = self._private.apps_per_page + elseif self.wrap_app_scrolling then + local app = self:get_grid():get_widgets_at(1, 1)[1] + app:select() + return + else + return + end + + local pos = self:get_grid():get_widget_position(self._private.active_widget) + + -- Remove the current page apps from the grid + self:get_grid():reset() + + for index, app in ipairs(self._private.matched_apps) do + -- Only add widgets that are between this range (part of the current page) + if index > min_app_index_to_include and index <= max_app_index_to_include then + self:get_grid():add(app_widget(self, app)) + end + end + + if self._private.current_page > 1 or self.wrap_page_scrolling then + local app = nil + if dir == "down" then + app = self:get_grid():get_widgets_at(1, 1)[1] + elseif dir == "right" then + app = self:get_grid():get_widgets_at(pos.row, 1) + if app then + app = app[1] + end + if app == nil then + app = self:get_grid().children[#self:get_grid().children] + end + end + app:select() + end +end + +function app_launcher:page_backward(dir) + if self._private.current_page > 1 then + self._private.current_page = self._private.current_page - 1 + elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then + self._private.current_page = self._private.pages_count + elseif self.wrap_app_scrolling then + local app = self:get_grid().children[#self:get_grid().children] + app:select() + return + else + return + end + + local pos = self:get_grid():get_widget_position(self._private.active_widget) + + -- Remove the current page apps from the grid + self:get_grid():reset() + + local max_app_index_to_include = self._private.apps_per_page * self._private.current_page + local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page + + for index, app in ipairs(self._private.matched_apps) do + -- Only add widgets that are between this range (part of the current page) + if index > min_app_index_to_include and index <= max_app_index_to_include then + self:get_grid():add(app_widget(self, app)) + end + end + + local app = nil + if self._private.current_page < self._private.pages_count then + if dir == "up" then + app = self:get_grid().children[#self:get_grid().children] + else + -- Keep the same row from last page + local _, columns = self:get_grid():get_dimension() + app = self:get_grid():get_widgets_at(pos.row, columns)[1] + end + elseif self.wrap_page_scrolling then + app = self:get_grid().children[#self:get_grid().children] + end + app:select() +end + function app_launcher:show() if self.show_on_focused_screen then self:get_widget().screen = awful.screen.focused() @@ -669,6 +668,7 @@ end local function new(args) args = args or {} + args.sort_fn = default_value(args.sort_fn, nil) args.favorites = default_value(args.favorites, {}) args.search_commands = default_value(args.search_commands, true) args.skip_names = default_value(args.skip_names, {}) @@ -733,7 +733,7 @@ local function new(args) autostart = true, single_shot = true, callback = function() - search(ret) + ret:search() end } From c45e64b7950f0e71f6e1fcb0589be18fdbee079b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 02:22:34 +0200 Subject: [PATCH 083/185] Rename selected and unselected to select and unselect + add 'page::forward' and 'page::backwards' signals --- widget/app_launcher/init.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index ccabf035..0e6f6ae8 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -167,7 +167,7 @@ local function app_widget(self, app) _self._private.active_widget:unselect() end _self._private.active_widget = self - self:emit_signal("selected") + self:emit_signal("select") self.selected = true if _self.app_template == nil then @@ -178,7 +178,7 @@ local function app_widget(self, app) end function widget:unselect() - self:emit_signal("unselected") + self:emit_signal("unselect") self.selected = false _self._private.active_widget = nil @@ -427,6 +427,7 @@ function app_launcher:set_favorites(favorites) self.favorites = favorites self:sort_apps() self:search() -- Refresh the app list + -- self:search() -- Refresh the app list end function app_launcher:search() @@ -554,6 +555,8 @@ function app_launcher:page_forward(dir) end app:select() end + + self:emit_signal("page::forward", dir) end function app_launcher:page_backward(dir) @@ -597,6 +600,8 @@ function app_launcher:page_backward(dir) app = self:get_grid().children[#self:get_grid().children] end app:select() + + self:emit_signal("page::backward", dir) end function app_launcher:show() From af15c9bc7ab7c32328eff29b2443d10831735ab6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 02:23:22 +0200 Subject: [PATCH 084/185] Add scroll signal --- widget/app_launcher/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 0e6f6ae8..b09204bf 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -228,6 +228,7 @@ local function scroll(self, dir) if can_scroll then local app = gtable.cycle_value(self:get_grid().children, self._private.active_widget, step_size) app:select() + self:emit_signal("scroll", dir) else if_cant_scroll_func() end From 9e0c6fd96ab9ec3f2e7feadba53850f0be8dd694 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 02:28:18 +0200 Subject: [PATCH 085/185] Pass the app launcher to the app_template --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index b09204bf..4f78aa15 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -136,7 +136,7 @@ local function app_widget(self, app) end end) else - widget = self.app_template(app) + widget = self.app_template(app, self) end widget:connect_signal("button::press", function(app, _, __, button) From 3c44c5c0566c70c9884bcba28c3c77b2594ce4f5 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 02:35:07 +0200 Subject: [PATCH 086/185] Use app_info:launch() for non terminal apps to fix issues with launching file managers --- widget/app_launcher/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4f78aa15..2cfdf641 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -154,7 +154,7 @@ local function app_widget(self, app) if app.terminal == true then awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) else - awful.spawn(app.exec) + app:launch() end if _self.hide_on_launch then @@ -276,6 +276,9 @@ local function generate_apps(self) icon_name = desktop_app_info:get_string("Icon"), terminal = desktop_app_info:get_string("Terminal") == "true" and true or false, exec = exec, + launch = function() + app:launch() + end }) end end From 47da489ba829cd294291f8f9c96c9a40fc6e9e1a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 02:39:59 +0200 Subject: [PATCH 087/185] Change class for terminal apps --- widget/app_launcher/init.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 2cfdf641..06f809a5 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -152,7 +152,14 @@ local function app_widget(self, app) local _self = self function widget:spawn() if app.terminal == true then - awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) + local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) + local class = app.startup_wm_class or app.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) else app:launch() end From 282ce3522514802d9566d5aae71c6f509fd21726 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:17:07 +0200 Subject: [PATCH 088/185] Rename spawn to run --- widget/app_launcher/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 06f809a5..aa028433 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -142,7 +142,7 @@ local function app_widget(self, app) widget:connect_signal("button::press", function(app, _, __, button) if button == 1 then if self._private.active_widget == app or not self.select_before_spawn then - app:spawn() + app:run() else app:select() end @@ -150,7 +150,7 @@ local function app_widget(self, app) end) local _self = self - function widget:spawn() + function widget:run() if app.terminal == true then local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) local class = app.startup_wm_class or app.name @@ -196,7 +196,7 @@ local function app_widget(self, app) end end - app.spawn = widget.spawn + app.run = widget.run app.select = widget.select app.unselect = widget.unselect @@ -390,7 +390,7 @@ local function build_widget(self) end if key == "Return" then if self._private.active_widget ~= nil then - self._private.active_widget:spawn() + self._private.active_widget:run() end end if key == "Up" then From a992a526b0b558eb5e73d72e32f626c2a4024db8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:17:34 +0200 Subject: [PATCH 089/185] Rename AWESOME_SENSIBLE_TERMINASL_PATH TO AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index aa028433..bbba341d 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -19,7 +19,7 @@ local app_launcher = { mt = {} } local KILL_OLD_INOTIFY_SCRIPT = [[ ps x | grep "inotifywait -e modify /usr/share/applications" | grep -v grep | awk '{print $1}' | xargs kill ]] local INOTIFY_SCRIPT = [[ bash -c "while (inotifywait -e modify /usr/share/applications -qq) do echo; done" ]] -local AWESOME_SENSIBLE_TERMINAL_PATH = debug.getinfo(1).source:match("@?(.*/)") .. +local AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "awesome-sensible-terminal" local function default_value(value, default) @@ -152,7 +152,7 @@ local function app_widget(self, app) local _self = self function widget:run() if app.terminal == true then - local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_PATH .. " -e " .. app.exec) + local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. app.exec) local class = app.startup_wm_class or app.name awful.spawn.with_shell(string.format( [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], From 86bef7a2d23609174d2b85cace4bdc074ab3843c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:17:40 +0200 Subject: [PATCH 090/185] Add function to run app as root --- widget/app_launcher/init.lua | 26 ++++++++++++++++++++++++++ widget/app_launcher/run-as-root.sh | 5 +++++ 2 files changed, 31 insertions(+) create mode 100755 widget/app_launcher/run-as-root.sh diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index bbba341d..7e744cf9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -21,6 +21,8 @@ local KILL_OLD_INOTIFY_SCRIPT = [[ ps x | grep "inotifywait -e modify /usr/share local INOTIFY_SCRIPT = [[ bash -c "while (inotifywait -e modify /usr/share/applications -qq) do echo; done" ]] local AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "awesome-sensible-terminal" +local RUN_AS_ROOT_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. + "run-as-root.sh" local function default_value(value, default) if value == nil then @@ -169,6 +171,29 @@ local function app_widget(self, app) end end + function widget:run_as_root() + if app.terminal == true then + local pid = awful.spawn.with_shell( + AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. + RUN_AS_ROOT_SCRIPT_PATH .. " " .. + app.exec + ) + local class = app.startup_wm_class or app.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) + else + awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. app.exec) + end + + if _self.hide_on_launch then + _self:hide() + end + end + function widget:select() if _self._private.active_widget then _self._private.active_widget:unselect() @@ -197,6 +222,7 @@ local function app_widget(self, app) end app.run = widget.run + app.run_as_root = widget.run_as_root app.select = widget.select app.unselect = widget.unselect diff --git a/widget/app_launcher/run-as-root.sh b/widget/app_launcher/run-as-root.sh new file mode 100755 index 00000000..61b72fc8 --- /dev/null +++ b/widget/app_launcher/run-as-root.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +PROGRAM=$1 + +pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY $PROGRAM From b300def18cbe4776401720e2a205c3a8aa094a4e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:18:33 +0200 Subject: [PATCH 091/185] Formatting --- widget/app_launcher/init.lua | 76 ++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 7e744cf9..530e6c2e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -82,6 +82,44 @@ local function has_value(tab, val) return false end +local function scroll(self, dir) + if #self:get_grid().children < 1 then + self._private.active_widget = nil + return + end + + local pos = self:get_grid():get_widget_position(self._private.active_widget) + local can_scroll = false + local step_size = 0 + local if_cant_scroll_func = nil + + if dir == "up" then + can_scroll = self:get_grid():index(self._private.active_widget) > 1 + step_size = -1 + if_cant_scroll_func = function() self:page_backward("up") end + elseif dir == "down" then + can_scroll = self:get_grid():index(self._private.active_widget) < #self:get_grid().children + step_size = 1 + if_cant_scroll_func = function() self:page_forward("down") end + elseif dir == "left" then + can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col - 1) ~= nil + step_size = -self:get_grid().forced_num_rows + if_cant_scroll_func = function() self:page_backward("left") end + elseif dir == "right" then + can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col + 1) ~= nil + step_size = self:get_grid().forced_num_cols + if_cant_scroll_func = function() self:page_forward("right") end + end + + if can_scroll then + local app = gtable.cycle_value(self:get_grid().children, self._private.active_widget, step_size) + app:select() + self:emit_signal("scroll", dir) + else + if_cant_scroll_func() + end +end + local function app_widget(self, app) local widget = nil @@ -229,44 +267,6 @@ local function app_widget(self, app) return widget end -local function scroll(self, dir) - if #self:get_grid().children < 1 then - self._private.active_widget = nil - return - end - - local pos = self:get_grid():get_widget_position(self._private.active_widget) - local can_scroll = false - local step_size = 0 - local if_cant_scroll_func = nil - - if dir == "up" then - can_scroll = self:get_grid():index(self._private.active_widget) > 1 - step_size = -1 - if_cant_scroll_func = function() self:page_backward("up") end - elseif dir == "down" then - can_scroll = self:get_grid():index(self._private.active_widget) < #self:get_grid().children - step_size = 1 - if_cant_scroll_func = function() self:page_forward("down") end - elseif dir == "left" then - can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col - 1) ~= nil - step_size = -self:get_grid().forced_num_rows - if_cant_scroll_func = function() self:page_backward("left") end - elseif dir == "right" then - can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col + 1) ~= nil - step_size = self:get_grid().forced_num_cols - if_cant_scroll_func = function() self:page_forward("right") end - end - - if can_scroll then - local app = gtable.cycle_value(self:get_grid().children, self._private.active_widget, step_size) - app:select() - self:emit_signal("scroll", dir) - else - if_cant_scroll_func() - end -end - local function generate_apps(self) self._private.all_apps = {} self._private.matched_apps = {} From 6439e6d3d6489a9fb815d9a84a6a832c3c1069e9 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:30:33 +0200 Subject: [PATCH 092/185] Fix not passing self --- widget/app_launcher/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 530e6c2e..2a16c70a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -259,10 +259,10 @@ local function app_widget(self, app) end end - app.run = widget.run - app.run_as_root = widget.run_as_root - app.select = widget.select - app.unselect = widget.unselect + function app:run() widget:run() end + function app:run_as_root() widget:run_as_root() end + function app:select() widget:select() end + function app:unselect() widget:unselect() end return widget end From 69c5653a4c7144a9c7dcc6da2574b331a23ec111 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 03:46:24 +0200 Subject: [PATCH 093/185] Add a proper refresh method --- widget/app_launcher/init.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 2a16c70a..89c74882 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -463,8 +463,20 @@ end function app_launcher:set_favorites(favorites) self.favorites = favorites self:sort_apps() - self:search() -- Refresh the app list - -- self:search() -- Refresh the app list + self:refresh() +end + +function app_launcher:refresh() + local max_app_index_to_include = self._private.apps_per_page * self._private.current_page + local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page + + self:get_grid():reset() + for index, app in ipairs(self._private.matched_apps) do + -- Only add widgets that are between this range (part of the current page) + if index > min_app_index_to_include and index <= max_app_index_to_include then + self:get_grid():add(app_widget(self, app)) + end + end end function app_launcher:search() From 49c815641cefbea9751c43e3535b822e72bc3db0 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 25 Feb 2023 04:17:46 +0200 Subject: [PATCH 094/185] Simplify and fix scrolling logic --- widget/app_launcher/init.lua | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 89c74882..6a79551e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -88,32 +88,26 @@ local function scroll(self, dir) return end - local pos = self:get_grid():get_widget_position(self._private.active_widget) - local can_scroll = false - local step_size = 0 + local next_app_index = nil local if_cant_scroll_func = nil if dir == "up" then - can_scroll = self:get_grid():index(self._private.active_widget) > 1 - step_size = -1 + next_app_index = self:get_grid():index(self._private.active_widget) - 1 if_cant_scroll_func = function() self:page_backward("up") end elseif dir == "down" then - can_scroll = self:get_grid():index(self._private.active_widget) < #self:get_grid().children - step_size = 1 + next_app_index = self:get_grid():index(self._private.active_widget) + 1 if_cant_scroll_func = function() self:page_forward("down") end elseif dir == "left" then - can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col - 1) ~= nil - step_size = -self:get_grid().forced_num_rows + next_app_index = self:get_grid():index(self._private.active_widget) - self:get_grid().forced_num_rows if_cant_scroll_func = function() self:page_backward("left") end elseif dir == "right" then - can_scroll = self:get_grid():get_widgets_at(pos.row, pos.col + 1) ~= nil - step_size = self:get_grid().forced_num_cols + next_app_index = self:get_grid():index(self._private.active_widget) + self:get_grid().forced_num_rows if_cant_scroll_func = function() self:page_forward("right") end end - if can_scroll then - local app = gtable.cycle_value(self:get_grid().children, self._private.active_widget, step_size) - app:select() + local next_app = self:get_grid().children[next_app_index] + if next_app then + next_app:select() self:emit_signal("scroll", dir) else if_cant_scroll_func() From c084c9bb5d76630c5a3c8b0b62485b384d9884f6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 27 Feb 2023 11:15:31 +0200 Subject: [PATCH 095/185] Add a method to get the current page + pass the current page in the page signals --- widget/app_launcher/init.lua | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 6a79551e..01d812d4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -461,7 +461,7 @@ function app_launcher:set_favorites(favorites) end function app_launcher:refresh() - local max_app_index_to_include = self._private.apps_per_page * self._private.current_page + local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page self:get_grid():reset() @@ -555,10 +555,10 @@ function app_launcher:page_forward(dir) local min_app_index_to_include = 0 local max_app_index_to_include = self._private.apps_per_page - if self._private.current_page < self._private.pages_count then - min_app_index_to_include = self._private.apps_per_page * self._private.current_page - self._private.current_page = self._private.current_page + 1 - max_app_index_to_include = self._private.apps_per_page * self._private.current_page + if self:get_current_page() < self._private.pages_count then + min_app_index_to_include = self._private.apps_per_page * self:get_current_page() + self._private.current_page = self:get_current_page() + 1 + max_app_index_to_include = self._private.apps_per_page * self:get_current_page() elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then self._private.current_page = 1 min_app_index_to_include = 0 @@ -583,7 +583,7 @@ function app_launcher:page_forward(dir) end end - if self._private.current_page > 1 or self.wrap_page_scrolling then + if self:get_current_page() > 1 or self.wrap_page_scrolling then local app = nil if dir == "down" then app = self:get_grid():get_widgets_at(1, 1)[1] @@ -599,12 +599,12 @@ function app_launcher:page_forward(dir) app:select() end - self:emit_signal("page::forward", dir) + self:emit_signal("page::forward", dir, self:get_current_page()) end function app_launcher:page_backward(dir) - if self._private.current_page > 1 then - self._private.current_page = self._private.current_page - 1 + if self:get_current_page() > 1 then + self._private.current_page = self:get_current_page() - 1 elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then self._private.current_page = self._private.pages_count elseif self.wrap_app_scrolling then @@ -620,7 +620,7 @@ function app_launcher:page_backward(dir) -- Remove the current page apps from the grid self:get_grid():reset() - local max_app_index_to_include = self._private.apps_per_page * self._private.current_page + local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page for index, app in ipairs(self._private.matched_apps) do @@ -631,7 +631,7 @@ function app_launcher:page_backward(dir) end local app = nil - if self._private.current_page < self._private.pages_count then + if self:get_current_page() < self._private.pages_count then if dir == "up" then app = self:get_grid().children[#self:get_grid().children] else @@ -644,7 +644,7 @@ function app_launcher:page_backward(dir) end app:select() - self:emit_signal("page::backward", dir) + self:emit_signal("page::backward", dir, self:get_current_page()) end function app_launcher:show() @@ -713,6 +713,10 @@ function app_launcher:get_grid() return self._private.grid end +function app_launcher:get_current_page() + return self._private.current_page +end + local function new(args) args = args or {} From 0982fd604eb928ac33b2a7e2f31d9befdf564e1f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 27 Feb 2023 11:18:02 +0200 Subject: [PATCH 096/185] Add method to get pages count + pass the pages count in the page signals --- widget/app_launcher/init.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 01d812d4..ab25d4ce 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -555,7 +555,7 @@ function app_launcher:page_forward(dir) local min_app_index_to_include = 0 local max_app_index_to_include = self._private.apps_per_page - if self:get_current_page() < self._private.pages_count then + if self:get_current_page() < self:get_pages_count() then min_app_index_to_include = self._private.apps_per_page * self:get_current_page() self._private.current_page = self:get_current_page() + 1 max_app_index_to_include = self._private.apps_per_page * self:get_current_page() @@ -599,14 +599,14 @@ function app_launcher:page_forward(dir) app:select() end - self:emit_signal("page::forward", dir, self:get_current_page()) + self:emit_signal("page::forward", dir, self:get_current_page(), self:get_pages_count()) end function app_launcher:page_backward(dir) if self:get_current_page() > 1 then self._private.current_page = self:get_current_page() - 1 elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then - self._private.current_page = self._private.pages_count + self._private.current_page = self:get_pages_count() elseif self.wrap_app_scrolling then local app = self:get_grid().children[#self:get_grid().children] app:select() @@ -631,7 +631,7 @@ function app_launcher:page_backward(dir) end local app = nil - if self:get_current_page() < self._private.pages_count then + if self:get_current_page() < self:get_pages_count() then if dir == "up" then app = self:get_grid().children[#self:get_grid().children] else @@ -644,7 +644,7 @@ function app_launcher:page_backward(dir) end app:select() - self:emit_signal("page::backward", dir, self:get_current_page()) + self:emit_signal("page::backward", dir, self:get_current_page(), self:get_pages_count()) end function app_launcher:show() @@ -713,6 +713,10 @@ function app_launcher:get_grid() return self._private.grid end +function app_launcher:get_pages_count() + return self._private.pages_count +end + function app_launcher:get_current_page() return self._private.current_page end From 93327aeccf9d32c5b3c5b1b11063715cad9b324f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 27 Feb 2023 11:21:47 +0200 Subject: [PATCH 097/185] Add a method to get the text + add a search signal --- widget/app_launcher/init.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index ab25d4ce..208fcf22 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -396,7 +396,7 @@ local function build_widget(self) end) self:get_prompt():connect_signal("text::changed", function(_, text) - if text == self._private.text then + if text == self:get_text() then return end @@ -474,7 +474,7 @@ function app_launcher:refresh() end function app_launcher:search() - local text = self._private.text + local text = self:get_text() local old_pos = self:get_grid():get_widget_position(self._private.active_widget) -- Reset all the matched apps @@ -533,6 +533,8 @@ function app_launcher:search() local app = self:get_grid():get_widgets_at(1, 1)[1] app:select() end + + self:emit_signal("search", self:get_text(), self:get_current_page(), self:get_pages_count()) end function app_launcher:scroll_up() @@ -721,6 +723,10 @@ function app_launcher:get_current_page() return self._private.current_page end +function app_launcher:get_text() + return self._private.text +end + local function new(args) args = args or {} From 8f7ce8fe1ee5b77d9570faf5c4415129b52df87d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 1 Mar 2023 07:13:46 +0200 Subject: [PATCH 098/185] Add more methods + don't connect the button::press signals for templates --- widget/app_launcher/init.lua | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 208fcf22..8089ca78 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -169,20 +169,20 @@ local function app_widget(self, app) widget.cursor = "left_ptr" end end) + + widget:connect_signal("button::press", function(app, _, __, button) + if button == 1 then + if app:get_is_selected() or not self.select_before_spawn then + app:run() + else + app:select() + end + end + end) else widget = self.app_template(app, self) end - widget:connect_signal("button::press", function(app, _, __, button) - if button == 1 then - if self._private.active_widget == app or not self.select_before_spawn then - app:run() - else - app:select() - end - end - end) - local _self = self function widget:run() if app.terminal == true then @@ -203,6 +203,14 @@ local function app_widget(self, app) end end + function widget:run_or_select() + if app:get_is_selected() then + app:run() + else + app:select() + end + end + function widget:run_as_root() if app.terminal == true then local pid = awful.spawn.with_shell( @@ -253,10 +261,16 @@ local function app_widget(self, app) end end + function widget:get_is_selected() + return self._private.active_widget == app + end + function app:run() widget:run() end + function app:run_or_select() widget:run_or_select() end function app:run_as_root() widget:run_as_root() end function app:select() widget:select() end function app:unselect() widget:unselect() end + function app:get_is_selected() widget:get_is_selected() end return widget end From 12478ebbc613feee18567f7335f1666c62439185 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 1 Mar 2023 19:15:04 +0200 Subject: [PATCH 099/185] Clearer naming --- widget/app_launcher/init.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 8089ca78..d6ea0df9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -183,7 +183,7 @@ local function app_widget(self, app) widget = self.app_template(app, self) end - local _self = self + local app_launcher = self function widget:run() if app.terminal == true then local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. app.exec) @@ -198,8 +198,8 @@ local function app_widget(self, app) app:launch() end - if _self.hide_on_launch then - _self:hide() + if app_launcher.hide_on_launch then + app_launcher:hide() end end @@ -229,35 +229,35 @@ local function app_widget(self, app) awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. app.exec) end - if _self.hide_on_launch then - _self:hide() + if app_launcher.hide_on_launch then + app_launcher:hide() end end function widget:select() - if _self._private.active_widget then - _self._private.active_widget:unselect() + if app_launcher._private.active_widget then + app_launcher._private.active_widget:unselect() end - _self._private.active_widget = self + app_launcher._private.active_widget = self self:emit_signal("select") self.selected = true - if _self.app_template == nil then - widget.bg = _self.app_selected_color + if app_launcher.app_template == nil then + widget.bg = app_launcher.app_selected_color local name_widget = self:get_children_by_id("name_role")[1] - name_widget.markup = string.format("%s", _self.app_name_selected_color, name_widget.text) + name_widget.markup = string.format("%s", app_launcher.app_name_selected_color, name_widget.text) end end function widget:unselect() self:emit_signal("unselect") self.selected = false - _self._private.active_widget = nil + app_launcher._private.active_widget = nil - if _self.app_template == nil then - widget.bg = _self.app_normal_color + if app_launcher.app_template == nil then + widget.bg = app_launcher.app_normal_color local name_widget = self:get_children_by_id("name_role")[1] - name_widget.markup = string.format("%s", _self.app_name_normal_color, name_widget.text) + name_widget.markup = string.format("%s", app_launcher.app_name_normal_color, name_widget.text) end end From 051c84bd293f0c47002bc9e3a211a99f9b9c8ffb Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 1 Mar 2023 19:25:28 +0200 Subject: [PATCH 100/185] Fix run_or_select + some renaming --- widget/app_launcher/init.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index d6ea0df9..20ffb2ee 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -172,7 +172,7 @@ local function app_widget(self, app) widget:connect_signal("button::press", function(app, _, __, button) if button == 1 then - if app:get_is_selected() or not self.select_before_spawn then + if app:is_selected() or not self.select_before_spawn then app:run() else app:select() @@ -204,10 +204,10 @@ local function app_widget(self, app) end function widget:run_or_select() - if app:get_is_selected() then - app:run() + if self:is_selected() then + self:run() else - app:select() + self:select() end end @@ -261,8 +261,8 @@ local function app_widget(self, app) end end - function widget:get_is_selected() - return self._private.active_widget == app + function widget:is_selected() + return app_launcher._private.active_widget == self end function app:run() widget:run() end @@ -270,7 +270,7 @@ local function app_widget(self, app) function app:run_as_root() widget:run_as_root() end function app:select() widget:select() end function app:unselect() widget:unselect() end - function app:get_is_selected() widget:get_is_selected() end + function app:is_selected() widget:is_selected() end return widget end From a33bb8f7745aa538e86619b14da7806898795a0a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 1 Mar 2023 19:25:44 +0200 Subject: [PATCH 101/185] Try to collect garbage --- widget/app_launcher/init.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 20ffb2ee..c8aff7e4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -479,6 +479,8 @@ function app_launcher:refresh() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page self:get_grid():reset() + collectgarbage("collect") + for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then @@ -495,6 +497,7 @@ function app_launcher:search() self._private.matched_apps = {} -- Remove all the grid widgets self:get_grid():reset() + collectgarbage("collect") if text == "" then self._private.matched_apps = self._private.all_apps @@ -591,6 +594,7 @@ function app_launcher:page_forward(dir) -- Remove the current page apps from the grid self:get_grid():reset() + collectgarbage("collect") for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) @@ -635,6 +639,7 @@ function app_launcher:page_backward(dir) -- Remove the current page apps from the grid self:get_grid():reset() + collectgarbage("collect") local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page From c6581734d8acc1f1f387292fa6eb183b3b80015d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 1 Mar 2023 19:45:32 +0200 Subject: [PATCH 102/185] Improve searching --- widget/app_launcher/fzy.lua | 275 +++++++++++++++++++++++++++++++++++ widget/app_launcher/init.lua | 14 +- 2 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 widget/app_launcher/fzy.lua diff --git a/widget/app_launcher/fzy.lua b/widget/app_launcher/fzy.lua new file mode 100644 index 00000000..d80b543f --- /dev/null +++ b/widget/app_launcher/fzy.lua @@ -0,0 +1,275 @@ +-- The lua implementation of the fzy string matching algorithm + +local SCORE_GAP_LEADING = -0.005 +local SCORE_GAP_TRAILING = -0.005 +local SCORE_GAP_INNER = -0.01 +local SCORE_MATCH_CONSECUTIVE = 1.0 +local SCORE_MATCH_SLASH = 0.9 +local SCORE_MATCH_WORD = 0.8 +local SCORE_MATCH_CAPITAL = 0.7 +local SCORE_MATCH_DOT = 0.6 +local SCORE_MAX = math.huge +local SCORE_MIN = -math.huge +local MATCH_MAX_LENGTH = 1024 + +local fzy = {} + +-- Check if `needle` is a subsequence of the `haystack`. +-- +-- Usually called before `score` or `positions`. +-- +-- Args: +-- needle (string) +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- bool +function fzy.has_match(needle, haystack, case_sensitive) + if not case_sensitive then + needle = string.lower(needle) + haystack = string.lower(haystack) + end + + local j = 1 + for i = 1, string.len(needle) do + j = string.find(haystack, needle:sub(i, i), j, true) + if not j then + return false + else + j = j + 1 + end + end + + return true +end + +local function is_lower(c) + return c:match("%l") +end + +local function is_upper(c) + return c:match("%u") +end + +local function precompute_bonus(haystack) + local match_bonus = {} + + local last_char = "/" + for i = 1, string.len(haystack) do + local this_char = haystack:sub(i, i) + if last_char == "/" or last_char == "\\" then + match_bonus[i] = SCORE_MATCH_SLASH + elseif last_char == "-" or last_char == "_" or last_char == " " then + match_bonus[i] = SCORE_MATCH_WORD + elseif last_char == "." then + match_bonus[i] = SCORE_MATCH_DOT + elseif is_lower(last_char) and is_upper(this_char) then + match_bonus[i] = SCORE_MATCH_CAPITAL + else + match_bonus[i] = 0 + end + + last_char = this_char + end + + return match_bonus +end + +local function compute(needle, haystack, D, M, case_sensitive) + -- Note that the match bonuses must be computed before the arguments are + -- converted to lowercase, since there are bonuses for camelCase. + local match_bonus = precompute_bonus(haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if not case_sensitive then + needle = string.lower(needle) + haystack = string.lower(haystack) + end + + -- Because lua only grants access to chars through substring extraction, + -- get all the characters from the haystack once now, to reuse below. + local haystack_chars = {} + for i = 1, m do + haystack_chars[i] = haystack:sub(i, i) + end + + for i = 1, n do + D[i] = {} + M[i] = {} + + local prev_score = SCORE_MIN + local gap_score = i == n and SCORE_GAP_TRAILING or SCORE_GAP_INNER + local needle_char = needle:sub(i, i) + + for j = 1, m do + if needle_char == haystack_chars[j] then + local score = SCORE_MIN + if i == 1 then + score = ((j - 1) * SCORE_GAP_LEADING) + match_bonus[j] + elseif j > 1 then + local a = M[i - 1][j - 1] + match_bonus[j] + local b = D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE + score = math.max(a, b) + end + D[i][j] = score + prev_score = math.max(score, prev_score + gap_score) + M[i][j] = prev_score + else + D[i][j] = SCORE_MIN + prev_score = prev_score + gap_score + M[i][j] = prev_score + end + end + end +end + +-- Compute a matching score. +-- +-- Args: +-- needle (string): must be a subequence of `haystack`, or the result is +-- undefined. +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- number: higher scores indicate better matches. See also `get_score_min` +-- and `get_score_max`. +function fzy.score(needle, haystack, case_sensitive) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then + return SCORE_MIN + elseif n == m then + return SCORE_MAX + else + local D = {} + local M = {} + compute(needle, haystack, D, M, case_sensitive) + return M[n][m] + end +end + +-- Compute the locations where fzy matches a string. +-- +-- Determine where each character of the `needle` is matched to the `haystack` +-- in the optimal match. +-- +-- Args: +-- needle (string): must be a subequence of `haystack`, or the result is +-- undefined. +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- {int,...}: indices, where `indices[n]` is the location of the `n`th +-- character of `needle` in `haystack`. +-- number: the same matching score returned by `score` +function fzy.positions(needle, haystack, case_sensitive) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then + return {}, SCORE_MIN + elseif n == m then + local consecutive = {} + for i = 1, n do + consecutive[i] = i + end + return consecutive, SCORE_MAX + end + + local D = {} + local M = {} + compute(needle, haystack, D, M, case_sensitive) + + local positions = {} + local match_required = false + local j = m + for i = n, 1, -1 do + while j >= 1 do + if D[i][j] ~= SCORE_MIN and (match_required or D[i][j] == M[i][j]) then + match_required = (i ~= 1) and (j ~= 1) and ( + M[i][j] == D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE) + positions[i] = j + j = j - 1 + break + else + j = j - 1 + end + end + end + + return positions, M[n][m] +end + +-- Apply `has_match` and `positions` to an array of haystacks. +-- +-- Args: +-- needle (string) +-- haystack ({string, ...}) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- {{idx, positions, score}, ...}: an array with one entry per matching line +-- in `haystacks`, each entry giving the index of the line in `haystacks` +-- as well as the equivalent to the return value of `positions` for that +-- line. +function fzy.filter(needle, haystacks, case_sensitive) + local result = {} + + for i, line in ipairs(haystacks) do + if fzy.has_match(needle, line, case_sensitive) then + local p, s = fzy.positions(needle, line, case_sensitive) + table.insert(result, {i, p, s}) + end + end + + return result +end + +-- The lowest value returned by `score`. +-- +-- In two special cases: +-- - an empty `needle`, or +-- - a `needle` or `haystack` larger than than `get_max_length`, +-- the `score` function will return this exact value, which can be used as a +-- sentinel. This is the lowest possible score. +function fzy.get_score_min() + return SCORE_MIN +end + +-- The score returned for exact matches. This is the highest possible score. +function fzy.get_score_max() + return SCORE_MAX +end + +-- The maximum size for which `fzy` will evaluate scores. +function fzy.get_max_length() + return MATCH_MAX_LENGTH +end + +-- The minimum score returned for normal matches. +-- +-- For matches that don't return `get_score_min`, their score will be greater +-- than than this value. +function fzy.get_score_floor() + return MATCH_MAX_LENGTH * SCORE_GAP_INNER +end + +-- The maximum score for non-exact matches. +-- +-- For matches that don't return `get_score_max`, their score will be less than +-- this value. +function fzy.get_score_ceiling() + return MATCH_MAX_LENGTH * SCORE_MATCH_CONSECUTIVE +end + +-- The name of the currently-running implmenetation, "lua" or "native". +function fzy.get_implementation_name() + return "lua" +end + +return fzy \ No newline at end of file diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c8aff7e4..4ad058bd 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -6,6 +6,7 @@ local gtimer = require("gears.timer") local wibox = require("wibox") local beautiful = require("beautiful") local prompt_widget = require(... .. ".prompt") +local fzy = require(... .. ".fzy") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -505,17 +506,20 @@ function app_launcher:search() for _, app in ipairs(self._private.all_apps) do text = text:gsub( "%W", "" ) - -- Check if there's a match by the app name or app command - if string.find(app.name:lower(), text:lower(), 1, true) ~= nil or - self.search_commands and string.find(app.exec, text:lower(), 1, true) ~= nil - then + -- Filter with fzy + if fzy.has_match(text:lower(), app.name) or (self.search_commands and fzy.has_match(text:lower(), app.exec)) then table.insert(self._private.matched_apps, app) end end -- Sort by string similarity table.sort(self._private.matched_apps, function(a, b) - return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) + if self.search_commands then + return string_levenshtein(text, a.name) + string_levenshtein(text, a.exec) < + string_levenshtein(text, b.name) + string_levenshtein(text, b.exec) + else + return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) + end end) end for _, app in ipairs(self._private.matched_apps) do From 468e4d9119073049b5e45cc364ff9de7f29e9e30 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 2 Mar 2023 03:31:09 +0200 Subject: [PATCH 103/185] Rename active_widget for selected_app_widget and a public method to retrive it's value --- widget/app_launcher/init.lua | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4ad058bd..9a58025e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -85,7 +85,7 @@ end local function scroll(self, dir) if #self:get_grid().children < 1 then - self._private.active_widget = nil + self._private.selected_app_widget = nil return end @@ -93,16 +93,16 @@ local function scroll(self, dir) local if_cant_scroll_func = nil if dir == "up" then - next_app_index = self:get_grid():index(self._private.active_widget) - 1 + next_app_index = self:get_grid():index(self:get_selected_app_widget()) - 1 if_cant_scroll_func = function() self:page_backward("up") end elseif dir == "down" then - next_app_index = self:get_grid():index(self._private.active_widget) + 1 + next_app_index = self:get_grid():index(self:get_selected_app_widget()) + 1 if_cant_scroll_func = function() self:page_forward("down") end elseif dir == "left" then - next_app_index = self:get_grid():index(self._private.active_widget) - self:get_grid().forced_num_rows + next_app_index = self:get_grid():index(self:get_selected_app_widget()) - self:get_grid().forced_num_rows if_cant_scroll_func = function() self:page_backward("left") end elseif dir == "right" then - next_app_index = self:get_grid():index(self._private.active_widget) + self:get_grid().forced_num_rows + next_app_index = self:get_grid():index(self:get_selected_app_widget()) + self:get_grid().forced_num_rows if_cant_scroll_func = function() self:page_forward("right") end end @@ -236,10 +236,10 @@ local function app_widget(self, app) end function widget:select() - if app_launcher._private.active_widget then - app_launcher._private.active_widget:unselect() + if app_launcher:get_selected_app_widget() then + app_launcher:get_selected_app_widget():unselect() end - app_launcher._private.active_widget = self + app_launcher._private.selected_app_widget = self self:emit_signal("select") self.selected = true @@ -253,7 +253,7 @@ local function app_widget(self, app) function widget:unselect() self:emit_signal("unselect") self.selected = false - app_launcher._private.active_widget = nil + app_launcher._private.selected_app_widget = nil if app_launcher.app_template == nil then widget.bg = app_launcher.app_normal_color @@ -263,7 +263,7 @@ local function app_widget(self, app) end function widget:is_selected() - return app_launcher._private.active_widget == self + return app_launcher._private.selected_app_widget == self end function app:run() widget:run() end @@ -424,8 +424,8 @@ local function build_widget(self) self:hide() end if key == "Return" then - if self._private.active_widget ~= nil then - self._private.active_widget:run() + if self:get_selected_app_widget() ~= nil then + self:get_selected_app_widget():run() end end if key == "Up" then @@ -492,7 +492,7 @@ end function app_launcher:search() local text = self:get_text() - local old_pos = self:get_grid():get_widget_position(self._private.active_widget) + local old_pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) -- Reset all the matched apps self._private.matched_apps = {} @@ -594,7 +594,7 @@ function app_launcher:page_forward(dir) return end - local pos = self:get_grid():get_widget_position(self._private.active_widget) + local pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) -- Remove the current page apps from the grid self:get_grid():reset() @@ -639,7 +639,7 @@ function app_launcher:page_backward(dir) return end - local pos = self:get_grid():get_widget_position(self._private.active_widget) + local pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) -- Remove the current page apps from the grid self:get_grid():reset() @@ -750,6 +750,10 @@ function app_launcher:get_text() return self._private.text end +function app_launcher:get_selected_app_widget() + return self._private.selected_app_widget +end + local function new(args) args = args or {} From bf7db8a1100f1d6d98560b28460f533cca96d1e6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 03:50:54 +0200 Subject: [PATCH 104/185] Fix search timer --- widget/app_launcher/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9a58025e..4cbd1614 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -819,7 +819,8 @@ local function new(args) ret._private.current_page = 1 ret._private.search_timer = gtimer { timeout = 0.05, - autostart = true, + call_now = false, + autostart = false, single_shot = true, callback = function() ret:search() From 4e9f1d69c4f0951da73e21ca197ba858bbcf7481 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 04:17:06 +0200 Subject: [PATCH 105/185] Use desktop app info search instead --- widget/app_launcher/fzy.lua | 275 ----------------------------------- widget/app_launcher/init.lua | 67 +-------- 2 files changed, 8 insertions(+), 334 deletions(-) delete mode 100644 widget/app_launcher/fzy.lua diff --git a/widget/app_launcher/fzy.lua b/widget/app_launcher/fzy.lua deleted file mode 100644 index d80b543f..00000000 --- a/widget/app_launcher/fzy.lua +++ /dev/null @@ -1,275 +0,0 @@ --- The lua implementation of the fzy string matching algorithm - -local SCORE_GAP_LEADING = -0.005 -local SCORE_GAP_TRAILING = -0.005 -local SCORE_GAP_INNER = -0.01 -local SCORE_MATCH_CONSECUTIVE = 1.0 -local SCORE_MATCH_SLASH = 0.9 -local SCORE_MATCH_WORD = 0.8 -local SCORE_MATCH_CAPITAL = 0.7 -local SCORE_MATCH_DOT = 0.6 -local SCORE_MAX = math.huge -local SCORE_MIN = -math.huge -local MATCH_MAX_LENGTH = 1024 - -local fzy = {} - --- Check if `needle` is a subsequence of the `haystack`. --- --- Usually called before `score` or `positions`. --- --- Args: --- needle (string) --- haystack (string) --- case_sensitive (bool, optional): defaults to false --- --- Returns: --- bool -function fzy.has_match(needle, haystack, case_sensitive) - if not case_sensitive then - needle = string.lower(needle) - haystack = string.lower(haystack) - end - - local j = 1 - for i = 1, string.len(needle) do - j = string.find(haystack, needle:sub(i, i), j, true) - if not j then - return false - else - j = j + 1 - end - end - - return true -end - -local function is_lower(c) - return c:match("%l") -end - -local function is_upper(c) - return c:match("%u") -end - -local function precompute_bonus(haystack) - local match_bonus = {} - - local last_char = "/" - for i = 1, string.len(haystack) do - local this_char = haystack:sub(i, i) - if last_char == "/" or last_char == "\\" then - match_bonus[i] = SCORE_MATCH_SLASH - elseif last_char == "-" or last_char == "_" or last_char == " " then - match_bonus[i] = SCORE_MATCH_WORD - elseif last_char == "." then - match_bonus[i] = SCORE_MATCH_DOT - elseif is_lower(last_char) and is_upper(this_char) then - match_bonus[i] = SCORE_MATCH_CAPITAL - else - match_bonus[i] = 0 - end - - last_char = this_char - end - - return match_bonus -end - -local function compute(needle, haystack, D, M, case_sensitive) - -- Note that the match bonuses must be computed before the arguments are - -- converted to lowercase, since there are bonuses for camelCase. - local match_bonus = precompute_bonus(haystack) - local n = string.len(needle) - local m = string.len(haystack) - - if not case_sensitive then - needle = string.lower(needle) - haystack = string.lower(haystack) - end - - -- Because lua only grants access to chars through substring extraction, - -- get all the characters from the haystack once now, to reuse below. - local haystack_chars = {} - for i = 1, m do - haystack_chars[i] = haystack:sub(i, i) - end - - for i = 1, n do - D[i] = {} - M[i] = {} - - local prev_score = SCORE_MIN - local gap_score = i == n and SCORE_GAP_TRAILING or SCORE_GAP_INNER - local needle_char = needle:sub(i, i) - - for j = 1, m do - if needle_char == haystack_chars[j] then - local score = SCORE_MIN - if i == 1 then - score = ((j - 1) * SCORE_GAP_LEADING) + match_bonus[j] - elseif j > 1 then - local a = M[i - 1][j - 1] + match_bonus[j] - local b = D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE - score = math.max(a, b) - end - D[i][j] = score - prev_score = math.max(score, prev_score + gap_score) - M[i][j] = prev_score - else - D[i][j] = SCORE_MIN - prev_score = prev_score + gap_score - M[i][j] = prev_score - end - end - end -end - --- Compute a matching score. --- --- Args: --- needle (string): must be a subequence of `haystack`, or the result is --- undefined. --- haystack (string) --- case_sensitive (bool, optional): defaults to false --- --- Returns: --- number: higher scores indicate better matches. See also `get_score_min` --- and `get_score_max`. -function fzy.score(needle, haystack, case_sensitive) - local n = string.len(needle) - local m = string.len(haystack) - - if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then - return SCORE_MIN - elseif n == m then - return SCORE_MAX - else - local D = {} - local M = {} - compute(needle, haystack, D, M, case_sensitive) - return M[n][m] - end -end - --- Compute the locations where fzy matches a string. --- --- Determine where each character of the `needle` is matched to the `haystack` --- in the optimal match. --- --- Args: --- needle (string): must be a subequence of `haystack`, or the result is --- undefined. --- haystack (string) --- case_sensitive (bool, optional): defaults to false --- --- Returns: --- {int,...}: indices, where `indices[n]` is the location of the `n`th --- character of `needle` in `haystack`. --- number: the same matching score returned by `score` -function fzy.positions(needle, haystack, case_sensitive) - local n = string.len(needle) - local m = string.len(haystack) - - if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then - return {}, SCORE_MIN - elseif n == m then - local consecutive = {} - for i = 1, n do - consecutive[i] = i - end - return consecutive, SCORE_MAX - end - - local D = {} - local M = {} - compute(needle, haystack, D, M, case_sensitive) - - local positions = {} - local match_required = false - local j = m - for i = n, 1, -1 do - while j >= 1 do - if D[i][j] ~= SCORE_MIN and (match_required or D[i][j] == M[i][j]) then - match_required = (i ~= 1) and (j ~= 1) and ( - M[i][j] == D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE) - positions[i] = j - j = j - 1 - break - else - j = j - 1 - end - end - end - - return positions, M[n][m] -end - --- Apply `has_match` and `positions` to an array of haystacks. --- --- Args: --- needle (string) --- haystack ({string, ...}) --- case_sensitive (bool, optional): defaults to false --- --- Returns: --- {{idx, positions, score}, ...}: an array with one entry per matching line --- in `haystacks`, each entry giving the index of the line in `haystacks` --- as well as the equivalent to the return value of `positions` for that --- line. -function fzy.filter(needle, haystacks, case_sensitive) - local result = {} - - for i, line in ipairs(haystacks) do - if fzy.has_match(needle, line, case_sensitive) then - local p, s = fzy.positions(needle, line, case_sensitive) - table.insert(result, {i, p, s}) - end - end - - return result -end - --- The lowest value returned by `score`. --- --- In two special cases: --- - an empty `needle`, or --- - a `needle` or `haystack` larger than than `get_max_length`, --- the `score` function will return this exact value, which can be used as a --- sentinel. This is the lowest possible score. -function fzy.get_score_min() - return SCORE_MIN -end - --- The score returned for exact matches. This is the highest possible score. -function fzy.get_score_max() - return SCORE_MAX -end - --- The maximum size for which `fzy` will evaluate scores. -function fzy.get_max_length() - return MATCH_MAX_LENGTH -end - --- The minimum score returned for normal matches. --- --- For matches that don't return `get_score_min`, their score will be greater --- than than this value. -function fzy.get_score_floor() - return MATCH_MAX_LENGTH * SCORE_GAP_INNER -end - --- The maximum score for non-exact matches. --- --- For matches that don't return `get_score_max`, their score will be less than --- this value. -function fzy.get_score_ceiling() - return MATCH_MAX_LENGTH * SCORE_MATCH_CONSECUTIVE -end - --- The name of the currently-running implmenetation, "lua" or "native". -function fzy.get_implementation_name() - return "lua" -end - -return fzy \ No newline at end of file diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4cbd1614..01e5b2c6 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -6,7 +6,6 @@ local gtimer = require("gears.timer") local wibox = require("wibox") local beautiful = require("beautiful") local prompt_widget = require(... .. ".prompt") -local fzy = require(... .. ".fzy") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -33,47 +32,6 @@ local function default_value(value, default) end end -local function string_levenshtein(str1, str2) - local len1 = string.len(str1) - local len2 = string.len(str2) - local matrix = {} - local cost = 0 - - -- quick cut-offs to save time - if (len1 == 0) then - return len2 - elseif (len2 == 0) then - return len1 - elseif (str1 == str2) then - return 0 - end - - -- initialise the base matrix values - for i = 0, len1, 1 do - matrix[i] = {} - matrix[i][0] = i - end - for j = 0, len2, 1 do - matrix[0][j] = j - end - - -- actual Levenshtein algorithm - for i = 1, len1, 1 do - for j = 1, len2, 1 do - if (str1:byte(i) == str2:byte(j)) then - cost = 0 - else - cost = 1 - end - - matrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost) - end - end - - -- return the last value - this is the Levenshtein distance - return matrix[len1][len2] -end - local function has_value(tab, val) for _, value in ipairs(tab) do if val:lower():find(value:lower(), 1, true) then @@ -503,24 +461,16 @@ function app_launcher:search() if text == "" then self._private.matched_apps = self._private.all_apps else - for _, app in ipairs(self._private.all_apps) do - text = text:gsub( "%W", "" ) - - -- Filter with fzy - if fzy.has_match(text:lower(), app.name) or (self.search_commands and fzy.has_match(text:lower(), app.exec)) then - table.insert(self._private.matched_apps, app) + local matched_apps = Gio.DesktopAppInfo.search(text:lower()) + for _, matched_app in ipairs(matched_apps) do + for _, app_id in ipairs(matched_app) do + for _, app in ipairs(self._private.all_apps) do + if app.id == app_id then + table.insert(self._private.matched_apps, app) + end + end end end - - -- Sort by string similarity - table.sort(self._private.matched_apps, function(a, b) - if self.search_commands then - return string_levenshtein(text, a.name) + string_levenshtein(text, a.exec) < - string_levenshtein(text, b.name) + string_levenshtein(text, b.exec) - else - return string_levenshtein(text, a.name) < string_levenshtein(text, b.name) - end - end) end for _, app in ipairs(self._private.matched_apps) do -- Only add the widgets for apps that are part of the first page @@ -759,7 +709,6 @@ local function new(args) args.sort_fn = default_value(args.sort_fn, nil) args.favorites = default_value(args.favorites, {}) - args.search_commands = default_value(args.search_commands, true) args.skip_names = default_value(args.skip_names, {}) args.skip_commands = default_value(args.skip_commands, {}) args.skip_empty_icons = default_value(args.skip_empty_icons, false) From b8dc4fa4241c4afae2f1f61644916a8332d57b59 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 04:25:44 +0200 Subject: [PATCH 106/185] Use gears cache for the app widgets --- widget/app_launcher/init.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 01e5b2c6..745457e1 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -3,6 +3,7 @@ local awful = require("awful") local gobject = require("gears.object") local gtable = require("gears.table") local gtimer = require("gears.timer") +local gcache = require("gears.cache") local wibox = require("wibox") local beautiful = require("beautiful") local prompt_widget = require(... .. ".prompt") @@ -73,7 +74,7 @@ local function scroll(self, dir) end end -local function app_widget(self, app) +local app_widget = gcache.new(function(self, app) local widget = nil if self.app_template == nil then @@ -232,7 +233,7 @@ local function app_widget(self, app) function app:is_selected() widget:is_selected() end return widget -end +end) local function generate_apps(self) self._private.all_apps = {} @@ -443,7 +444,7 @@ function app_launcher:refresh() for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) + self:get_grid():add(app_widget:get(self, app)) end end end @@ -475,7 +476,7 @@ function app_launcher:search() for _, app in ipairs(self._private.matched_apps) do -- Only add the widgets for apps that are part of the first page if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget(self, app)) + self:get_grid():add(app_widget:get(self, app)) end end @@ -553,7 +554,7 @@ function app_launcher:page_forward(dir) for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) + self:get_grid():add(app_widget:get(self, app)) end end @@ -601,7 +602,7 @@ function app_launcher:page_backward(dir) for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) + self:get_grid():add(app_widget:get(self, app)) end end @@ -664,7 +665,7 @@ function app_launcher:reset() for index, app in ipairs(self._private.all_apps) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then - self:get_grid():add(app_widget(self, app)) + self:get_grid():add(app_widget:get(self, app)) else break end From 297ea259bb007b4f91ca750832d1057bd5bce177 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 14:32:23 +0200 Subject: [PATCH 107/185] Don't loop twice --- widget/app_launcher/init.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 745457e1..8bf4ee1e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -461,6 +461,11 @@ function app_launcher:search() if text == "" then self._private.matched_apps = self._private.all_apps + for _, matched_app in ipairs(self._private.matched_apps) do + if #self:get_grid().children + 1 <= self._private.max_apps_per_page then + self:get_grid():add(app_widget:get(self, matched_app)) + end + end else local matched_apps = Gio.DesktopAppInfo.search(text:lower()) for _, matched_app in ipairs(matched_apps) do @@ -468,17 +473,15 @@ function app_launcher:search() for _, app in ipairs(self._private.all_apps) do if app.id == app_id then table.insert(self._private.matched_apps, app) + -- Only add the widgets for apps that are part of the first page + if #self:get_grid().children + 1 <= self._private.max_apps_per_page then + self:get_grid():add(app_widget:get(self, app)) + end end end end end end - for _, app in ipairs(self._private.matched_apps) do - -- Only add the widgets for apps that are part of the first page - if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget:get(self, app)) - end - end -- Recalculate the apps per page based on the current matched apps self._private.apps_per_page = math.min(#self._private.matched_apps, self._private.max_apps_per_page) From 6f5b50fb4f0307cc02dd605cf161d8cdac41d5d5 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 14:32:56 +0200 Subject: [PATCH 108/185] Break --- widget/app_launcher/init.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 8bf4ee1e..ab9ebd65 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -464,6 +464,8 @@ function app_launcher:search() for _, matched_app in ipairs(self._private.matched_apps) do if #self:get_grid().children + 1 <= self._private.max_apps_per_page then self:get_grid():add(app_widget:get(self, matched_app)) + else + break end end else From 68e6e7e2e5a84aeae77a1adfc77808c6a0a90b6c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 14:36:22 +0200 Subject: [PATCH 109/185] Has no effect --- widget/app_launcher/init.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index ab9ebd65..a978b0b5 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -439,7 +439,6 @@ function app_launcher:refresh() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page self:get_grid():reset() - collectgarbage("collect") for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) @@ -457,7 +456,6 @@ function app_launcher:search() self._private.matched_apps = {} -- Remove all the grid widgets self:get_grid():reset() - collectgarbage("collect") if text == "" then self._private.matched_apps = self._private.all_apps @@ -554,7 +552,6 @@ function app_launcher:page_forward(dir) -- Remove the current page apps from the grid self:get_grid():reset() - collectgarbage("collect") for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) @@ -599,7 +596,6 @@ function app_launcher:page_backward(dir) -- Remove the current page apps from the grid self:get_grid():reset() - collectgarbage("collect") local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page From b6518e6e17ce2395e0357731987de8946e4c598d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 3 Mar 2023 19:57:42 +0200 Subject: [PATCH 110/185] Handle vertical orientation --- widget/app_launcher/init.lua | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index a978b0b5..0bfa5a33 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -43,29 +43,47 @@ local function has_value(tab, val) end local function scroll(self, dir) - if #self:get_grid().children < 1 then + local grid = self:get_grid() + if #grid.children < 1 then self._private.selected_app_widget = nil return end local next_app_index = nil local if_cant_scroll_func = nil + local grid_orientation = grid:get_orientation() if dir == "up" then - next_app_index = self:get_grid():index(self:get_selected_app_widget()) - 1 + if grid_orientation == "horizontal" then + next_app_index = grid:index(self:get_selected_app_widget()) - 1 + elseif grid_orientation == "vertical" then + next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_cols + end if_cant_scroll_func = function() self:page_backward("up") end elseif dir == "down" then - next_app_index = self:get_grid():index(self:get_selected_app_widget()) + 1 + if grid_orientation == "horizontal" then + next_app_index = grid:index(self:get_selected_app_widget()) + 1 + elseif grid_orientation == "vertical" then + next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_cols + end if_cant_scroll_func = function() self:page_forward("down") end elseif dir == "left" then - next_app_index = self:get_grid():index(self:get_selected_app_widget()) - self:get_grid().forced_num_rows + if grid_orientation == "horizontal" then + next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_rows + elseif grid_orientation == "vertical" then + next_app_index = grid:index(self:get_selected_app_widget()) - 1 + end if_cant_scroll_func = function() self:page_backward("left") end elseif dir == "right" then - next_app_index = self:get_grid():index(self:get_selected_app_widget()) + self:get_grid().forced_num_rows + if grid_orientation == "horizontal" then + next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_rows + elseif grid_orientation == "vertical" then + next_app_index = grid:index(self:get_selected_app_widget()) + 1 + end if_cant_scroll_func = function() self:page_forward("right") end end - local next_app = self:get_grid().children[next_app_index] + local next_app = grid.children[next_app_index] if next_app then next_app:select() self:emit_signal("scroll", dir) From 436b8eb241a297ed582be4233ae50dc619f80899 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 4 Mar 2023 02:01:56 +0200 Subject: [PATCH 111/185] Use the correct methods for scrolling with the mouse wheel depending on the grid orientation --- widget/app_launcher/init.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 0bfa5a33..3abfd91d 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -381,9 +381,17 @@ local function build_widget(self) self:get_grid():connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) if button == 4 then - self:scroll_up() + if self:get_grid():get_orientation() == "horizontal" then + self:scroll_up() + else + self:scroll_left() + end elseif button == 5 then - self:scroll_down() + if self:get_grid():get_orientation() == "horizontal" then + self:scroll_down() + else + self:scroll_right() + end end end) From 92b651b4c7472e9f6efa1b0ba5e2f2326b3cb8f7 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 4 Mar 2023 02:14:48 +0200 Subject: [PATCH 112/185] Make it select the first index at the new page when scrolling with the mouse in an horizontal orientaiton --- widget/app_launcher/init.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3abfd91d..69c0555e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -42,7 +42,7 @@ local function has_value(tab, val) return false end -local function scroll(self, dir) +local function scroll(self, dir, page_dir) local grid = self:get_grid() if #grid.children < 1 then self._private.selected_app_widget = nil @@ -50,7 +50,6 @@ local function scroll(self, dir) end local next_app_index = nil - local if_cant_scroll_func = nil local grid_orientation = grid:get_orientation() if dir == "up" then @@ -59,28 +58,24 @@ local function scroll(self, dir) elseif grid_orientation == "vertical" then next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_cols end - if_cant_scroll_func = function() self:page_backward("up") end elseif dir == "down" then if grid_orientation == "horizontal" then next_app_index = grid:index(self:get_selected_app_widget()) + 1 elseif grid_orientation == "vertical" then next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_cols end - if_cant_scroll_func = function() self:page_forward("down") end elseif dir == "left" then if grid_orientation == "horizontal" then next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_rows elseif grid_orientation == "vertical" then next_app_index = grid:index(self:get_selected_app_widget()) - 1 end - if_cant_scroll_func = function() self:page_backward("left") end elseif dir == "right" then if grid_orientation == "horizontal" then next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_rows elseif grid_orientation == "vertical" then next_app_index = grid:index(self:get_selected_app_widget()) + 1 end - if_cant_scroll_func = function() self:page_forward("right") end end local next_app = grid.children[next_app_index] @@ -88,7 +83,11 @@ local function scroll(self, dir) next_app:select() self:emit_signal("scroll", dir) else - if_cant_scroll_func() + if dir == "up" or dir == "left" then + self:page_forward(page_dir or dir) + elseif dir == "down" or dir == "right" then + self:page_forward(page_dir or dir) + end end end @@ -384,13 +383,13 @@ local function build_widget(self) if self:get_grid():get_orientation() == "horizontal" then self:scroll_up() else - self:scroll_left() + self:scroll_left("up") end elseif button == 5 then if self:get_grid():get_orientation() == "horizontal" then self:scroll_down() else - self:scroll_right() + self:scroll_right("down") end end end) @@ -538,20 +537,20 @@ function app_launcher:search() self:emit_signal("search", self:get_text(), self:get_current_page(), self:get_pages_count()) end -function app_launcher:scroll_up() - scroll(self, "up") +function app_launcher:scroll_up(page_dir) + scroll(self, "up", page_dir) end -function app_launcher:scroll_down() - scroll(self, "down") +function app_launcher:scroll_down(page_dir) + scroll(self, "down", page_dir) end -function app_launcher:scroll_left() - scroll(self, "left") +function app_launcher:scroll_left(page_dir) + scroll(self, "left", page_dir) end -function app_launcher:scroll_right() - scroll(self, "right") +function app_launcher:scroll_right(page_dir) + scroll(self, "right", page_dir) end function app_launcher:page_forward(dir) From 12b94b52a3e4278c8ecc69b0ecf889381d8a2437 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 4 Mar 2023 02:47:14 +0200 Subject: [PATCH 113/185] Typo --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 69c0555e..4f1bb9b7 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -84,7 +84,7 @@ local function scroll(self, dir, page_dir) self:emit_signal("scroll", dir) else if dir == "up" or dir == "left" then - self:page_forward(page_dir or dir) + self:page_backward(page_dir or dir) elseif dir == "down" or dir == "right" then self:page_forward(page_dir or dir) end From 216094d0d58ff861eebbee5934fe8e5829016435 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 6 Mar 2023 00:24:18 +0200 Subject: [PATCH 114/185] gears.cache didn't work this, make the cache manually to stop it from leaking --- widget/app_launcher/init.lua | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4f1bb9b7..256cedb0 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -3,7 +3,6 @@ local awful = require("awful") local gobject = require("gears.object") local gtable = require("gears.table") local gtimer = require("gears.timer") -local gcache = require("gears.cache") local wibox = require("wibox") local beautiful = require("beautiful") local prompt_widget = require(... .. ".prompt") @@ -91,7 +90,11 @@ local function scroll(self, dir, page_dir) end end -local app_widget = gcache.new(function(self, app) +local function app_widget(self, app) + if self._private.apps_widgets_cache[app.name] then + return self._private.apps_widgets_cache[app.name] + end + local widget = nil if self.app_template == nil then @@ -249,8 +252,10 @@ local app_widget = gcache.new(function(self, app) function app:unselect() widget:unselect() end function app:is_selected() widget:is_selected() end + self._private.apps_widgets_cache[app.name] = widget + return widget -end) +end local function generate_apps(self) self._private.all_apps = {} @@ -468,7 +473,7 @@ function app_launcher:refresh() for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget:get(self, app)) + self:get_grid():add(app_widget(self, app)) end end end @@ -486,7 +491,7 @@ function app_launcher:search() self._private.matched_apps = self._private.all_apps for _, matched_app in ipairs(self._private.matched_apps) do if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget:get(self, matched_app)) + self:get_grid():add(app_widget(self, matched_app)) else break end @@ -500,7 +505,7 @@ function app_launcher:search() table.insert(self._private.matched_apps, app) -- Only add the widgets for apps that are part of the first page if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget:get(self, app)) + self:get_grid():add(app_widget(self, app)) end end end @@ -581,7 +586,7 @@ function app_launcher:page_forward(dir) for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget:get(self, app)) + self:get_grid():add(app_widget(self, app)) end end @@ -628,7 +633,7 @@ function app_launcher:page_backward(dir) for index, app in ipairs(self._private.matched_apps) do -- Only add widgets that are between this range (part of the current page) if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget:get(self, app)) + self:get_grid():add(app_widget(self, app)) end end @@ -691,7 +696,7 @@ function app_launcher:reset() for index, app in ipairs(self._private.all_apps) do -- Only add the apps that are part of the first page if index <= self._private.apps_per_page then - self:get_grid():add(app_widget:get(self, app)) + self:get_grid():add(app_widget(self, app)) else break end @@ -793,6 +798,7 @@ local function new(args) ret._private.text = "" ret._private.pages_count = 0 ret._private.current_page = 1 + ret._private.apps_widgets_cache = {} ret._private.search_timer = gtimer { timeout = 0.05, call_now = false, From 2ad3c5dcc9fe8ad07fa7a18257e761acd1a99e9b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 6 Mar 2023 02:06:53 +0200 Subject: [PATCH 115/185] Make the table weak --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 256cedb0..c90f5479 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -798,7 +798,7 @@ local function new(args) ret._private.text = "" ret._private.pages_count = 0 ret._private.current_page = 1 - ret._private.apps_widgets_cache = {} + ret._private.apps_widgets_cache = setmetatable({}, { __mode = "v" }) ret._private.search_timer = gtimer { timeout = 0.05, call_now = false, From 13d0f1c43525fce242e10bc5ca3ac39c331a30df Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 7 Mar 2023 01:05:32 +0200 Subject: [PATCH 116/185] Add an option to lazy load widgets (worse peformance, but less ram on init) --- widget/app_launcher/init.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c90f5479..70fd630d 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -287,7 +287,7 @@ local function generate_apps(self) end end - table.insert(self._private.all_apps, { + local app = { desktop_app_info = desktop_app_info, path = desktop_app_info:get_filename(), id = id, @@ -302,7 +302,11 @@ local function generate_apps(self) launch = function() app:launch() end - }) + } + table.insert(self._private.all_apps, app) + if self.lazy_load_widgets == false then + self._private.apps_widgets_cache[app.name] = app_widget(self, app) + end end end end @@ -754,6 +758,7 @@ local function new(args) args.reset_on_hide = default_value(args.reset_on_hide, true) args.wrap_page_scrolling = default_value(args.wrap_page_scrolling, true) args.wrap_app_scrolling = default_value(args.wrap_app_scrolling, true) + args.lazy_load_widgets = default_value(args.lazy_load_widgets, false) args.type = default_value(args.type, "dock") args.show_on_focused_screen = default_value(args.show_on_focused_screen, true) From 4cdc4fb5f9a4cbb0528fc99ce8b156a6b2665a45 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 7 Mar 2023 13:12:21 +0200 Subject: [PATCH 117/185] Replace the prompt with a proper text input widget --- widget/app_launcher/init.lua | 82 ++- widget/app_launcher/prompt.lua | 480 ------------------ widget/app_launcher/text_input.lua | 769 +++++++++++++++++++++++++++++ 3 files changed, 801 insertions(+), 530 deletions(-) delete mode 100644 widget/app_launcher/prompt.lua create mode 100644 widget/app_launcher/text_input.lua diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 70fd630d..9df96438 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -5,7 +5,7 @@ local gtable = require("gears.table") local gtimer = require("gears.timer") local wibox = require("wibox") local beautiful = require("beautiful") -local prompt_widget = require(... .. ".prompt") +local text_input_widget = require(... .. ".text_input") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -318,22 +318,24 @@ end local function build_widget(self) local widget = self.widget_template if widget == nil then - self._private.prompt = wibox.widget + self._private.text_input = wibox.widget { - widget = prompt_widget, - always_on = true, + widget = text_input_widget, reset_on_stop = self.reset_on_hide, - icon_font = self.prompt_icon_font, - icon_size = self.prompt_icon_size, - icon_color = self.prompt_icon_color, - icon = self.prompt_icon, - label_font = self.prompt_label_font, - label_size = self.prompt_label_size, - label_color = self.prompt_label_color, - label = self.prompt_label, - text_font = self.prompt_text_font, - text_size = self.prompt_text_size, - text_color = self.prompt_text_color, + placeholder = self.text_input_placeholder, + widget_template = wibox.widget { + widget = wibox.container.background, + forced_height = dpi(120), + bg = self.text_input_bg_color, + { + widget = wibox.container.margin, + margins = dpi(30), + { + widget = wibox.widget.textbox, + id = "text_role" + } + } + } } self._private.grid = wibox.widget { @@ -347,21 +349,7 @@ local function build_widget(self) widget = wibox.widget { layout = wibox.layout.fixed.vertical, - { - widget = wibox.container.background, - forced_height = dpi(120), - bg = self.prompt_bg_color, - { - widget = wibox.container.margin, - margins = dpi(30), - { - widget = wibox.container.place, - halign = "left", - valign = "center", - self._private.prompt - } - } - }, + self._private.text_input, { widget = wibox.container.margin, margins = dpi(30), @@ -369,7 +357,7 @@ local function build_widget(self) } } else - self._private.prompt = widget:get_children_by_id("prompt_role")[1] + self._private.text_input = widget:get_children_by_id("text_input_role")[1] self._private.grid = widget:get_children_by_id("grid_role")[1] end @@ -403,7 +391,7 @@ local function build_widget(self) end end) - self:get_prompt():connect_signal("text::changed", function(_, text) + self:get_text_input():connect_signal("property::text", function(_, text) if text == self:get_text() then return end @@ -412,10 +400,14 @@ local function build_widget(self) self._private.search_timer:again() end) - self:get_prompt():connect_signal("key::release", function(_, mod, key, cmd) + + self:get_text_input():connect_signal("key::press", function(_, mod, key, cmd) if key == "Escape" then self:hide() end + end) + + self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) if key == "Return" then if self:get_selected_app_widget() ~= nil then self:get_selected_app_widget():run() @@ -664,7 +656,7 @@ function app_launcher:show() end self:get_widget().visible = true - self:get_prompt():start() + self:get_text_input():focus() self:emit_signal("visibility", true) end @@ -678,7 +670,7 @@ function app_launcher:hide() end self:get_widget().visible = false - self:get_prompt():stop() + self:get_text_input():unfocus() self:emit_signal("visibility", false) end @@ -709,15 +701,15 @@ function app_launcher:reset() local app = self:get_grid():get_widgets_at(1, 1)[1] app:select() - self:get_prompt():set_text("") + self:get_text_input():set_text("") end function app_launcher:get_widget() return self._private.widget end -function app_launcher:get_prompt() - return self._private.prompt +function app_launcher:get_text_input() + return self._private.text_input end function app_launcher:get_grid() @@ -777,18 +769,8 @@ local function new(args) args.apps_per_row = default_value(args.apps_per_row, 5) args.apps_per_column = default_value(args.apps_per_column, 3) - args.prompt_bg_color = default_value(args.prompt_bg_color, "#000000") - args.prompt_icon_font = default_value(args.prompt_icon_font, beautiful.font) - args.prompt_icon_size = default_value(args.prompt_icon_size, 12) - args.prompt_icon_color = default_value(args.prompt_icon_color, "#FFFFFF") - args.prompt_icon = default_value(args.prompt_icon, "") - args.prompt_label_font = default_value(args.prompt_label_font, beautiful.font) - args.prompt_label_size = default_value(args.prompt_label_size, 12) - args.prompt_label_color = default_value(args.prompt_label_color, "#FFFFFF") - args.prompt_label = default_value(args.prompt_label, "Search: ") - args.prompt_text_font = default_value(args.prompt_text_font, beautiful.font) - args.prompt_text_size = default_value(args.prompt_text_size, 12) - args.prompt_text_color = default_value(args.prompt_text_color, "#FFFFFF") + args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") + args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") args.app_normal_color = default_value(args.app_normal_color, "#000000") args.app_selected_color = default_value(args.app_selected_color, "#FFFFFF") diff --git a/widget/app_launcher/prompt.lua b/widget/app_launcher/prompt.lua deleted file mode 100644 index 15ac3133..00000000 --- a/widget/app_launcher/prompt.lua +++ /dev/null @@ -1,480 +0,0 @@ -------------------------------------------- --- @author https://github.com/Kasper24 --- @copyright 2021-2022 Kasper24 -------------------------------------------- -local lgi = require('lgi') -local Gtk = lgi.require('Gtk', '3.0') -local Gdk = lgi.require('Gdk', '3.0') -local awful = require("awful") -local gtable = require("gears.table") -local gstring = require("gears.string") -local wibox = require("wibox") -local beautiful = require("beautiful") -local dpi = beautiful.xresources.apply_dpi -local tostring = tostring -local tonumber = tonumber -local ceil = math.ceil -local ipairs = ipairs -local string = string -local type = type -local capi = { - awesome = awesome, - root = root, - mouse = mouse, - tag = tag, - client = client -} - -local prompt = { - mt = {} -} - -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 - return false - else - return true - end -end - -local function cword_start(s, pos) - local i = pos - if i > 1 then - i = i - 1 - end - while i >= 1 and not is_word_char(s:sub(i, i)) do - i = i - 1 - end - while i >= 1 and is_word_char(s:sub(i, i)) do - i = i - 1 - end - if i <= #s then - i = i + 1 - end - return i -end - -local function cword_end(s, pos) - local i = 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 - i = i + 1 - end - return i -end - -local function have_multibyte_char_at(text, position) - return text:sub(position, position):wlen() == -1 -end - -local function generate_markup(self) - local wp = self._private - - 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 - if type(wp.icon) == "table" then - local icon_size = dpi(ceil(wp.icon.size * 1024)) - markup = string.format( - '%s ', - wp.icon.font, icon_size, wp.icon.color, wp.icon.icon) - else - local icon_size = dpi(ceil(wp.icon_size * 1024)) - markup = string.format( - '%s ', - wp.icon_font, icon_size, wp.icon_color, wp.icon) - end - end - - if self._private.state == 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 - - 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 - - self:set_markup(markup) -end - -local function paste(self) - local wp = self._private - - wp.clipboard:request_text(function(clipboard, text) - if text then - 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) - end - end) -end - -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) - 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) -end - -function prompt:get_text() - return self._private.text -end - -function prompt:start() - local wp = self._private - wp.state = true - - capi.awesome.emit_signal("prompt::toggled_on", self) - generate_markup(self) - - 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 - - if event ~= "press" then - self:emit_signal("key::release", mod, key, wp.text) - return - end - - self:emit_signal("key::press", mod, key, wp.text) - - -- Control cases - if mod.Control then - if key == "v" then - paste(self) - elseif key == "a" then - wp.cur_pos = 1 - elseif key == "b" then - 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 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 - wp.cur_pos = #wp.text + 1 - elseif key == "f" then - 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 - wp.cur_pos = wp.cur_pos + 1 - end - end - elseif key == "h" then - if wp.cur_pos > 1 then - local offset = 0 - if have_multibyte_char_at(wp.text, wp.cur_pos - 1) then - offset = 1 - end - 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 - wp.text = wp.text:sub(1, wp.cur_pos - 1) - elseif key == "u" then - 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 < 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 = wp.cur_pos - 1 - break - end - wstart = wend + 1 - 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 - wp.cur_pos = cword_start(wp.text, wp.cur_pos) - elseif key == "f" then - wp.cur_pos = cword_end(wp.text, wp.cur_pos) - elseif key == "d" then - 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(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 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 - wp.cur_pos = 1 - elseif key == "End" then - wp.cur_pos = #wp.text + 1 - elseif key == "BackSpace" then - if wp.cur_pos > 1 then - local offset = 0 - if have_multibyte_char_at(wp.text, wp.cur_pos - 1) then - offset = 1 - end - 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 - wp.text = wp.text:sub(1, wp.cur_pos - 1) .. wp.text:sub(wp.cur_pos + 1) - elseif key == "Left" then - wp.cur_pos = wp.cur_pos - 1 - elseif key == "Right" then - 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 - 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 wp.cur_pos < 1 then - wp.cur_pos = 1 - elseif wp.cur_pos > #wp.text + 1 then - wp.cur_pos = #wp.text + 1 - end - end - - if wp.only_numbers and wp.text == "" then - wp.text = "0" - wp.cur_pos = #wp.text + 1 - end - - generate_markup(self) - self:emit_signal("text::changed", wp.text) - end) -end - -function prompt:stop() - local wp = self._private - if wp.state == false then - return - end - - wp.state = false - - if self.reset_on_stop == true then - self:set_text("") - end - - if wp.grabber then - awful.keygrabber.stop(wp.grabber) - end - generate_markup(self) - - self:emit_signal("stopped", wp.text) -end - -function prompt:toggle() - local wp = self._private - - if wp.state == true then - self:stop() - else - self:start() - end -end - -local function new() - local widget = wibox.widget.textbox() - gtable.crush(widget, prompt, true) - - 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 = false - wp.stop_on_tag_changed = false - 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 - wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) - - 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 - -build_properties(prompt, properties) - -return setmetatable(prompt, prompt.mt) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua new file mode 100644 index 00000000..09748377 --- /dev/null +++ b/widget/app_launcher/text_input.lua @@ -0,0 +1,769 @@ +------------------------------------------- +-- @author https://github.com/Kasper24 +-- @copyright 2021-2022 Kasper24 +------------------------------------------- +local lgi = require('lgi') +local Gtk = lgi.require('Gtk', '3.0') +local Gdk = lgi.require('Gdk', '3.0') +local Pango = lgi.Pango +local awful = require("awful") +local gtable = require("gears.table") +local gtimer = require("gears.timer") +local gcolor = require("gears.color") +local wibox = require("wibox") +local beautiful = require("beautiful") +local tonumber = tonumber +local ipairs = ipairs +local string = string +local capi = { + awesome = awesome, + root = root, + tag = tag, + client = client, + mouse = mouse, + mousegrabber = mousegrabber +} + +local text_input = { + mt = {} +} + +local properties = { + "unfocus_keys", "unfocus_on_clicked_inside", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", + "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", + "reset_on_unfocus", + "placeholder", "text", "only_numbers", "round", "obscure", + "cursor_blink", "cursor_blink_rate","cursor_size", "cursor_bg", + "selection_bg" +} + +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) + end + return self + end + end + if not prototype["get_" .. prop] then + prototype["get_" .. prop] = function(self) + return self._private[prop] + end + end + end +end + +local function has_value(tab, val) + for _, value in ipairs(tab) do + if val:lower():find(value:lower(), 1, true) then + return true + end + end + return false +end + +local function is_word_char(c) + if string.find(c, "[{[(,.:;_-+=@/ ]") then + return false + else + return true + end +end + +local function cword_start(s, pos) + local i = pos + if i > 1 then + i = i - 1 + end + while i >= 1 and not is_word_char(s:sub(i, i)) do + i = i - 1 + end + while i >= 1 and is_word_char(s:sub(i, i)) do + i = i - 1 + end + if i <= #s then + i = i + 1 + end + return i +end + +local function cword_end(s, pos) + local i = 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 + i = i + 1 + end + return i +end + +local function run_mousegrabber(self) + capi.mousegrabber.run(function(m) + if m.buttons[1] then + if capi.mouse.current_widget ~= self and self.unfocus_on_clicked_outside then + self:unfocus() + return false + elseif capi.mouse.current_widget == self and self.unfocus_on_clicked_inside then + self:unfocus() + return false + end + end + return true + end, "xterm") +end + +local function run_keygrabber(self) + local wp = self._private + wp.keygrabber = awful.keygrabber.run(function(modifiers, key, event) + if event ~= "press" then + self:emit_signal("key::release", modifiers, key, event) + return + end + self:emit_signal("key::press", modifiers, key, event) + + -- Convert index array to hash table + local mod = {} + for _, v in ipairs(modifiers) do + mod[v] = true + end + + if mod.Control then + if key == "a" then + self:select_all() + elseif key == "c" then + self:copy() + elseif key == "v" then + self:paste() + elseif key == "b" or key == "Left" then + self:set_cursor_index_to_word_start() + elseif key == "f" or key == "Right" then + self:set_cursor_index_to_word_end() + elseif key == "d" then + self:delete_next_word() + elseif key == "BackSpace" then + self:delete_previous_word() + end + elseif mod.Shift then + if key =="Left" then + self:decremeant_selection_end_index() + elseif key == "Right" then + self:increamant_selection_end_index() + end + else + if has_value(wp.unfocus_keys, key) then + self:unfocus() + end + + if mod.Shift and key == "Insert" then + self:paste() + elseif key == "Home" then + self:set_cursor_index(0) + elseif key == "End" then + self:set_cursor_index_to_end() + elseif key == "BackSpace" then + self:delete_text() + elseif key == "Delete" then + self:delete_text_after_cursor() + elseif key == "Left" then + self:decremeant_cursor_index() + elseif key == "Right" then + self:increamant_cursor_index() + else + if (wp.round and key == ".") or (wp.only_numbers and tonumber(self:get_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 + self:update_text(key) + end + end + end + end) +end + +function text_input:set_widget_template(widget_template) + local wp = self._private + + self._private.text_widget = widget_template:get_children_by_id("text_role")[1] + self._private.text_widget.forced_width = math.huge + local text_draw = self._private.text_widget.draw + + local placeholder_widget = widget_template:get_children_by_id("placeholder_role") + if placeholder_widget then + placeholder_widget = placeholder_widget[1] + end + + function self._private.text_widget:draw(context, cr, width, height) + -- Selection bg + local ink_rect, logical_rect = self._private.layout:get_pixel_extents() + cr:set_source(gcolor.change_opacity(wp.selection_bg, wp.selection_opacity)) + cr:rectangle( + wp.selection_start_x, + logical_rect.y - 3, + wp.selection_end_x - wp.selection_start_x, + logical_rect.y + logical_rect.height + 6 + ) + cr:fill() + + -- Cursor + local ink_rect, logical_rect = self._private.layout:get_pixel_extents() + cr:set_source(gcolor.change_opacity(wp.cursor_bg, wp.cursor_opacity)) + cr:set_line_width(wp.cursor_width) + cr:move_to(wp.cursor_x, logical_rect.y - 3) + cr:line_to(wp.cursor_x, logical_rect.y + logical_rect.height + 6) + cr:stroke() + + cr:set_source_rgb(1, 1, 1) + + text_draw(self, context, cr, width, height) + + if self:get_text() == "" and placeholder_widget then + placeholder_widget.visible = true + elseif placeholder_widget then + placeholder_widget.visible = false + end + end + + wp.selecting_text = false + + local function on_drag(drawable, lx, ly) + if not wp.selecting_text and (lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly) then + self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) + self:set_selection_end_index(self._private.selection_start) + wp.selecting_text = true + elseif wp.selecting_text then + self:set_selection_end_index_from_x_y(lx - wp.offset.x, ly - wp.offset.y) + end + end + + self._private.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + if button == 1 then + self:focus() + wp.press_pos = { lx = lx, ly = ly } + wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } + find_widgets_result.drawable:connect_signal("mouse::move", on_drag) + end + end) + + self._private.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + if not wp.selecting_text then + self:set_cursor_index_from_x_y(lx, ly) + else + wp.selecting_text = false + end + end) + + self._private.text_widget:connect_signal("mouse::enter", function() + capi.root.cursor("xterm") + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = "xterm" + end + end) + + self._private.text_widget:connect_signal("mouse::leave", function(_, find_widgets_result) + if self:get_focused() == false then + capi.root.cursor("left_ptr") + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = "left_ptr" + end + end + + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + if wp.unfocus_on_mouse_leave then + self:unfocus() + end + end) + + self:set_widget(widget_template) +end + +function text_input:get_mode() + return self._private.mode +end + +function text_input:set_focused(focused) + if focused == true then + self:focus() + else + self:unfocus() + end +end + +function text_input:toggle_obscure() + self:set_obscure(not self._private.obscure) +end + +function text_input:update_text(text) + if self:get_mode() == "insert" then + self:insert_text(text) + else + self:overwrite_text(text) + end +end + +function text_input:set_text(text) + local wp = self._private + local text_widget = self:get_text_widget() + + text_widget:set_text(text) + if text_widget:get_text() == "" then + self:set_cursor_index(0) + else + self:set_cursor_index(#text) + end + + self:emit_signal("property::text", text_widget:get_text()) +end + +function text_input:insert_text(text) + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + local left_text = old_text:sub(1, cursor_index) .. text + local right_text = old_text:sub(cursor_index + 1) + self:get_text_widget():set_text(left_text .. right_text) + self:set_cursor_index(self:get_cursor_index() + #text) + + self:emit_signal("property::text", self:get_text()) +end + +function text_input:overwrite_text(text) + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + + local old_text = self:get_text() + local left_text = old_text:sub(1, start_pos) + local right_text = old_text:sub(end_pos + 1) + self:get_text_widget():set_text(left_text .. text .. right_text) + self:set_cursor_index(#left_text) + + self:emit_signal("property::text", self:get_text()) +end + +function text_input:copy() + local wp = self._private + if self:get_mode() == "overwrite" then + local text = self:get_text() + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos + 1, start_pos + end + text = text:sub(start_pos, end_pos) + wp.clipboard:set_text(text, -1) + end +end + +function text_input:paste() + local wp = self._private + + wp.clipboard:request_text(function(clipboard, text) + if text then + self:update_text(text) + end + end) +end + +function text_input:delete_next_word() + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + + local left_text = old_text:sub(1, cursor_index) + local right_text = old_text:sub(cword_end(old_text, cursor_index + 1)) + self:get_text_widget():set_text(left_text .. right_text) + self:emit_signal("property::text", self:get_text()) +end + +function text_input:delete_previous_word() + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + local wstart = cword_start(old_text, cursor_index + 1) - 1 + local left_text = old_text:sub(1, wstart) + local right_text = old_text:sub(cursor_index + 1) + self:get_text_widget():set_text(left_text .. right_text) + self:set_cursor_index(wstart) + self:emit_signal("property::text", self:get_text()) +end + +function text_input:delete_text() + if self:get_mode() == "insert" then + self:delete_text_before_cursor() + else + self:overwrite_text("") + end +end + +function text_input:delete_text_before_cursor() + local cursor_index = self:get_cursor_index() + if cursor_index > 0 then + local old_text = self:get_text() + local left_text = old_text:sub(1, cursor_index - 1) + local right_text = old_text:sub(cursor_index + 1) + self:get_text_widget():set_text(left_text .. right_text) + self:set_cursor_index(cursor_index - 1) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:delete_text_after_cursor() + local cursor_index = self:get_cursor_index() + if cursor_index < #self:get_text() then + local old_text = self:get_text() + local left_text = old_text:sub(1, cursor_index) + local right_text = old_text:sub(cursor_index + 2) + self:get_text_widget():set_text(left_text .. right_text) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:get_text() + return self:get_text_widget():get_text() +end + +function text_input:get_text_widget() + return self._private.text_widget +end + +function text_input:show_selection() + self._private.selection_opacity = 1 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:hide_selection() + self._private.selection_opacity = 0 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:select_all() + self:set_selection_start_index(0) + self:set_selection_end_index(#self:get_text()) +end + +function text_input:set_selection_start_index(index) + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_caret_pos(index) + if strong_pos then + self._private.selection_start = index + self._private.mode = "overwrite" + + self._private.selection_start_x = strong_pos.x / Pango.SCALE + self._private.selection_start_y = strong_pos.y / Pango.SCALE + + self:show_selection() + self:hide_cursor() + + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:set_selection_end_index(index) + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_caret_pos(index) + if strong_pos then + self._private.selection_end_x = strong_pos.x / Pango.SCALE + self._private.selection_end_y = strong_pos.y / Pango.SCALE + self._private.selection_end = index + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:increamant_selection_end_index() + if self:get_mode() == "insert" then + self:set_selection_start_index(self:get_cursor_index()) + self:set_selection_end_index(self:get_cursor_index() + 1) + else + self:set_selection_end_index(self._private.selection_end + 1) + end +end + +function text_input:decremeant_selection_end_index() + if self:get_mode() == "insert" then + self:set_selection_start_index(self:get_cursor_index()) + self:set_selection_end_index(self:get_cursor_index() - 1) + else + self:set_selection_end_index(self._private.selection_end - 1) + end +end + +function text_input:set_selection_start_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + if index then + self:set_selection_start_index(index) + else + self:set_selection_start_index(#self:get_text()) + end +end + +function text_input:set_selection_end_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + if index then + self:set_selection_end_index(index + trailing) + end +end + +function text_input:show_cursor() + self._private.cursor_opacity = 1 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:hide_cursor() + self._private.cursor_opacity = 0 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:set_cursor_index(index) + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_cursor_pos(index) + if strong_pos then + self._private.cursor_index = index + self._private.mode = "insert" + + self._private.cursor_x = strong_pos.x / Pango.SCALE + self._private.cursor_y = strong_pos.y / Pango.SCALE + + if self:get_focused() then + self:show_cursor() + end + self:hide_selection() + + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:set_cursor_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + + if index then + self:set_cursor_index(index) + else + self:set_cursor_index(#self:get_text()) + end +end + +function text_input:set_cursor_index_to_word_start() + self:set_cursor_index(cword_start(self:get_text(), self:get_cursor_index() + 1) - 1) +end + +function text_input:set_cursor_index_to_word_end() + self:set_cursor_index(cword_end(self:get_text(), self:get_cursor_index() + 1) - 1) +end + +function text_input:set_cursor_index_to_end() + self:set_cursor_index(#self:get_text()) +end + +function text_input:increamant_cursor_index() + if self:get_mode() == "insert" then + self:set_cursor_index(self:get_cursor_index() + 1) + else + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + self:set_cursor_index(end_pos) + end +end + +function text_input:decremeant_cursor_index() + if self:get_mode() == "insert" then + self:set_cursor_index(self:get_cursor_index() - 1) + else + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + self:set_cursor_index(start_pos) + end +end + +function text_input:get_cursor_index() + return self._private.cursor_index +end + +function text_input:set_focus_on_subject_mouse_enter(subject) + subject:connect_signal("mouse::enter", function() + self:focus() + end) +end + +function text_input:set_unfocus_on_subject_mouse_leave(subject) + subject:connect_signal("mouse::leave", function() + self:unfocus() + end) +end + +function text_input:get_focused() + return self._private.focused +end + +function text_input:focus() + local wp = self._private + if self:get_focused() == true then + return + end + + self:show_cursor() + run_keygrabber(self) + if wp.unfocus_on_clicked_outside or wp.unfocus_on_clicked_inside then + run_mousegrabber(self) + end + + if wp.cursor_blink then + gtimer.start_new(wp.cursor_blink_rate, function() + if self:get_focused() == true then + if self._private.cursor_opacity == 1 then + self:hide_cursor() + elseif self:get_mode() == "insert" then + self:show_cursor() + end + return true + end + return false + end) + end + + wp.focused = true + self:emit_signal("focus") + capi.awesome.emit_signal("text_input::focus", self) +end + +function text_input:unfocus() + local wp = self._private + if self:get_focused() == false then + return + end + + self:hide_cursor() + self:hide_selection() + if self.reset_on_unfocus == true then + self:set_text("") + end + awful.keygrabber.stop(wp.keygrabber) + if wp.unfocus_on_clicked_outside then + capi.mousegrabber.stop() + end + capi.root.cursor("left_ptr") + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = "left_ptr" + end + + wp.focused = false + self:emit_signal("unfocus") + capi.awesome.emit_signal("text_input::unfocus", self) +end + +function text_input:toggle() + local wp = self._private + + if self:get_focused() == false then + self:focus() + else + self:unfocus() + end +end + +local function new() + local widget = wibox.container.background() + gtable.crush(widget, text_input, true) + + local wp = widget._private + + wp.focused = false + wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + wp.cursor_index = 0 + wp.mode = "insert" + + wp.cursor_x = 0 + wp.cursor_y = 0 + wp.cursor_opacity = 0 + wp.selection_start_x = 0 + wp.selection_end_x = 0 + wp.selection_start_y = 0 + wp.selection_end_y = 0 + wp.selection_opacity = 0 + + wp.unfocus_keys = { "Escape", "Return" } + wp.unfocus_on_clicked_inside = false + wp.unfocus_on_clicked_outside = true + wp.unfocus_on_mouse_leave = false + wp.unfocus_on_tag_change = true + wp.unfocus_on_other_text_input_focus = true + + wp.focus_on_subject_mouse_enter = nil + wp.unfocus_on_subject_mouse_leave = nil + + wp.reset_on_unfocus = false + + wp.placeholder = "" + wp.text = "" + wp.only_numbers = false + wp.round = false + wp.obscure = false + + wp.cursor_width = 2 + wp.cursor_bg = beautiful.fg_normal + wp.cursor_blink = true + wp.cursor_blink_rate = 0.6 + + wp.selection_bg = beautiful.bg_normal + + widget:set_widget_template(wibox.widget { + layout = wibox.layout.stack, + { + widget = wibox.widget.textbox, + id = "placeholder_role", + text = wp.placeholder + }, + { + widget = wibox.widget.textbox, + id = "text_role", + text = wp.text + } + }) + + capi.tag.connect_signal("property::selected", function() + if wp.unfocus_on_tag_change then + widget:unfocus() + end + end) + + capi.awesome.connect_signal("text_input::focus", function(text_input) + if wp.unfocus_on_other_text_input_focus == false and text_input ~= self then + widget:unfocus() + end + end) + + return widget +end + +function text_input.mt:__call(...) + return new(...) +end + +build_properties(text_input, properties) + +return setmetatable(text_input, text_input.mt) From 4873f1e9b61dc61c537ec41c8d6e46d01649422a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 7 Mar 2023 13:20:28 +0200 Subject: [PATCH 118/185] Use simpler commands --- widget/app_launcher/init.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 9df96438..3d1e0bc9 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -17,12 +17,8 @@ local helpers = require(tostring(path):match(".*bling") .. ".helpers") local app_launcher = { mt = {} } -local KILL_OLD_INOTIFY_SCRIPT = [[ ps x | grep "inotifywait -e modify /usr/share/applications" | grep -v grep | awk '{print $1}' | xargs kill ]] -local INOTIFY_SCRIPT = [[ bash -c "while (inotifywait -e modify /usr/share/applications -qq) do echo; done" ]] -local AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. - "awesome-sensible-terminal" -local RUN_AS_ROOT_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. - "run-as-root.sh" +local AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "awesome-sensible-terminal" +local RUN_AS_ROOT_SCRIPT_PATH = debug.getinfo(1).source:match("@?(.*/)") .. "run-as-root.sh" local function default_value(value, default) if value == nil then @@ -823,8 +819,8 @@ local function new(args) ) end - awful.spawn.easy_async_with_shell(KILL_OLD_INOTIFY_SCRIPT, function() - awful.spawn.with_line_callback(INOTIFY_SCRIPT, {stdout = function() + awful.spawn.easy_async_with_shell("pkill -f 'inotifywait -m /usr/share/applications -e modify'", function() + awful.spawn.with_line_callback("inotifywait -m /usr/share/applications -e modify", {stdout = function() generate_apps(ret) end}) end) From f028caee477742bb94e5b428889af5167fe54eca Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 10 Mar 2023 04:53:17 +0200 Subject: [PATCH 119/185] Fix some text input issues --- widget/app_launcher/text_input.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 09748377..a364d7a8 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -29,7 +29,7 @@ local text_input = { } local properties = { - "unfocus_keys", "unfocus_on_clicked_inside", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", + "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "reset_on_unfocus", "placeholder", "text", "only_numbers", "round", "obscure", @@ -108,9 +108,6 @@ local function run_mousegrabber(self) if capi.mouse.current_widget ~= self and self.unfocus_on_clicked_outside then self:unfocus() return false - elseif capi.mouse.current_widget == self and self.unfocus_on_clicked_inside then - self:unfocus() - return false end end return true @@ -195,6 +192,8 @@ function text_input:set_widget_template(widget_template) self._private.text_widget.forced_width = math.huge local text_draw = self._private.text_widget.draw + self._private.text_widget:set_text(self:get_text()) + local placeholder_widget = widget_template:get_children_by_id("placeholder_role") if placeholder_widget then placeholder_widget = placeholder_widget[1] @@ -220,8 +219,7 @@ function text_input:set_widget_template(widget_template) cr:line_to(wp.cursor_x, logical_rect.y + logical_rect.height + 6) cr:stroke() - cr:set_source_rgb(1, 1, 1) - + cr:set_source(gcolor(wp.text_color)) text_draw(self, context, cr, width, height) if self:get_text() == "" and placeholder_widget then @@ -245,7 +243,6 @@ function text_input:set_widget_template(widget_template) self._private.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) if button == 1 then - self:focus() wp.press_pos = { lx = lx, ly = ly } wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } find_widgets_result.drawable:connect_signal("mouse::move", on_drag) @@ -259,6 +256,7 @@ function text_input:set_widget_template(widget_template) else wp.selecting_text = false end + self:focus() end) self._private.text_widget:connect_signal("mouse::enter", function() @@ -276,6 +274,8 @@ function text_input:set_widget_template(widget_template) if wibox then wibox.cursor = "left_ptr" end + elseif wp.unfocus_on_clicked_outside then + run_mousegrabber(self) end find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) @@ -312,7 +312,6 @@ function text_input:update_text(text) end function text_input:set_text(text) - local wp = self._private local text_widget = self:get_text_widget() text_widget:set_text(text) @@ -625,12 +624,12 @@ function text_input:focus() return end - self:show_cursor() - run_keygrabber(self) - if wp.unfocus_on_clicked_outside or wp.unfocus_on_clicked_inside then - run_mousegrabber(self) + if self:get_mode() == "insert" then + self:show_cursor() end + run_keygrabber(self) + if wp.cursor_blink then gtimer.start_new(wp.cursor_blink_rate, function() if self:get_focused() == true then @@ -707,7 +706,6 @@ local function new() wp.selection_opacity = 0 wp.unfocus_keys = { "Escape", "Return" } - wp.unfocus_on_clicked_inside = false wp.unfocus_on_clicked_outside = true wp.unfocus_on_mouse_leave = false wp.unfocus_on_tag_change = true @@ -718,12 +716,14 @@ local function new() wp.reset_on_unfocus = false - wp.placeholder = "" - wp.text = "" wp.only_numbers = false wp.round = false wp.obscure = false + wp.placeholder = "" + wp.text_color = beautiful.colors.fg_normal + wp.text = "" + wp.cursor_width = 2 wp.cursor_bg = beautiful.fg_normal wp.cursor_blink = true From 881895e2004a7c333034ce60340a4b2fa529024c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 10 Mar 2023 05:27:00 +0200 Subject: [PATCH 120/185] Text filtering the right way --- widget/app_launcher/text_input.lua | 73 ++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index a364d7a8..b32403e4 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -12,7 +12,6 @@ local gtimer = require("gears.timer") local gcolor = require("gears.color") local wibox = require("wibox") local beautiful = require("beautiful") -local tonumber = tonumber local ipairs = ipairs local string = string local capi = { @@ -32,11 +31,25 @@ local properties = { "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "reset_on_unfocus", - "placeholder", "text", "only_numbers", "round", "obscure", + "placeholder", "text", "pattern", "obscure", "cursor_blink", "cursor_blink_rate","cursor_size", "cursor_bg", "selection_bg" } +text_input.patterns = { + numbers = "[%d.]*", + numbers_one_decimal = "%d*%.?%d*", + round_numbers = "[0-9]*", + email = "%S+@%S+%.%S+", + time = "%d%d?:%d%d:%d%d?|%d%d?:%d%d", + date = "%d%d%d%d%-%d%d%-%d%d|%d%d?/%d%d?/%d%d%d%d|%d%d?%.%d%d?%.%d%d%d%d", + phone = "%+?%d[%d%-%s]+%d", + url = "https?://[%w-_%.]+%.[%w]+/?[%w-_%.?=%+]*", + email = "[%w._%-%+]+@[%w._%-]+%.%w+", + alphanumeric = "%w+", + letters = "[a-zA-Z]+" +} + local function build_properties(prototype, prop_names) for _, prop in ipairs(prop_names) do if not prototype["set_" .. prop] then @@ -171,12 +184,6 @@ local function run_keygrabber(self) elseif key == "Right" then self:increamant_cursor_index() else - if (wp.round and key == ".") or (wp.only_numbers and tonumber(self:get_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 self:update_text(key) end @@ -299,6 +306,10 @@ function text_input:set_focused(focused) end end +function text_input:set_pattern(pattern) + self._private.pattern = text_input.patterns[pattern] +end + function text_input:toggle_obscure() self:set_obscure(not self._private.obscure) end @@ -325,19 +336,32 @@ function text_input:set_text(text) end function text_input:insert_text(text) + local wp = self._private + local old_text = self:get_text() local cursor_index = self:get_cursor_index() local left_text = old_text:sub(1, cursor_index) .. text local right_text = old_text:sub(cursor_index + 1) - self:get_text_widget():set_text(left_text .. right_text) - self:set_cursor_index(self:get_cursor_index() + #text) - - self:emit_signal("property::text", self:get_text()) + local new_text = left_text .. right_text + if wp.pattern then + new_text = new_text:match(wp.pattern) + if new_text then + self:get_text_widget():set_text(new_text) + self:set_cursor_index(self:get_cursor_index() + #text) + self:emit_signal("property::text", self:get_text()) + end + else + self:get_text_widget():set_text(new_text) + self:set_cursor_index(self:get_cursor_index() + #text) + self:emit_signal("property::text", self:get_text()) + end end function text_input:overwrite_text(text) - local start_pos = self._private.selection_start - local end_pos = self._private.selection_end + local wp = self._private + + local start_pos = wp.selection_start + local end_pos = wp.selection_end if start_pos > end_pos then start_pos, end_pos = end_pos, start_pos end @@ -345,10 +369,20 @@ function text_input:overwrite_text(text) local old_text = self:get_text() local left_text = old_text:sub(1, start_pos) local right_text = old_text:sub(end_pos + 1) - self:get_text_widget():set_text(left_text .. text .. right_text) - self:set_cursor_index(#left_text) - - self:emit_signal("property::text", self:get_text()) + local new_text = left_text .. text .. right_text + + if wp.pattern then + new_text = new_text:match(wp.pattern) + if new_text then + self:get_text_widget():set_text(new_text) + self:set_cursor_index(#left_text) + self:emit_signal("property::text", self:get_text()) + end + else + self:get_text_widget():set_text(new_text) + self:set_cursor_index(#left_text) + self:emit_signal("property::text", self:get_text()) + end end function text_input:copy() @@ -716,8 +750,7 @@ local function new() wp.reset_on_unfocus = false - wp.only_numbers = false - wp.round = false + wp.pattern = nil wp.obscure = false wp.placeholder = "" From a99eb3ffe07f403c9d1b61e3095da794ea2d1ace Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 10 Mar 2023 05:38:32 +0200 Subject: [PATCH 121/185] Adjust some code + fix initial value not working --- widget/app_launcher/text_input.lua | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index b32403e4..de1c9ad3 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -31,7 +31,7 @@ local properties = { "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "reset_on_unfocus", - "placeholder", "text", "pattern", "obscure", + "placeholder", "initial", "pattern", "obscure", "cursor_blink", "cursor_blink_rate","cursor_size", "cursor_bg", "selection_bg" } @@ -195,20 +195,21 @@ end function text_input:set_widget_template(widget_template) local wp = self._private - self._private.text_widget = widget_template:get_children_by_id("text_role")[1] - self._private.text_widget.forced_width = math.huge - local text_draw = self._private.text_widget.draw - - self._private.text_widget:set_text(self:get_text()) + wp.text_widget = widget_template:get_children_by_id("text_role")[1] + wp.text_widget.forced_width = math.huge + local text_draw = wp.text_widget.draw + if self:get_initial() then + self:set_text(self:get_initial()) + end local placeholder_widget = widget_template:get_children_by_id("placeholder_role") if placeholder_widget then placeholder_widget = placeholder_widget[1] end - function self._private.text_widget:draw(context, cr, width, height) + function wp.text_widget:draw(context, cr, width, height) -- Selection bg - local ink_rect, logical_rect = self._private.layout:get_pixel_extents() + local _, logical_rect = self._private.layout:get_pixel_extents() cr:set_source(gcolor.change_opacity(wp.selection_bg, wp.selection_opacity)) cr:rectangle( wp.selection_start_x, @@ -219,7 +220,7 @@ function text_input:set_widget_template(widget_template) cr:fill() -- Cursor - local ink_rect, logical_rect = self._private.layout:get_pixel_extents() + local _, logical_rect = self._private.layout:get_pixel_extents() cr:set_source(gcolor.change_opacity(wp.cursor_bg, wp.cursor_opacity)) cr:set_line_width(wp.cursor_width) cr:move_to(wp.cursor_x, logical_rect.y - 3) @@ -236,19 +237,17 @@ function text_input:set_widget_template(widget_template) end end - wp.selecting_text = false - - local function on_drag(drawable, lx, ly) + local function on_drag(_, lx, ly) if not wp.selecting_text and (lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly) then self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) - self:set_selection_end_index(self._private.selection_start) + self:set_selection_end_index(wp.selection_start) wp.selecting_text = true elseif wp.selecting_text then self:set_selection_end_index_from_x_y(lx - wp.offset.x, ly - wp.offset.y) end end - self._private.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + wp.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) if button == 1 then wp.press_pos = { lx = lx, ly = ly } wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } @@ -256,7 +255,7 @@ function text_input:set_widget_template(widget_template) end end) - self._private.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) + wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) if not wp.selecting_text then self:set_cursor_index_from_x_y(lx, ly) @@ -266,7 +265,7 @@ function text_input:set_widget_template(widget_template) self:focus() end) - self._private.text_widget:connect_signal("mouse::enter", function() + wp.text_widget:connect_signal("mouse::enter", function() capi.root.cursor("xterm") local wibox = capi.mouse.current_wibox if wibox then @@ -274,7 +273,7 @@ function text_input:set_widget_template(widget_template) end end) - self._private.text_widget:connect_signal("mouse::leave", function(_, find_widgets_result) + wp.text_widget:connect_signal("mouse::leave", function(_, find_widgets_result) if self:get_focused() == false then capi.root.cursor("left_ptr") local wibox = capi.mouse.current_wibox @@ -314,6 +313,11 @@ function text_input:toggle_obscure() self:set_obscure(not self._private.obscure) end +function text_input:set_initial(initial) + self._private.initial = initial + self:set_text(initial) +end + function text_input:update_text(text) if self:get_mode() == "insert" then self:insert_text(text) @@ -738,6 +742,7 @@ local function new() wp.selection_start_y = 0 wp.selection_end_y = 0 wp.selection_opacity = 0 + wp.selecting_text = false wp.unfocus_keys = { "Escape", "Return" } wp.unfocus_on_clicked_outside = true From e1c556e1cbd4777382c335890bc0ce3499a06e61 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 10 Mar 2023 05:51:15 +0200 Subject: [PATCH 122/185] Add unfocus on client focus prop --- widget/app_launcher/text_input.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index de1c9ad3..748e66b9 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -28,7 +28,7 @@ local text_input = { } local properties = { - "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", + "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "unfocus_on_client_focus", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "reset_on_unfocus", "placeholder", "initial", "pattern", "obscure", @@ -749,6 +749,7 @@ local function new() wp.unfocus_on_mouse_leave = false wp.unfocus_on_tag_change = true wp.unfocus_on_other_text_input_focus = true + wp.unfocus_on_client_focus = true wp.focus_on_subject_mouse_enter = nil wp.unfocus_on_subject_mouse_leave = nil @@ -795,6 +796,12 @@ local function new() end end) + capi.client.connect_signal("focus", function() + if wp.unfocus_on_client_focus then + widget:unfocus() + end + end) + return widget end From 60089224d759e10a575b8bf834f3788399f9edea Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 11 Mar 2023 00:13:31 +0200 Subject: [PATCH 123/185] pass in the text for the unfocus signal --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 748e66b9..7e750973 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -709,7 +709,7 @@ function text_input:unfocus() end wp.focused = false - self:emit_signal("unfocus") + self:emit_signal("unfocus", self:get_text()) capi.awesome.emit_signal("text_input::unfocus", self) end From 301d23e4cf3bafd5dd87fd006363fb6da2a64fe8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 11 Mar 2023 00:47:04 +0200 Subject: [PATCH 124/185] Fix text_color not customizable --- widget/app_launcher/text_input.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 7e750973..54244009 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -31,7 +31,9 @@ local properties = { "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "unfocus_on_client_focus", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "reset_on_unfocus", - "placeholder", "initial", "pattern", "obscure", + "text_color", + "placeholder", "initial", + "pattern", "obscure", "cursor_blink", "cursor_blink_rate","cursor_size", "cursor_bg", "selection_bg" } From 26a03aa23bb4d18e7dd18d1a030e0dcad7889159 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 11 Mar 2023 04:40:22 +0200 Subject: [PATCH 125/185] Fix focusing the text input when releasing any mouse button --- widget/app_launcher/text_input.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 54244009..76687518 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -258,13 +258,15 @@ function text_input:set_widget_template(widget_template) end) wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) - find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) - if not wp.selecting_text then - self:set_cursor_index_from_x_y(lx, ly) - else - wp.selecting_text = false + if button == 1 then + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + if not wp.selecting_text then + self:set_cursor_index_from_x_y(lx, ly) + else + wp.selecting_text = false + end + self:focus() end - self:focus() end) wp.text_widget:connect_signal("mouse::enter", function() From 0239710fdf724147783328e2023ae0384120c080 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 10:29:11 +0200 Subject: [PATCH 126/185] Don't emit the signal here, this is called by the user --- widget/app_launcher/text_input.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 76687518..973acf6f 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -339,8 +339,6 @@ function text_input:set_text(text) else self:set_cursor_index(#text) end - - self:emit_signal("property::text", text_widget:get_text()) end function text_input:insert_text(text) From 35e8152312d85b6fff3eb7521a56469ccc68c65e Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 11:38:13 +0200 Subject: [PATCH 127/185] temp --- widget/app_launcher/rofi_grid.lua | 593 ++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) create mode 100644 widget/app_launcher/rofi_grid.lua diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua new file mode 100644 index 00000000..ebc13824 --- /dev/null +++ b/widget/app_launcher/rofi_grid.lua @@ -0,0 +1,593 @@ +local gtable = require("gears.table") +local gtimer = require("gears.timer") +local wibox = require("wibox") +local ipairs = ipairs +local table = table +local math = math + +local rofi_grid = { mt = {} } + +local properties = { + "entries", "page", "lazy_load_widgets", + "widget_template", "entry_template", + "sort_fn", "search_fn", "search_sort_fn", + "sort_alphabetically","reverse_sort_alphabetically,", + "wrap_page_scrolling", "wrap_entry_scrolling" +} + +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) + end + return self + end + end + if not prototype["get_" .. prop] then + prototype["get_" .. prop] = function(self) + return self._private[prop] + end + end + end +end + +local function scroll(self, dir, page_dir) + local grid = self:get_grid() + if #grid.children < 1 then + self._private.selected_widget = nil + return + end + + local next_widget_index = nil + local grid_orientation = grid:get_orientation() + + if dir == "up" then + if grid_orientation == "horizontal" then + next_widget_index = grid:index(self:get_selected_widget()) - 1 + elseif grid_orientation == "vertical" then + next_widget_index = grid:index(self:get_selected_widget()) - grid.forced_num_cols + end + elseif dir == "down" then + if grid_orientation == "horizontal" then + next_widget_index = grid:index(self:get_selected_widget()) + 1 + elseif grid_orientation == "vertical" then + next_widget_index = grid:index(self:get_selected_widget()) + grid.forced_num_cols + end + elseif dir == "left" then + if grid_orientation == "horizontal" then + next_widget_index = grid:index(self:get_selected_widget()) - grid.forced_num_rows + elseif grid_orientation == "vertical" then + next_widget_index = grid:index(self:get_selected_widget()) - 1 + end + elseif dir == "right" then + if grid_orientation == "horizontal" then + next_widget_index = grid:index(self:get_selected_widget()) + grid.forced_num_rows + elseif grid_orientation == "vertical" then + next_widget_index = grid:index(self:get_selected_widget()) + 1 + end + end + + local next_widget = grid.children[next_widget_index] + if next_widget then + next_widget:select() + self:emit_signal("scroll", self:get_index_of_widget(next_widget)) + else + if dir == "up" or dir == "left" then + self:page_backward(page_dir or dir) + elseif dir == "down" or dir == "right" then + self:page_forward(page_dir or dir) + end + end +end + +local function entry_widget(self, entry) + if self._private.entries_widgets_cache[entry] then + return self._private.entries_widgets_cache[entry] + end + + local widget = self._private.entry_template(entry, self) + + local rofi_grid = self + function widget:select() + rofi_grid:select_widget(self) + end + + function widget:is_selected() + return rofi_grid:get_selected_widget() == self + end + + entry.widget = widget + widget.entry = entry + + self._private.entries_widgets_cache[entry] = widget + return widget +end + +function rofi_grid:set_widget_template(widget_template) + self._private.text_input = widget_template:get_children_by_id("text_input_role")[1] + self._private.grid = widget_template:get_children_by_id("grid_role")[1] + self._private.scrollbar = widget_template:get_children_by_id("scrollbar_role") + if self._private.scrollbar then + self._private.scrollbar = self._private.scrollbar[1] + end + + widget_template:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + if button == 4 then + if self:get_grid():get_orientation() == "horizontal" then + self:scroll_up() + else + self:scroll_left("up") + end + elseif button == 5 then + if self:get_grid():get_orientation() == "horizontal" then + self:scroll_down() + else + self:scroll_right("down") + end + end + end) + + self:get_text_input():connect_signal("property::text", function(_, text) + if text == self:get_text() then + return + end + + self._private.text = text + self._private.search_timer:again() + end) + + self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) + if key == "Up" then + self:scroll_up() + end + if key == "Down" then + self:scroll_down() + end + if key == "Left" then + self:scroll_left() + end + if key == "Right" then + self:scroll_right() + end + end) + + local scrollbar = self:get_scrollbar() + if scrollbar then + self:connect_signal("scroll", function(self, new_index) + scrollbar:set_value(new_index) + end) + + self:connect_signal("page::forward", function(self, new_index) + scrollbar:set_value(new_index) + end) + + self:connect_signal("page::backward", function(self, new_index) + scrollbar:set_value(new_index) + end) + + self:connect_signal("search", function(self, text, new_index) + scrollbar:set_maximum(math.max(2, #self:get_matched_entries())) + if new_index then + scrollbar:set_value(new_index) + end + end) + + self:connect_signal("select", function(self, new_index) + scrollbar:set_value(new_index) + end) + + scrollbar:connect_signal("property::value", function(_, value, instant) + if instant ~= true then + self:select_widget_by_index(value) + end + end) + end + + self._private.max_entries_per_page = self:get_grid().forced_num_cols * self:get_grid().forced_num_rows + self._private.entries_per_page = self._private.max_entries_per_page + + self:set_widget(widget_template) +end + +function rofi_grid:set_entries(entries, sort_fn) + self._private.entries = entries + self:set_sort_fn(sort_fn) + self:reset() + local scrollbar = self:get_scrollbar() + if scrollbar then + scrollbar:set_maximum(#self._private.entries) + scrollbar:set_value(1) + end + + if self._private.lazy_load_widgets == false then + for _, entry in ipairs(self._private.entries) do + self._private.entries_widgets_cache[entry] = entry_widget(self, entry) + end + end +end + +function rofi_grid:add_entry(entry) + table.insert(self._private.entries, entry) + self:set_sort_fn() + self:reset() +end + +function rofi_grid:set_sort_fn(sort_fn) + if sort_fn ~= nil then + self._private.sort_fn = sort_fn + end + if self._private.sort_fn ~= nil then + table.sort(self._private.entries, self._private.sort_fn) + end +end + +function rofi_grid:refresh() + local max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() + local min_entry_index_to_include = max_entry_index_to_include - self._private.entries_per_page + + self:get_grid():reset() + + for index, entry in ipairs(self:get_matched_entries()) do + -- Only add widgets that are between this range (part of the current page) + if index > min_entry_index_to_include and index <= max_entry_index_to_include then + self:get_grid():add(entry_widget(self, entry)) + end + end +end + +function rofi_grid:search() + local text = self:get_text() + local old_pos = self:get_grid():get_widget_position(self:get_selected_widget()) + + -- Reset all the matched entrys + self._private.matched_entries = {} + -- Remove all the grid widgets + self:get_grid():reset() + + if text == "" then + self._private.matched_entries = self._private.entries + else + for _, entry in ipairs(self._private.entries) do + text = text:gsub( "%W", "" ) + if self.search_fn(text:lower(), entry, self:get_entries()) then + table.insert(self:get_matched_entries(), entry) + end + end + + if self:get_sort_fn() then + table.sort(self:get_matched_entries(), function(a, b) + return self._private.search_sort_fn(text, a, b) + end) + end + end + for _, entry in ipairs(self._private.matched_entries) do + -- Only add the widgets for entrys that are part of the first page + if #self:get_grid().children + 1 <= self._private.max_entries_per_page then + self:get_grid():add(entry_widget(self, entry)) + end + end + + -- Recalculate the entrys per page based on the current matched entrys + self._private.entries_per_page = math.min(#self:get_matched_entries(), self._private.max_entries_per_page) + + -- Recalculate the pages count based on the current entrys per page + self._private.pages_count = math.ceil(math.max(1, #self:get_matched_entries()) / math.max(1, self._private.entries_per_page)) + + -- Page should be 1 after a search + self._private.current_page = 1 + + -- This is an option to mimic rofi behaviour where after a search + -- it will reselect the entry whose index is the same as the entry index that was previously selected + -- and if matched_entries.length < current_index it will instead select the entry with the greatest index + if self._private.try_to_keep_index_after_searching then + local widget_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) + if widget_at_old_pos and widget_at_old_pos[1] then + widget_at_old_pos[1]:select() + else + local widget = self:get_grid().children[#self:get_grid().children] + widget:select() + end + -- Otherwise select the first entry on the list + elseif self:get_grid().children[1] then + local widget = self:get_grid().children[1] + widget:select() + end + + self:emit_signal("search", self:get_text(), self:get_index_of_widget(self:get_selected_widget())) +end + +function rofi_grid:select_widget(widget) + print(widget) + self:select_widget_by_index(self:get_index_of_widget(widget)) +end + +function rofi_grid:select_widget_by_index(index) + local previous_widget = self:get_selected_widget() + if previous_widget then + self._private.selected_widget = nil + self:emit_signal("unselect") + previous_widget:emit_signal("unselect") + end + + print(index) + + -- local new_widget = self:get_widget_of_index(index) + -- if new_widget then + -- local page = self:get_page_of_index(index) + -- if self:get_current_page() ~= page then + -- self:set_page(page) + -- end + + -- self._private.selected_widget = new_widget + -- self:emit_signal("select", index) + -- new_widget:emit_signal("select", index) + -- end +end + +function rofi_grid:scroll_up(page_dir) + scroll(self, "up", page_dir) +end + +function rofi_grid:scroll_down(page_dir) + scroll(self, "down", page_dir) +end + +function rofi_grid:scroll_left(page_dir) + scroll(self, "left", page_dir) +end + +function rofi_grid:scroll_right(page_dir) + scroll(self, "right", page_dir) +end + +function rofi_grid:page_forward(dir) + local min_entry_index_to_include = 0 + local max_entry_index_to_include = self._private.entries_per_page + + if self:get_current_page() < self:get_pages_count() then + min_entry_index_to_include = self._private.entries_per_page * self:get_current_page() + self._private.current_page = self:get_current_page() + 1 + max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() + elseif self._private.wrap_page_scrolling and #self:get_matched_entries() >= self._private.max_entries_per_page then + self._private.current_page = 1 + min_entry_index_to_include = 0 + max_entry_index_to_include = self._private.entries_per_page + elseif self._private.wrap_entry_scrolling then + local widget = self:get_grid():get_widgets_at(1, 1)[1] + widget:select() + self:emit_signal("scroll", self:get_index_of_widget(widget)) + return + else + return + end + + local pos = self:get_grid():get_widget_position(self:get_selected_widget()) + + -- Remove the current page entrys from the grid + self:get_grid():reset() + + for index, entry in ipairs(self:get_matched_entries()) do + -- Only add widgets that are between this range (part of the current page) + if index > min_entry_index_to_include and index <= max_entry_index_to_include then + self:get_grid():add(entry_widget(self, entry)) + end + end + + if self:get_current_page() > 1 or self._private.wrap_page_scrolling then + local widget = nil + if dir == "down" then + widget = self:get_grid():get_widgets_at(1, 1)[1] + elseif dir == "right" then + widget = self:get_grid():get_widgets_at(pos.row, 1) + if widget then + widget = widget[1] + end + if widget == nil then + widget = self:get_grid().children[#self:get_grid().children] + end + end + widget:select() + end + + self:emit_signal("page::forward", self:get_index_of_widget(self:get_selected_widget())) +end + +function rofi_grid:page_backward(dir) + if self:get_current_page() > 1 then + self._private.current_page = self:get_current_page() - 1 + elseif self._private.wrap_page_scrolling and #self:get_matched_entries() >= self._private.max_entries_per_page then + self._private.current_page = self:get_pages_count() + elseif self._private.wrap_entry_scrolling then + local widget = self:get_grid().children[#self:get_grid().children] + widget:select() + self:emit_signal("scroll", self:get_index_of_widget(widget)) + return + else + return + end + + local pos = self:get_grid():get_widget_position(self:get_selected_widget()) + + -- Remove the current page entrys from the grid + self:get_grid():reset() + + local max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() + local min_entry_index_to_include = max_entry_index_to_include - self._private.entries_per_page + + for index, entry in ipairs(self:get_matched_entries()) do + -- Only add widgets that are between this range (part of the current page) + if index > min_entry_index_to_include and index <= max_entry_index_to_include then + self:get_grid():add(entry_widget(self, entry)) + end + end + + local widget = nil + if self:get_current_page() < self:get_pages_count() then + if dir == "up" then + widget = self:get_grid().children[#self:get_grid().children] + else + -- Keep the same row from last page + local _, columns = self:get_grid():get_dimension() + widget = self:get_grid():get_widgets_at(pos.row, columns)[1] + end + elseif self._private.wrap_page_scrolling then + widget = self:get_grid().children[#self:get_grid().children] + end + widget:select() + + self:emit_signal("page::backward", self:get_index_of_widget(self:get_selected_widget())) +end + +function rofi_grid:set_page(page) + self:get_grid():reset() + self._private.matched_entries = self._private.entries + self._private.entries_per_page = self._private.max_entries_per_page + self._private.pages_count = math.ceil(#self._private.entries / self._private.entries_per_page) + self._private.current_page = page + + local max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() + local min_entry_index_to_include = max_entry_index_to_include - self._private.entries_per_page + + for index, entry in ipairs(self:get_matched_entries()) do + -- Only add widgets that are between this range (part of the current page) + if index > min_entry_index_to_include and index <= max_entry_index_to_include then + self:get_grid():add(entry_widget(self, entry)) + end + end + + local widget = self:get_grid():get_widgets_at(1, 1) + if widget then + widget = widget[1] + if widget then + widget:select() + end + end +end + +function rofi_grid:reset() + self:get_grid():reset() + self._private.matched_entries = self._private.entries + self._private.entries_per_page = self._private.max_entries_per_page + self._private.pages_count = math.ceil(#self._private.entries / self._private.entries_per_page) + self._private.current_page = 1 + + for index, entry in ipairs(self._private.entries) do + -- Only add the entrys that are part of the first page + if index <= self._private.entries_per_page then + self:get_grid():add(entry_widget(self, entry)) + else + break + end + end + + local widget = self:get_grid():get_widgets_at(1, 1) + if widget then + widget = widget[1] + if widget then + widget:select() + end + end + + self:get_text_input():set_text("") +end + +function rofi_grid:get_scrollbar() + return self._private.scrollbar +end + +function rofi_grid:get_text_input() + return self._private.text_input +end + +function rofi_grid:get_grid() + return self._private.grid +end + +function rofi_grid:get_entries_per_page() + return self._private.entries_per_page +end + +function rofi_grid:get_pages_count() + return self._private.pages_count +end + +function rofi_grid:get_current_page() + return self._private.current_page +end + +function rofi_grid:get_entries() + return self._private.entries +end + +function rofi_grid:get_matched_entries() + return self._private.matched_entries +end + +function rofi_grid:get_page_of_index(index) + return math.floor((index - 1) / self._private.entries_per_page) + 1 +end + +function rofi_grid:get_index_of_widget(widget) + for index, matched_widget in ipairs(self:get_matched_entries()) do + if matched_widget.entry == widget.entry then + return index + end + end +end + +function rofi_grid:get_widget_of_index(index) + return self:get_matched_entries()[index].widget +end + +function rofi_grid:get_text() + return self._private.text +end + +function rofi_grid:get_selected_widget() + return self._private.selected_widget +end + +local function new() + local widget = wibox.container.background() + gtable.crush(widget, rofi_grid, true) + + local wp = widget._private + wp.entries_widgets_cache = setmetatable({}, { __mode = "v" }) + + wp.entries = {} + wp.sort_fn = nil + wp.sort_alphabetically = true + wp.reverse_sort_alphabetically = false + wp.try_to_keep_index_after_searching = false + wp.wrap_page_scrolling = true + wp.wrap_entry_scrolling = true + wp.search_fn = nil + wp.lazy_load_widgets = true + + wp.text = "" + wp.pages_count = 0 + wp.current_page = 1 + wp.search_timer = gtimer { + timeout = 0.35, + call_now = false, + autostart = false, + single_shot = true, + callback = function() + widget:search() + end + } + + return widget +end + +function rofi_grid.mt:__call(...) + return new(...) +end + +build_properties(rofi_grid, properties) + +return setmetatable(rofi_grid, rofi_grid.mt) From 4ba108df9282c2efc7cb8b3e1219a946e57d5faf Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:12:31 +0200 Subject: [PATCH 128/185] Fix typo --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 973acf6f..07aeb6a2 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -762,7 +762,7 @@ local function new() wp.obscure = false wp.placeholder = "" - wp.text_color = beautiful.colors.fg_normal + wp.text_color = beautiful.fg_normal wp.text = "" wp.cursor_width = 2 From ba8071b13f90451d1a792f7586fb3a011b49cd4b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:12:49 +0200 Subject: [PATCH 129/185] Extract the scroller widget into it's own file + add scrollbar --- widget/app_launcher/init.lua | 790 +++++++++--------------------- widget/app_launcher/rofi_grid.lua | 156 +++--- 2 files changed, 316 insertions(+), 630 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3d1e0bc9..3efbb8c4 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -6,6 +6,7 @@ local gtimer = require("gears.timer") local wibox = require("wibox") local beautiful = require("beautiful") local text_input_widget = require(... .. ".text_input") +local rofi_grid_widget = require(... .. ".rofi_grid") local dpi = beautiful.xresources.apply_dpi local string = string local table = table @@ -37,225 +38,221 @@ local function has_value(tab, val) return false end -local function scroll(self, dir, page_dir) - local grid = self:get_grid() - if #grid.children < 1 then - self._private.selected_app_widget = nil - return - end - - local next_app_index = nil - local grid_orientation = grid:get_orientation() - - if dir == "up" then - if grid_orientation == "horizontal" then - next_app_index = grid:index(self:get_selected_app_widget()) - 1 - elseif grid_orientation == "vertical" then - next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_cols - end - elseif dir == "down" then - if grid_orientation == "horizontal" then - next_app_index = grid:index(self:get_selected_app_widget()) + 1 - elseif grid_orientation == "vertical" then - next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_cols - end - elseif dir == "left" then - if grid_orientation == "horizontal" then - next_app_index = grid:index(self:get_selected_app_widget()) - grid.forced_num_rows - elseif grid_orientation == "vertical" then - next_app_index = grid:index(self:get_selected_app_widget()) - 1 - end - elseif dir == "right" then - if grid_orientation == "horizontal" then - next_app_index = grid:index(self:get_selected_app_widget()) + grid.forced_num_rows - elseif grid_orientation == "vertical" then - next_app_index = grid:index(self:get_selected_app_widget()) + 1 - end - end - - local next_app = grid.children[next_app_index] - if next_app then - next_app:select() - self:emit_signal("scroll", dir) - else - if dir == "up" or dir == "left" then - self:page_backward(page_dir or dir) - elseif dir == "down" or dir == "right" then - self:page_forward(page_dir or dir) - end - end -end - -local function app_widget(self, app) - if self._private.apps_widgets_cache[app.name] then - return self._private.apps_widgets_cache[app.name] - end - - local widget = nil - - if self.app_template == nil then - widget = wibox.widget +local function build_widget(self) + local widget_template = self.widget_template + if widget_template == nil then + widget_template = wibox.widget { - widget = wibox.container.background, - forced_width = dpi(300), - forced_height = dpi(120), - bg = self.app_normal_color, - { - widget = wibox.container.margin, - margins = dpi(10), + layout = rofi_grid_widget, + lazy_load_widgets = false, + widget_template = wibox.widget { + layout = wibox.layout.fixed.vertical, + forced_width = dpi(1000), + forced_height = dpi(1000), + spacing = dpi(15), + { + widget = text_input_widget, + id = "text_input_role", + reset_on_stop = self.reset_on_hide, + placeholder = self.text_input_placeholder, + unfocus_keys = { }, + unfocus_on_clicked_inside = false, + unfocus_on_clicked_outside = false, + unfocus_on_mouse_leave = false, + unfocus_on_tag_change = false, + unfocus_on_other_text_input_focus = false, + focus_on_subject_mouse_enter = nil, + unfocus_on_subject_mouse_leave = nil, + widget_template = wibox.widget { + widget = wibox.container.background, + forced_height = dpi(120), + bg = self.text_input_bg_color, + { + widget = wibox.container.margin, + margins = dpi(30), + { + widget = wibox.widget.textbox, + text_color = self.text_input_color, + id = "text_role" + } + } + } + }, { - layout = wibox.layout.fixed.vertical, + layout = wibox.layout.fixed.horizontal, spacing = dpi(10), { - widget = wibox.container.place, - halign = "center", - valign = "center", - { - widget = wibox.widget.imagebox, - id = "icon_role", - forced_width = dpi(70), - forced_height = dpi(70), - image = app.icon - }, + layout = wibox.layout.grid, + id = "grid_role", + orientation = "horizontal", + homogeneous = true, + spacing = dpi(30), + forced_num_cols = self.apps_per_column, + forced_num_rows = self.apps_per_row, }, { - widget = wibox.container.place, - halign = "center", - valign = "center", + layout = wibox.container.rotate, + direction = 'west', { - widget = wibox.widget.textbox, - id = "name_role", - markup = string.format("%s", self.app_name_normal_color, app.name) + widget = wibox.widget.slider, + id = "scrollbar_role", + forced_width = dpi(5), + minimum = 1, + value = 1, + -- bar_shape = helpers.ui.rrect(), + bar_height= 3, + bar_color = beautiful.colors.transparent, + bar_active_color = beautiful.colors.transparent, + handle_width = dpi(50), + handle_color = beautiful.bg_normal, + -- handle_shape = helpers.ui.rrect(), + handle_color = beautiful.colors.on_background + } + } + } + }, + entry_template = function(app) + local widget = wibox.widget + { + widget = wibox.container.background, + forced_width = dpi(300), + forced_height = dpi(120), + bg = self.app_normal_color, + { + widget = wibox.container.margin, + margins = dpi(10), + { + layout = wibox.layout.fixed.vertical, + spacing = dpi(10), + { + widget = wibox.container.place, + halign = "center", + valign = "center", + { + widget = wibox.widget.imagebox, + id = "icon_role", + forced_width = dpi(70), + forced_height = dpi(70), + image = app.icon + }, + }, + { + widget = wibox.container.place, + halign = "center", + valign = "center", + { + widget = wibox.widget.textbox, + id = "name_role", + markup = string.format("%s", self.app_name_normal_color, app.name) + } + } } } } - } - } - widget:connect_signal("mouse::enter", function() - local widget = capi.mouse.current_wibox - if widget then - widget.cursor = "hand2" - end - end) + widget:connect_signal("mouse::enter", function() + local widget = capi.mouse.current_wibox + if widget then + widget.cursor = "hand2" + end + end) - widget:connect_signal("mouse::leave", function() - local widget = capi.mouse.current_wibox - if widget then - widget.cursor = "left_ptr" - end - end) - - widget:connect_signal("button::press", function(app, _, __, button) - if button == 1 then - if app:is_selected() or not self.select_before_spawn then - app:run() - else - app:select() - end - end - end) - else - widget = self.app_template(app, self) - end + widget:connect_signal("mouse::leave", function() + local widget = capi.mouse.current_wibox + if widget then + widget.cursor = "left_ptr" + end + end) - local app_launcher = self - function widget:run() - if app.terminal == true then - local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. app.exec) - local class = app.startup_wm_class or app.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - app:launch() - end + widget:connect_signal("button::press", function(_, __, __, button) + if button == 1 then + if widget:is_selected() or not self.select_before_spawn then + widget:run() + else + widget:select() + end + end + end) - if app_launcher.hide_on_launch then - app_launcher:hide() - end - end + widget:connect_signal("select", function() + widget.bg = self.app_selected_color + local name_widget = widget:get_children_by_id("name_role")[1] + name_widget.markup = string.format("%s", self.app_name_selected_color, name_widget.text) + end) - function widget:run_or_select() - if self:is_selected() then - self:run() - else - self:select() - end - end + widget:connect_signal("unselect", function() + widget.bg = self.app_normal_color + local name_widget = widget:get_children_by_id("name_role")[1] + name_widget.markup = string.format("%s", self.app_name_normal_color, name_widget.text) + end) - function widget:run_as_root() - if app.terminal == true then - local pid = awful.spawn.with_shell( - AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. - RUN_AS_ROOT_SCRIPT_PATH .. " " .. - app.exec - ) - local class = app.startup_wm_class or app.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. app.exec) + return widget + end + } + end + widget_template:set_search_fn(function(text, app) + local matched_apps = Gio.DesktopAppInfo.search(text:lower()) + for _, matched_app in ipairs(matched_apps) do + for _, app_id in ipairs(matched_app) do + if app.id == app_id then + return true + end + end end + end) - if app_launcher.hide_on_launch then - app_launcher:hide() - end - end + self._private.widget = awful.popup + { + screen = self.screen, + type = self.type, + visible = false, + ontop = true, + placement = self.placement, + border_width = self.border_width, + border_color = self.border_color, + shape = self.shape, + bg = self.bg, + widget = widget_template + } - function widget:select() - if app_launcher:get_selected_app_widget() then - app_launcher:get_selected_app_widget():unselect() + self:get_text_input():connect_signal("key::press", function(_, mod, key, cmd) + if key == "Escape" then + self:hide() end - app_launcher._private.selected_app_widget = self - self:emit_signal("select") - self.selected = true - - if app_launcher.app_template == nil then - widget.bg = app_launcher.app_selected_color - local name_widget = self:get_children_by_id("name_role")[1] - name_widget.markup = string.format("%s", app_launcher.app_name_selected_color, name_widget.text) + end) + + self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) + if key == "Return" then + if self:get_rofi_grid():get_selected_widget() ~= nil then + self:get_rofi_grid():get_selected_widget():run() + end end - end + end) +end - function widget:unselect() - self:emit_signal("unselect") - self.selected = false - app_launcher._private.selected_app_widget = nil +local function default_sort_fn(a, b) + local is_a_favorite = has_value(self.favorites, a.id) + local is_b_favorite = has_value(self.favorites, b.id) - if app_launcher.app_template == nil then - widget.bg = app_launcher.app_normal_color - local name_widget = self:get_children_by_id("name_role")[1] - name_widget.markup = string.format("%s", app_launcher.app_name_normal_color, name_widget.text) - end + -- Sort the favorite apps first + if is_a_favorite and not is_b_favorite then + return true + elseif not is_a_favorite and is_b_favorite then + return false end - function widget:is_selected() - return app_launcher._private.selected_app_widget == self + -- Sort alphabetically if specified + if self.sort_alphabetically then + return a.name:lower() < b.name:lower() + elseif self.reverse_sort_alphabetically then + return b.name:lower() > a.name:lower() + else + return true end - - function app:run() widget:run() end - function app:run_or_select() widget:run_or_select() end - function app:run_as_root() widget:run_as_root() end - function app:select() widget:select() end - function app:unselect() widget:unselect() end - function app:is_selected() widget:is_selected() end - - self._private.apps_widgets_cache[app.name] = widget - - return widget end local function generate_apps(self) - self._private.all_apps = {} - self._private.matched_apps = {} + local entries = {} local app_info = Gio.AppInfo local apps = app_info.get_all() @@ -299,351 +296,69 @@ local function generate_apps(self) app:launch() end } - table.insert(self._private.all_apps, app) - if self.lazy_load_widgets == false then - self._private.apps_widgets_cache[app.name] = app_widget(self, app) - end + table.insert(entries, app) end end end end - self:sort_apps() + self:get_rofi_grid():set_entries(entries, self.sort_fn) end -local function build_widget(self) - local widget = self.widget_template - if widget == nil then - self._private.text_input = wibox.widget - { - widget = text_input_widget, - reset_on_stop = self.reset_on_hide, - placeholder = self.text_input_placeholder, - widget_template = wibox.widget { - widget = wibox.container.background, - forced_height = dpi(120), - bg = self.text_input_bg_color, - { - widget = wibox.container.margin, - margins = dpi(30), - { - widget = wibox.widget.textbox, - id = "text_role" - } - } - } - } - self._private.grid = wibox.widget - { - layout = wibox.layout.grid, - orientation = "horizontal", - homogeneous = true, - spacing = dpi(30), - forced_num_cols = self.apps_per_column, - forced_num_rows = self.apps_per_row, - } - widget = wibox.widget - { - layout = wibox.layout.fixed.vertical, - self._private.text_input, - { - widget = wibox.container.margin, - margins = dpi(30), - self._private.grid - } - } +function app_launcher:run(widget) + if widget.entry.terminal == true then + local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. widget.entry.exec) + local class = widget.entry.startup_wm_class or widget.entry.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) else - self._private.text_input = widget:get_children_by_id("text_input_role")[1] - self._private.grid = widget:get_children_by_id("grid_role")[1] + widget.entry:launch() end - self._private.widget = awful.popup - { - screen = self.screen, - type = self.type, - visible = false, - ontop = true, - placement = self.placement, - border_width = self.border_width, - border_color = self.border_color, - shape = self.shape, - bg = self.bg, - widget = widget - } - - self:get_grid():connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) - if button == 4 then - if self:get_grid():get_orientation() == "horizontal" then - self:scroll_up() - else - self:scroll_left("up") - end - elseif button == 5 then - if self:get_grid():get_orientation() == "horizontal" then - self:scroll_down() - else - self:scroll_right("down") - end - end - end) - - self:get_text_input():connect_signal("property::text", function(_, text) - if text == self:get_text() then - return - end - - self._private.text = text - self._private.search_timer:again() - end) - - - self:get_text_input():connect_signal("key::press", function(_, mod, key, cmd) - if key == "Escape" then - self:hide() - end - end) - - self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) - if key == "Return" then - if self:get_selected_app_widget() ~= nil then - self:get_selected_app_widget():run() - end - end - if key == "Up" then - self:scroll_up() - end - if key == "Down" then - self:scroll_down() - end - if key == "Left" then - self:scroll_left() - end - if key == "Right" then - self:scroll_right() - end - end) - - self._private.max_apps_per_page = self:get_grid().forced_num_cols * self:get_grid().forced_num_rows - self._private.apps_per_page = self._private.max_apps_per_page -end - -function app_launcher:sort_apps(sort_fn) - table.sort(self._private.all_apps, sort_fn or self.sort_fn or function(a, b) - local is_a_favorite = has_value(self.favorites, a.id) - local is_b_favorite = has_value(self.favorites, b.id) - - -- Sort the favorite apps first - if is_a_favorite and not is_b_favorite then - return true - elseif not is_a_favorite and is_b_favorite then - return false - end - - -- Sort alphabetically if specified - if self.sort_alphabetically then - return a.name:lower() < b.name:lower() - elseif self.reverse_sort_alphabetically then - return b.name:lower() > a.name:lower() - else - return true - end - end) -end - -function app_launcher:set_favorites(favorites) - self.favorites = favorites - self:sort_apps() - self:refresh() -end - -function app_launcher:refresh() - local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() - local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - - self:get_grid():reset() - - for index, app in ipairs(self._private.matched_apps) do - -- Only add widgets that are between this range (part of the current page) - if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) - end + if self.hide_on_launch then + app_launcher:hide() end end -function app_launcher:search() - local text = self:get_text() - local old_pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) - - -- Reset all the matched apps - self._private.matched_apps = {} - -- Remove all the grid widgets - self:get_grid():reset() - - if text == "" then - self._private.matched_apps = self._private.all_apps - for _, matched_app in ipairs(self._private.matched_apps) do - if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget(self, matched_app)) - else - break - end - end +function app_launcher:run_or_select(widget) + if self:get_selected_widget() == widget then + self:run(widget) else - local matched_apps = Gio.DesktopAppInfo.search(text:lower()) - for _, matched_app in ipairs(matched_apps) do - for _, app_id in ipairs(matched_app) do - for _, app in ipairs(self._private.all_apps) do - if app.id == app_id then - table.insert(self._private.matched_apps, app) - -- Only add the widgets for apps that are part of the first page - if #self:get_grid().children + 1 <= self._private.max_apps_per_page then - self:get_grid():add(app_widget(self, app)) - end - end - end - end - end - end - - -- Recalculate the apps per page based on the current matched apps - self._private.apps_per_page = math.min(#self._private.matched_apps, self._private.max_apps_per_page) - - -- Recalculate the pages count based on the current apps per page - self._private.pages_count = math.ceil(math.max(1, #self._private.matched_apps) / math.max(1, self._private.apps_per_page)) - - -- Page should be 1 after a search - self._private.current_page = 1 - - -- This is an option to mimic rofi behaviour where after a search - -- it will reselect the app whose index is the same as the app index that was previously selected - -- and if matched_apps.length < current_index it will instead select the app with the greatest index - if self.try_to_keep_index_after_searching then - if self:get_grid():get_widgets_at(old_pos.row, old_pos.col) == nil then - local app = self:get_grid().children[#self:get_grid().children] - app:select() - else - local app = self:get_grid():get_widgets_at(old_pos.row, old_pos.col)[1] - app:select() - end - -- Otherwise select the first app on the list - elseif #self:get_grid().children > 0 then - local app = self:get_grid():get_widgets_at(1, 1)[1] - app:select() + self:select(widget) end - - self:emit_signal("search", self:get_text(), self:get_current_page(), self:get_pages_count()) -end - -function app_launcher:scroll_up(page_dir) - scroll(self, "up", page_dir) -end - -function app_launcher:scroll_down(page_dir) - scroll(self, "down", page_dir) end -function app_launcher:scroll_left(page_dir) - scroll(self, "left", page_dir) -end - -function app_launcher:scroll_right(page_dir) - scroll(self, "right", page_dir) -end - -function app_launcher:page_forward(dir) - local min_app_index_to_include = 0 - local max_app_index_to_include = self._private.apps_per_page - - if self:get_current_page() < self:get_pages_count() then - min_app_index_to_include = self._private.apps_per_page * self:get_current_page() - self._private.current_page = self:get_current_page() + 1 - max_app_index_to_include = self._private.apps_per_page * self:get_current_page() - elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then - self._private.current_page = 1 - min_app_index_to_include = 0 - max_app_index_to_include = self._private.apps_per_page - elseif self.wrap_app_scrolling then - local app = self:get_grid():get_widgets_at(1, 1)[1] - app:select() - return +function app_launcher:run_as_root(widget) + if widget.entry.terminal == true then + local pid = awful.spawn.with_shell( + AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. + RUN_AS_ROOT_SCRIPT_PATH .. " " .. + widget.entry.exec + ) + local class = widget.entry.startup_wm_class or widget.entry.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) else - return + awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. widget.entry.exec) end - local pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) - - -- Remove the current page apps from the grid - self:get_grid():reset() - - for index, app in ipairs(self._private.matched_apps) do - -- Only add widgets that are between this range (part of the current page) - if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) - end - end - - if self:get_current_page() > 1 or self.wrap_page_scrolling then - local app = nil - if dir == "down" then - app = self:get_grid():get_widgets_at(1, 1)[1] - elseif dir == "right" then - app = self:get_grid():get_widgets_at(pos.row, 1) - if app then - app = app[1] - end - if app == nil then - app = self:get_grid().children[#self:get_grid().children] - end - end - app:select() + if self.hide_on_launch then + app_launcher:hide() end - - self:emit_signal("page::forward", dir, self:get_current_page(), self:get_pages_count()) end -function app_launcher:page_backward(dir) - if self:get_current_page() > 1 then - self._private.current_page = self:get_current_page() - 1 - elseif self.wrap_page_scrolling and #self._private.matched_apps >= self._private.max_apps_per_page then - self._private.current_page = self:get_pages_count() - elseif self.wrap_app_scrolling then - local app = self:get_grid().children[#self:get_grid().children] - app:select() - return - else - return - end - - local pos = self:get_grid():get_widget_position(self:get_selected_app_widget()) - - -- Remove the current page apps from the grid - self:get_grid():reset() - - local max_app_index_to_include = self._private.apps_per_page * self:get_current_page() - local min_app_index_to_include = max_app_index_to_include - self._private.apps_per_page - - for index, app in ipairs(self._private.matched_apps) do - -- Only add widgets that are between this range (part of the current page) - if index > min_app_index_to_include and index <= max_app_index_to_include then - self:get_grid():add(app_widget(self, app)) - end - end - - local app = nil - if self:get_current_page() < self:get_pages_count() then - if dir == "up" then - app = self:get_grid().children[#self:get_grid().children] - else - -- Keep the same row from last page - local _, columns = self:get_grid():get_dimension() - app = self:get_grid():get_widgets_at(pos.row, columns)[1] - end - elseif self.wrap_page_scrolling then - app = self:get_grid().children[#self:get_grid().children] - end - app:select() - - self:emit_signal("page::backward", dir, self:get_current_page(), self:get_pages_count()) +function app_launcher:set_favorites(favorites) + self.favorites = favorites + self:get_rofi_grid():set_sort_fn(self.sort_fn) + self:refresh() end function app_launcher:show() @@ -662,7 +377,7 @@ function app_launcher:hide() end if self.reset_on_hide == true then - self:reset() + self:get_rofi_grid():reset() end self:get_widget().visible = false @@ -678,54 +393,16 @@ function app_launcher:toggle() end end -function app_launcher:reset() - self:get_grid():reset() - self._private.matched_apps = self._private.all_apps - self._private.apps_per_page = self._private.max_apps_per_page - self._private.pages_count = math.ceil(#self._private.all_apps / self._private.apps_per_page) - self._private.current_page = 1 - - for index, app in ipairs(self._private.all_apps) do - -- Only add the apps that are part of the first page - if index <= self._private.apps_per_page then - self:get_grid():add(app_widget(self, app)) - else - break - end - end - - local app = self:get_grid():get_widgets_at(1, 1)[1] - app:select() - - self:get_text_input():set_text("") -end - function app_launcher:get_widget() return self._private.widget end -function app_launcher:get_text_input() - return self._private.text_input -end - -function app_launcher:get_grid() - return self._private.grid +function app_launcher:get_rofi_grid() + return self:get_widget().widget end -function app_launcher:get_pages_count() - return self._private.pages_count -end - -function app_launcher:get_current_page() - return self._private.current_page -end - -function app_launcher:get_text() - return self._private.text -end - -function app_launcher:get_selected_app_widget() - return self._private.selected_app_widget +function app_launcher:get_text_input() + return self:get_rofi_grid():get_text_input() end local function new(args) @@ -766,6 +443,7 @@ local function new(args) args.apps_per_column = default_value(args.apps_per_column, 3) args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") + args.text_input_color = default_value(args.text_input_bg_color, "#FFFFFF") args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") args.app_normal_color = default_value(args.app_normal_color, "#000000") @@ -781,7 +459,6 @@ local function new(args) ret._private.text = "" ret._private.pages_count = 0 ret._private.current_page = 1 - ret._private.apps_widgets_cache = setmetatable({}, { __mode = "v" }) ret._private.search_timer = gtimer { timeout = 0.05, call_now = false, @@ -827,7 +504,6 @@ local function new(args) build_widget(ret) generate_apps(ret) - ret:reset() return ret end diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index ebc13824..85caecac 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -74,7 +74,7 @@ local function scroll(self, dir, page_dir) local next_widget = grid.children[next_widget_index] if next_widget then next_widget:select() - self:emit_signal("scroll", self:get_index_of_widget(next_widget)) + self:emit_signal("scroll", self:get_index_of_entry(next_widget.entry)) else if dir == "up" or dir == "left" then self:page_backward(page_dir or dir) @@ -93,17 +93,37 @@ local function entry_widget(self, entry) local rofi_grid = self function widget:select() - rofi_grid:select_widget(self) + if rofi_grid:get_selected_widget() then + rofi_grid:get_selected_widget():unselect() + end + rofi_grid._private.selected_widget = self + self.selected = true + + local index = rofi_grid:get_index_of_entry(entry) + self:emit_signal("select", index) + rofi_grid:emit_signal("select", index) + end + + function widget:unselect() + self.selected = false + rofi_grid._private.selected_widget = nil + + self:emit_signal("unselect") + rofi_grid:emit_signal("unselect") end function widget:is_selected() - return rofi_grid:get_selected_widget() == self + return rofi_grid._private.selected_widget == self end + function entry:select() widget:select() end + function entry:unselect() widget:unselect() end + function entry:is_selected() widget:is_selected() end entry.widget = widget widget.entry = entry self._private.entries_widgets_cache[entry] = widget + return widget end @@ -182,7 +202,7 @@ function rofi_grid:set_widget_template(widget_template) scrollbar:connect_signal("property::value", function(_, value, instant) if instant ~= true then - self:select_widget_by_index(value) + self:set_selected_entry(value) end end) end @@ -253,12 +273,12 @@ function rofi_grid:search() else for _, entry in ipairs(self._private.entries) do text = text:gsub( "%W", "" ) - if self.search_fn(text:lower(), entry, self:get_entries()) then + if self._private.search_fn(text:lower(), entry) then table.insert(self:get_matched_entries(), entry) end end - if self:get_sort_fn() then + if self:get_search_sort_fn() then table.sort(self:get_matched_entries(), function(a, b) return self._private.search_sort_fn(text, a, b) end) @@ -284,9 +304,9 @@ function rofi_grid:search() -- it will reselect the entry whose index is the same as the entry index that was previously selected -- and if matched_entries.length < current_index it will instead select the entry with the greatest index if self._private.try_to_keep_index_after_searching then - local widget_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) - if widget_at_old_pos and widget_at_old_pos[1] then - widget_at_old_pos[1]:select() + local entry_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) + if entry_at_old_pos and entry_at_old_pos[1] then + entry_at_old_pos[1]:select() else local widget = self:get_grid().children[#self:get_grid().children] widget:select() @@ -297,35 +317,21 @@ function rofi_grid:search() widget:select() end - self:emit_signal("search", self:get_text(), self:get_index_of_widget(self:get_selected_widget())) -end - -function rofi_grid:select_widget(widget) - print(widget) - self:select_widget_by_index(self:get_index_of_widget(widget)) + self:emit_signal("search", self:get_text(), self:get_index_of_entry(self:get_selected_widget().entry)) end -function rofi_grid:select_widget_by_index(index) - local previous_widget = self:get_selected_widget() - if previous_widget then - self._private.selected_widget = nil - self:emit_signal("unselect") - previous_widget:emit_signal("unselect") +function rofi_grid:set_selected_entry(index) + local selected_widget_index = self:get_grid():index(self:get_selected_widget()) + if index == selected_widget_index then + return end - print(index) - - -- local new_widget = self:get_widget_of_index(index) - -- if new_widget then - -- local page = self:get_page_of_index(index) - -- if self:get_current_page() ~= page then - -- self:set_page(page) - -- end + local page = self:get_page_of_index(index) + if self:get_current_page() ~= page then + self:set_page(page) + end - -- self._private.selected_widget = new_widget - -- self:emit_signal("select", index) - -- new_widget:emit_signal("select", index) - -- end + self:get_entry_of_index(index).widget:select() end function rofi_grid:scroll_up(page_dir) @@ -359,7 +365,7 @@ function rofi_grid:page_forward(dir) elseif self._private.wrap_entry_scrolling then local widget = self:get_grid():get_widgets_at(1, 1)[1] widget:select() - self:emit_signal("scroll", self:get_index_of_widget(widget)) + self:emit_signal("scroll", self:get_index_of_entry(widget.entry)) return else return @@ -378,22 +384,22 @@ function rofi_grid:page_forward(dir) end if self:get_current_page() > 1 or self._private.wrap_page_scrolling then - local widget = nil + local entry = nil if dir == "down" then - widget = self:get_grid():get_widgets_at(1, 1)[1] + entry = self:get_grid():get_widgets_at(1, 1)[1] elseif dir == "right" then - widget = self:get_grid():get_widgets_at(pos.row, 1) - if widget then - widget = widget[1] + entry = self:get_grid():get_widgets_at(pos.row, 1) + if entry then + entry = entry[1] end - if widget == nil then - widget = self:get_grid().children[#self:get_grid().children] + if entry == nil then + entry = self:get_grid().children[#self:get_grid().children] end end - widget:select() + entry:select() end - self:emit_signal("page::forward", self:get_index_of_widget(self:get_selected_widget())) + self:emit_signal("page::forward", self:get_index_of_entry(self:get_selected_widget().entry)) end function rofi_grid:page_backward(dir) @@ -404,7 +410,7 @@ function rofi_grid:page_backward(dir) elseif self._private.wrap_entry_scrolling then local widget = self:get_grid().children[#self:get_grid().children] widget:select() - self:emit_signal("scroll", self:get_index_of_widget(widget)) + self:emit_signal("scroll", self:get_index_of_entry(widget.entry)) return else return @@ -425,21 +431,21 @@ function rofi_grid:page_backward(dir) end end - local widget = nil + local entry = nil if self:get_current_page() < self:get_pages_count() then if dir == "up" then - widget = self:get_grid().children[#self:get_grid().children] + entry = self:get_grid().children[#self:get_grid().children] else -- Keep the same row from last page local _, columns = self:get_grid():get_dimension() - widget = self:get_grid():get_widgets_at(pos.row, columns)[1] + entry = self:get_grid():get_widgets_at(pos.row, columns)[1] end elseif self._private.wrap_page_scrolling then - widget = self:get_grid().children[#self:get_grid().children] + entry = self:get_grid().children[#self:get_grid().children] end - widget:select() + entry:select() - self:emit_signal("page::backward", self:get_index_of_widget(self:get_selected_widget())) + self:emit_signal("page::backward", self:get_index_of_entry(self:get_selected_widget().entry)) end function rofi_grid:set_page(page) @@ -459,11 +465,11 @@ function rofi_grid:set_page(page) end end - local widget = self:get_grid():get_widgets_at(1, 1) - if widget then - widget = widget[1] - if widget then - widget:select() + local entry = self:get_grid():get_widgets_at(1, 1) + if entry then + entry = entry[1] + if entry then + entry:select() end end end @@ -484,11 +490,11 @@ function rofi_grid:reset() end end - local widget = self:get_grid():get_widgets_at(1, 1) - if widget then - widget = widget[1] - if widget then - widget:select() + local entry = self:get_grid():get_widgets_at(1, 1) + if entry then + entry = entry[1] + if entry then + entry:select() end end @@ -527,28 +533,32 @@ function rofi_grid:get_matched_entries() return self._private.matched_entries end +function rofi_grid:get_text() + return self._private.text +end + +function rofi_grid:get_selected_widget() + return self._private.selected_widget +end + +function rofi_grid:get_page_of_entry(entry) + return math.floor((self:get_index_of_entry(entry) - 1) / self._private.entries_per_page) + 1 +end + function rofi_grid:get_page_of_index(index) return math.floor((index - 1) / self._private.entries_per_page) + 1 end -function rofi_grid:get_index_of_widget(widget) - for index, matched_widget in ipairs(self:get_matched_entries()) do - if matched_widget.entry == widget.entry then +function rofi_grid:get_index_of_entry(entry) + for index, matched_entry in ipairs(self:get_matched_entries()) do + if matched_entry == entry then return index end end end -function rofi_grid:get_widget_of_index(index) - return self:get_matched_entries()[index].widget -end - -function rofi_grid:get_text() - return self._private.text -end - -function rofi_grid:get_selected_widget() - return self._private.selected_widget +function rofi_grid:get_entry_of_index(index) + return self:get_matched_entries()[index] end local function new() From c6e87c11d0acadbd3c27b7bf19e82238ec4cd1fa Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:24:33 +0200 Subject: [PATCH 130/185] Unbork the default template + fixes some beautiful.colors refs --- widget/app_launcher/init.lua | 117 +++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 54 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 3efbb8c4..966dcb17 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -46,67 +46,76 @@ local function build_widget(self) layout = rofi_grid_widget, lazy_load_widgets = false, widget_template = wibox.widget { - layout = wibox.layout.fixed.vertical, - forced_width = dpi(1000), - forced_height = dpi(1000), - spacing = dpi(15), + widget = wibox.container.margin, + margins = dpi(15), { - widget = text_input_widget, - id = "text_input_role", - reset_on_stop = self.reset_on_hide, - placeholder = self.text_input_placeholder, - unfocus_keys = { }, - unfocus_on_clicked_inside = false, - unfocus_on_clicked_outside = false, - unfocus_on_mouse_leave = false, - unfocus_on_tag_change = false, - unfocus_on_other_text_input_focus = false, - focus_on_subject_mouse_enter = nil, - unfocus_on_subject_mouse_leave = nil, - widget_template = wibox.widget { - widget = wibox.container.background, - forced_height = dpi(120), - bg = self.text_input_bg_color, - { - widget = wibox.container.margin, - margins = dpi(30), + layout = wibox.layout.fixed.vertical, + spacing = dpi(15), + { + widget = text_input_widget, + id = "text_input_role", + forced_width = dpi(650), + forced_height = dpi(60), + reset_on_stop = self.reset_on_hide, + placeholder = self.text_input_placeholder, + unfocus_keys = { }, + unfocus_on_clicked_inside = false, + unfocus_on_clicked_outside = false, + unfocus_on_mouse_leave = false, + unfocus_on_tag_change = false, + unfocus_on_other_text_input_focus = false, + focus_on_subject_mouse_enter = nil, + unfocus_on_subject_mouse_leave = nil, + widget_template = wibox.widget { + widget = wibox.container.background, + bg = self.text_input_bg_color, { - widget = wibox.widget.textbox, - text_color = self.text_input_color, - id = "text_role" + widget = wibox.container.margin, + margins = dpi(15), + { + layout = wibox.layout.stack, + { + widget = wibox.widget.textbox, + id = "placeholder_role", + text = "Search: " + }, + { + widget = wibox.widget.textbox, + id = "text_role" + }, + } } } - } - }, - { - layout = wibox.layout.fixed.horizontal, - spacing = dpi(10), - { - layout = wibox.layout.grid, - id = "grid_role", - orientation = "horizontal", - homogeneous = true, - spacing = dpi(30), - forced_num_cols = self.apps_per_column, - forced_num_rows = self.apps_per_row, }, { - layout = wibox.container.rotate, - direction = 'west', + layout = wibox.layout.fixed.horizontal, + spacing = dpi(10), + { + layout = wibox.layout.grid, + id = "grid_role", + orientation = "horizontal", + homogeneous = true, + spacing = dpi(30), + forced_num_cols = self.apps_per_column, + forced_num_rows = self.apps_per_row, + }, { - widget = wibox.widget.slider, - id = "scrollbar_role", - forced_width = dpi(5), - minimum = 1, - value = 1, - -- bar_shape = helpers.ui.rrect(), - bar_height= 3, - bar_color = beautiful.colors.transparent, - bar_active_color = beautiful.colors.transparent, - handle_width = dpi(50), - handle_color = beautiful.bg_normal, - -- handle_shape = helpers.ui.rrect(), - handle_color = beautiful.colors.on_background + layout = wibox.container.rotate, + direction = 'west', + { + widget = wibox.widget.slider, + id = "scrollbar_role", + forced_width = dpi(5), + forced_height = dpi(10), + minimum = 1, + value = 1, + bar_height= 3, + bar_color = "#00000000", + bar_active_color = "#00000000", + handle_width = dpi(50), + handle_color = beautiful.bg_normal, + handle_color = beautiful.fg_normal + } } } } From df2953fafee5e17b2f4d7015167d15deb28f2ce8 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:32:11 +0200 Subject: [PATCH 131/185] Formatting --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 966dcb17..ea03458f 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -457,7 +457,7 @@ local function new(args) args.app_normal_color = default_value(args.app_normal_color, "#000000") args.app_selected_color = default_value(args.app_selected_color, "#FFFFFF") - args.app_name_normal_color = default_value( args.app_name_normal_color, "#FFFFFF") + args.app_name_normal_color = default_value(args.app_name_normal_color, "#FFFFFF") args.app_name_selected_color = default_value(args.app_name_selected_color, "#000000") local ret = gobject {} From 74f4834dc4a99ba15dc0e2f04cf6e537ac8e573f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:32:14 +0200 Subject: [PATCH 132/185] 15 is enough --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index ea03458f..5444ed0a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -95,7 +95,7 @@ local function build_widget(self) id = "grid_role", orientation = "horizontal", homogeneous = true, - spacing = dpi(30), + spacing = dpi(15), forced_num_cols = self.apps_per_column, forced_num_rows = self.apps_per_row, }, From 45f858d8f7822be1d1ae3e19478f93601c8531fe Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:41:53 +0200 Subject: [PATCH 133/185] Fix not setting the text input color ond default template --- widget/app_launcher/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 5444ed0a..6ea98563 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -56,6 +56,7 @@ local function build_widget(self) id = "text_input_role", forced_width = dpi(650), forced_height = dpi(60), + text_color = self.text_input_color, reset_on_stop = self.reset_on_hide, placeholder = self.text_input_placeholder, unfocus_keys = { }, From 020defb095a9d2e2b0924725a6feab4985065dcc Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 12:43:58 +0200 Subject: [PATCH 134/185] Fix using wrong value --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 6ea98563..e9490c9e 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -453,7 +453,7 @@ local function new(args) args.apps_per_column = default_value(args.apps_per_column, 3) args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") - args.text_input_color = default_value(args.text_input_bg_color, "#FFFFFF") + args.text_input_color = default_value(args.text_input_color, "#FFFFFF") args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") args.app_normal_color = default_value(args.app_normal_color, "#000000") From 63d8f27011f1ca84db6a43b18515f90b60c80cb1 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 18:27:28 +0200 Subject: [PATCH 135/185] Fix not returning the value --- widget/app_launcher/rofi_grid.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 85caecac..4be73247 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -118,7 +118,7 @@ local function entry_widget(self, entry) function entry:select() widget:select() end function entry:unselect() widget:unselect() end - function entry:is_selected() widget:is_selected() end + function entry:is_selected() return widget:is_selected() end entry.widget = widget widget.entry = entry From 9226d7c23a1a63dd71e161487052883c849c51ec Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 12 Mar 2023 18:28:26 +0200 Subject: [PATCH 136/185] Fix varius issues --- widget/app_launcher/init.lua | 110 ++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index e9490c9e..b0bda636 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -176,10 +176,11 @@ local function build_widget(self) widget:connect_signal("button::press", function(_, __, __, button) if button == 1 then - if widget:is_selected() or not self.select_before_spawn then - widget:run() + print(app:is_selected()) + if app:is_selected() or not self.select_before_spawn then + app:run() else - widget:select() + app:select() end end end) @@ -264,6 +265,8 @@ end local function generate_apps(self) local entries = {} + local app_launcher = self + local app_info = Gio.AppInfo local apps = app_info.get_all() for _, app in ipairs(apps) do @@ -306,63 +309,64 @@ local function generate_apps(self) app:launch() end } - table.insert(entries, app) - end - end - end - end - self:get_rofi_grid():set_entries(entries, self.sort_fn) -end + function app:run() + if self.terminal == true then + local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. self.exec) + local class = self.startup_wm_class or self.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) + else + self:launch() + end -function app_launcher:run(widget) - if widget.entry.terminal == true then - local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. widget.entry.exec) - local class = widget.entry.startup_wm_class or widget.entry.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - widget.entry:launch() - end + if app_launcher.hide_on_launch then + app_launcher:hide() + end + end - if self.hide_on_launch then - app_launcher:hide() - end -end + function app:run_or_select() + if self:is_selected() then + self:run() + else + self:select() + end + end -function app_launcher:run_or_select(widget) - if self:get_selected_widget() == widget then - self:run(widget) - else - self:select(widget) - end -end + function app:run_as_root() + if self.terminal == true then + local pid = awful.spawn.with_shell( + AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. + RUN_AS_ROOT_SCRIPT_PATH .. " " .. + self.exec + ) + local class = self.startup_wm_class or self.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) + else + awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. self.exec) + end -function app_launcher:run_as_root(widget) - if widget.entry.terminal == true then - local pid = awful.spawn.with_shell( - AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. - RUN_AS_ROOT_SCRIPT_PATH .. " " .. - widget.entry.exec - ) - local class = widget.entry.startup_wm_class or widget.entry.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. widget.entry.exec) - end + if app_launcher.hide_on_launch then + app_launcher:hide() + end + end - if self.hide_on_launch then - app_launcher:hide() + table.insert(entries, app) + end + end + end end + + self:get_rofi_grid():set_entries(entries, self.sort_fn) end function app_launcher:set_favorites(favorites) From fb688b25a02162d8a7cdcf95f98e023711272bd1 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 00:49:41 +0200 Subject: [PATCH 137/185] Part of the rofi grid widget now --- widget/app_launcher/init.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index b0bda636..afad220a 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -433,11 +433,7 @@ local function new(args) args.hide_on_left_clicked_outside = default_value(args.hide_on_left_clicked_outside, true) args.hide_on_right_clicked_outside = default_value(args.hide_on_right_clicked_outside, true) args.hide_on_launch = default_value(args.hide_on_launch, true) - args.try_to_keep_index_after_searching = default_value(args.try_to_keep_index_after_searching, false) args.reset_on_hide = default_value(args.reset_on_hide, true) - args.wrap_page_scrolling = default_value(args.wrap_page_scrolling, true) - args.wrap_app_scrolling = default_value(args.wrap_app_scrolling, true) - args.lazy_load_widgets = default_value(args.lazy_load_widgets, false) args.type = default_value(args.type, "dock") args.show_on_focused_screen = default_value(args.show_on_focused_screen, true) From 2e3da9006123458e497a402e5fe4fbed4722c869 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 00:49:54 +0200 Subject: [PATCH 138/185] Fix default sorting + formatting --- widget/app_launcher/init.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index afad220a..c7209103 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -241,7 +241,7 @@ local function build_widget(self) end) end -local function default_sort_fn(a, b) +local function default_sort_fn(self, a, b) local is_a_favorite = has_value(self.favorites, a.id) local is_b_favorite = has_value(self.favorites, b.id) @@ -422,13 +422,17 @@ end local function new(args) args = args or {} - args.sort_fn = default_value(args.sort_fn, nil) + local ret = gobject {} + + args.sort_fn = default_value(args.sort_fn, function(a, b) + return default_sort_fn(ret, a, b) + end) + args.sort_alphabetically = default_value(args.sort_alphabetically, true) + args.reverse_sort_alphabetically = default_value(args.reverse_sort_alphabetically, false) args.favorites = default_value(args.favorites, {}) args.skip_names = default_value(args.skip_names, {}) args.skip_commands = default_value(args.skip_commands, {}) args.skip_empty_icons = default_value(args.skip_empty_icons, false) - args.sort_alphabetically = default_value(args.sort_alphabetically, true) - args.reverse_sort_alphabetically = default_value(args.reverse_sort_alphabetically, false) args.select_before_spawn = default_value(args.select_before_spawn, true) args.hide_on_left_clicked_outside = default_value(args.hide_on_left_clicked_outside, true) args.hide_on_right_clicked_outside = default_value(args.hide_on_right_clicked_outside, true) @@ -461,7 +465,6 @@ local function new(args) args.app_name_normal_color = default_value(args.app_name_normal_color, "#FFFFFF") args.app_name_selected_color = default_value(args.app_name_selected_color, "#000000") - local ret = gobject {} gtable.crush(ret, app_launcher, true) gtable.crush(ret, args, true) From 89a1a1f1ece0010f33d0a5cb23c15b5459ba7184 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 00:58:03 +0200 Subject: [PATCH 139/185] These defaults makes more sense for the app launcher, but less if used in other widgets --- widget/app_launcher/init.lua | 9 --------- widget/app_launcher/rofi_grid.lua | 2 +- widget/app_launcher/text_input.lua | 12 ++++++------ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c7209103..5f719db8 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -44,7 +44,6 @@ local function build_widget(self) widget_template = wibox.widget { layout = rofi_grid_widget, - lazy_load_widgets = false, widget_template = wibox.widget { widget = wibox.container.margin, margins = dpi(15), @@ -59,14 +58,6 @@ local function build_widget(self) text_color = self.text_input_color, reset_on_stop = self.reset_on_hide, placeholder = self.text_input_placeholder, - unfocus_keys = { }, - unfocus_on_clicked_inside = false, - unfocus_on_clicked_outside = false, - unfocus_on_mouse_leave = false, - unfocus_on_tag_change = false, - unfocus_on_other_text_input_focus = false, - focus_on_subject_mouse_enter = nil, - unfocus_on_subject_mouse_leave = nil, widget_template = wibox.widget { widget = wibox.container.background, bg = self.text_input_bg_color, diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 4be73247..df4d7fd2 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -576,7 +576,7 @@ local function new() wp.wrap_page_scrolling = true wp.wrap_entry_scrolling = true wp.search_fn = nil - wp.lazy_load_widgets = true + wp.lazy_load_widgets = false wp.text = "" wp.pages_count = 0 diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 07aeb6a2..c7963970 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -746,17 +746,17 @@ local function new() wp.selection_opacity = 0 wp.selecting_text = false - wp.unfocus_keys = { "Escape", "Return" } - wp.unfocus_on_clicked_outside = true + wp.unfocus_keys = { } + wp.unfocus_on_clicked_outside = false wp.unfocus_on_mouse_leave = false - wp.unfocus_on_tag_change = true - wp.unfocus_on_other_text_input_focus = true - wp.unfocus_on_client_focus = true + wp.unfocus_on_tag_change = false + wp.unfocus_on_other_text_input_focus = false + wp.unfocus_on_client_focus = false wp.focus_on_subject_mouse_enter = nil wp.unfocus_on_subject_mouse_leave = nil - wp.reset_on_unfocus = false + wp.reset_on_unfocus = true wp.pattern = nil wp.obscure = false From aa629f6d92bfbfd06801aa0aeb1be22bb6c8a2bd Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 00:58:38 +0200 Subject: [PATCH 140/185] Add text_input_selection_bg prop --- widget/app_launcher/init.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 5f719db8..fb708d70 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -56,6 +56,7 @@ local function build_widget(self) forced_width = dpi(650), forced_height = dpi(60), text_color = self.text_input_color, + selection_bg = self.text_input_selection_bg, reset_on_stop = self.reset_on_hide, placeholder = self.text_input_placeholder, widget_template = wibox.widget { @@ -449,6 +450,7 @@ local function new(args) args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") args.text_input_color = default_value(args.text_input_color, "#FFFFFF") + args.text_input_selection_bg = default_value(args.text_input_selection_bg, "#FF0000") args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") args.app_normal_color = default_value(args.app_normal_color, "#000000") From df599e260d739624343fba5eed88ce2f2e30c41f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 00:58:51 +0200 Subject: [PATCH 141/185] Rename text_input_color to text_input_text_color to avoid confusion --- widget/app_launcher/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index fb708d70..68e21755 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -55,7 +55,7 @@ local function build_widget(self) id = "text_input_role", forced_width = dpi(650), forced_height = dpi(60), - text_color = self.text_input_color, + text_color = self.text_input_text_color, selection_bg = self.text_input_selection_bg, reset_on_stop = self.reset_on_hide, placeholder = self.text_input_placeholder, @@ -449,7 +449,7 @@ local function new(args) args.apps_per_column = default_value(args.apps_per_column, 3) args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") - args.text_input_color = default_value(args.text_input_color, "#FFFFFF") + args.text_input_text_color = default_value(args.text_input_text_color, "#FFFFFF") args.text_input_selection_bg = default_value(args.text_input_selection_bg, "#FF0000") args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") From debaa440427d5465121fe04cb769eaf1a2f72e32 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 01:06:40 +0200 Subject: [PATCH 142/185] Fix calling the method on the wrong object --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 68e21755..033a7f02 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -227,7 +227,7 @@ local function build_widget(self) self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) if key == "Return" then if self:get_rofi_grid():get_selected_widget() ~= nil then - self:get_rofi_grid():get_selected_widget():run() + self:get_rofi_grid():get_selected_widget().entry:run() end end end) From 996fc7cf7980926a455fc923a38d3dd9a1ee619f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 01:56:46 +0200 Subject: [PATCH 143/185] Should check if true --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index c7963970..2bfc0aaa 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -793,7 +793,7 @@ local function new() end) capi.awesome.connect_signal("text_input::focus", function(text_input) - if wp.unfocus_on_other_text_input_focus == false and text_input ~= self then + if wp.unfocus_on_other_text_input_focus and text_input ~= self then widget:unfocus() end end) From ef7cba1b4d6eb7a0f08dfbad9242dea250f2df59 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 02:02:52 +0200 Subject: [PATCH 144/185] Fix checking for the wrong value --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 2bfc0aaa..4b820afe 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -793,7 +793,7 @@ local function new() end) capi.awesome.connect_signal("text_input::focus", function(text_input) - if wp.unfocus_on_other_text_input_focus and text_input ~= self then + if wp.unfocus_on_other_text_input_focus and text_input ~= widget then widget:unfocus() end end) From 31934c1576d2cc9847b94b623414f7f446a2f2c6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 02:28:22 +0200 Subject: [PATCH 145/185] Implement double click to select current word + triple click to select all text --- widget/app_launcher/text_input.lua | 44 +++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 4b820afe..ecc3d61b 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -30,6 +30,7 @@ local text_input = { local properties = { "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "unfocus_on_client_focus", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", + "click_timeout", "reset_on_unfocus", "text_color", "placeholder", "initial", @@ -117,6 +118,25 @@ local function cword_end(s, pos) return i end +local function single_double_triple_tap(self, args) + local wp = self._private + + local current_time = os.time() + if current_time - wp.last_click_time <= wp.click_timeout then + wp.click_count = wp.click_count + 1 + if wp.click_count == 2 then + args.on_double_click() + elseif wp.click_count == 3 then + args.on_triple_click() + wp.click_count = 0 + end + else + args.on_single_click() + wp.click_count = 1 + end + wp.last_click_time = current_time +end + local function run_mousegrabber(self) capi.mousegrabber.run(function(m) if m.buttons[1] then @@ -265,7 +285,17 @@ function text_input:set_widget_template(widget_template) else wp.selecting_text = false end - self:focus() + single_double_triple_tap(self, { + on_single_click = function() + self:focus() + end, + on_double_click = function() + self:set_selection_to_word() + end, + on_triple_click = function() + self:select_all() + end + }) end end) @@ -490,6 +520,14 @@ function text_input:select_all() self:set_selection_end_index(#self:get_text()) end +function text_input:set_selection_to_word() + local word_start_index = cword_start(self:get_text(), self:get_cursor_index() + 1) - 1 + local word_end_index = cword_end(self:get_text(), self:get_cursor_index() + 1) - 1 + + self:set_selection_start_index(word_start_index) + self:set_selection_end_index(word_end_index) +end + function text_input:set_selection_start_index(index) index = math.max(math.min(index, #self:get_text()), 0) @@ -735,6 +773,8 @@ local function new() wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) wp.cursor_index = 0 wp.mode = "insert" + wp.last_click_time = 0 + wp.click_count = 0 wp.cursor_x = 0 wp.cursor_y = 0 @@ -748,6 +788,8 @@ local function new() wp.unfocus_keys = { } wp.unfocus_on_clicked_outside = false + wp.click_timeout = 0.3 + wp.unfocus_on_mouse_leave = false wp.unfocus_on_tag_change = false wp.unfocus_on_other_text_input_focus = false From 98a3b31df47666690a01aadfb575314a5c63e1a6 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 03:42:09 +0200 Subject: [PATCH 146/185] Better naming --- widget/app_launcher/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 033a7f02..657bec12 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -194,9 +194,9 @@ local function build_widget(self) } end widget_template:set_search_fn(function(text, app) - local matched_apps = Gio.DesktopAppInfo.search(text:lower()) - for _, matched_app in ipairs(matched_apps) do - for _, app_id in ipairs(matched_app) do + local matched_groups = Gio.DesktopAppInfo.search(text:lower()) + for _, matched_group in ipairs(matched_groups) do + for _, app_id in ipairs(matched_group) do if app.id == app_id then return true end From ad9c4d795ba32134495bd02d5791bedb0a524607 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 03:42:42 +0200 Subject: [PATCH 147/185] DesktopAppInfo sucks for sorting the matched apps for best results, use fzy instead --- helpers/fzy.lua | 275 +++++++++++++++++++++++++++++++++++ helpers/init.lua | 1 + widget/app_launcher/init.lua | 3 + 3 files changed, 279 insertions(+) create mode 100644 helpers/fzy.lua diff --git a/helpers/fzy.lua b/helpers/fzy.lua new file mode 100644 index 00000000..d80b543f --- /dev/null +++ b/helpers/fzy.lua @@ -0,0 +1,275 @@ +-- The lua implementation of the fzy string matching algorithm + +local SCORE_GAP_LEADING = -0.005 +local SCORE_GAP_TRAILING = -0.005 +local SCORE_GAP_INNER = -0.01 +local SCORE_MATCH_CONSECUTIVE = 1.0 +local SCORE_MATCH_SLASH = 0.9 +local SCORE_MATCH_WORD = 0.8 +local SCORE_MATCH_CAPITAL = 0.7 +local SCORE_MATCH_DOT = 0.6 +local SCORE_MAX = math.huge +local SCORE_MIN = -math.huge +local MATCH_MAX_LENGTH = 1024 + +local fzy = {} + +-- Check if `needle` is a subsequence of the `haystack`. +-- +-- Usually called before `score` or `positions`. +-- +-- Args: +-- needle (string) +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- bool +function fzy.has_match(needle, haystack, case_sensitive) + if not case_sensitive then + needle = string.lower(needle) + haystack = string.lower(haystack) + end + + local j = 1 + for i = 1, string.len(needle) do + j = string.find(haystack, needle:sub(i, i), j, true) + if not j then + return false + else + j = j + 1 + end + end + + return true +end + +local function is_lower(c) + return c:match("%l") +end + +local function is_upper(c) + return c:match("%u") +end + +local function precompute_bonus(haystack) + local match_bonus = {} + + local last_char = "/" + for i = 1, string.len(haystack) do + local this_char = haystack:sub(i, i) + if last_char == "/" or last_char == "\\" then + match_bonus[i] = SCORE_MATCH_SLASH + elseif last_char == "-" or last_char == "_" or last_char == " " then + match_bonus[i] = SCORE_MATCH_WORD + elseif last_char == "." then + match_bonus[i] = SCORE_MATCH_DOT + elseif is_lower(last_char) and is_upper(this_char) then + match_bonus[i] = SCORE_MATCH_CAPITAL + else + match_bonus[i] = 0 + end + + last_char = this_char + end + + return match_bonus +end + +local function compute(needle, haystack, D, M, case_sensitive) + -- Note that the match bonuses must be computed before the arguments are + -- converted to lowercase, since there are bonuses for camelCase. + local match_bonus = precompute_bonus(haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if not case_sensitive then + needle = string.lower(needle) + haystack = string.lower(haystack) + end + + -- Because lua only grants access to chars through substring extraction, + -- get all the characters from the haystack once now, to reuse below. + local haystack_chars = {} + for i = 1, m do + haystack_chars[i] = haystack:sub(i, i) + end + + for i = 1, n do + D[i] = {} + M[i] = {} + + local prev_score = SCORE_MIN + local gap_score = i == n and SCORE_GAP_TRAILING or SCORE_GAP_INNER + local needle_char = needle:sub(i, i) + + for j = 1, m do + if needle_char == haystack_chars[j] then + local score = SCORE_MIN + if i == 1 then + score = ((j - 1) * SCORE_GAP_LEADING) + match_bonus[j] + elseif j > 1 then + local a = M[i - 1][j - 1] + match_bonus[j] + local b = D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE + score = math.max(a, b) + end + D[i][j] = score + prev_score = math.max(score, prev_score + gap_score) + M[i][j] = prev_score + else + D[i][j] = SCORE_MIN + prev_score = prev_score + gap_score + M[i][j] = prev_score + end + end + end +end + +-- Compute a matching score. +-- +-- Args: +-- needle (string): must be a subequence of `haystack`, or the result is +-- undefined. +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- number: higher scores indicate better matches. See also `get_score_min` +-- and `get_score_max`. +function fzy.score(needle, haystack, case_sensitive) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then + return SCORE_MIN + elseif n == m then + return SCORE_MAX + else + local D = {} + local M = {} + compute(needle, haystack, D, M, case_sensitive) + return M[n][m] + end +end + +-- Compute the locations where fzy matches a string. +-- +-- Determine where each character of the `needle` is matched to the `haystack` +-- in the optimal match. +-- +-- Args: +-- needle (string): must be a subequence of `haystack`, or the result is +-- undefined. +-- haystack (string) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- {int,...}: indices, where `indices[n]` is the location of the `n`th +-- character of `needle` in `haystack`. +-- number: the same matching score returned by `score` +function fzy.positions(needle, haystack, case_sensitive) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > m then + return {}, SCORE_MIN + elseif n == m then + local consecutive = {} + for i = 1, n do + consecutive[i] = i + end + return consecutive, SCORE_MAX + end + + local D = {} + local M = {} + compute(needle, haystack, D, M, case_sensitive) + + local positions = {} + local match_required = false + local j = m + for i = n, 1, -1 do + while j >= 1 do + if D[i][j] ~= SCORE_MIN and (match_required or D[i][j] == M[i][j]) then + match_required = (i ~= 1) and (j ~= 1) and ( + M[i][j] == D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE) + positions[i] = j + j = j - 1 + break + else + j = j - 1 + end + end + end + + return positions, M[n][m] +end + +-- Apply `has_match` and `positions` to an array of haystacks. +-- +-- Args: +-- needle (string) +-- haystack ({string, ...}) +-- case_sensitive (bool, optional): defaults to false +-- +-- Returns: +-- {{idx, positions, score}, ...}: an array with one entry per matching line +-- in `haystacks`, each entry giving the index of the line in `haystacks` +-- as well as the equivalent to the return value of `positions` for that +-- line. +function fzy.filter(needle, haystacks, case_sensitive) + local result = {} + + for i, line in ipairs(haystacks) do + if fzy.has_match(needle, line, case_sensitive) then + local p, s = fzy.positions(needle, line, case_sensitive) + table.insert(result, {i, p, s}) + end + end + + return result +end + +-- The lowest value returned by `score`. +-- +-- In two special cases: +-- - an empty `needle`, or +-- - a `needle` or `haystack` larger than than `get_max_length`, +-- the `score` function will return this exact value, which can be used as a +-- sentinel. This is the lowest possible score. +function fzy.get_score_min() + return SCORE_MIN +end + +-- The score returned for exact matches. This is the highest possible score. +function fzy.get_score_max() + return SCORE_MAX +end + +-- The maximum size for which `fzy` will evaluate scores. +function fzy.get_max_length() + return MATCH_MAX_LENGTH +end + +-- The minimum score returned for normal matches. +-- +-- For matches that don't return `get_score_min`, their score will be greater +-- than than this value. +function fzy.get_score_floor() + return MATCH_MAX_LENGTH * SCORE_GAP_INNER +end + +-- The maximum score for non-exact matches. +-- +-- For matches that don't return `get_score_max`, their score will be less than +-- this value. +function fzy.get_score_ceiling() + return MATCH_MAX_LENGTH * SCORE_MATCH_CONSECUTIVE +end + +-- The name of the currently-running implmenetation, "lua" or "native". +function fzy.get_implementation_name() + return "lua" +end + +return fzy \ No newline at end of file diff --git a/helpers/init.lua b/helpers/init.lua index 7e7f5d71..2bed9b3f 100644 --- a/helpers/init.lua +++ b/helpers/init.lua @@ -2,6 +2,7 @@ return { client = require(... .. ".client"), color = require(... .. ".color"), filesystem = require(... .. ".filesystem"), + fzy = require(... .. ".fzy"), icon_theme = require(... .. ".icon_theme"), shape = require(... .. ".shape"), time = require(... .. ".time"), diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 657bec12..cb781eb1 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -203,6 +203,9 @@ local function build_widget(self) end end end) + widget_template:set_search_sort_fn(function(text, a, b) + return helpers.fzy.score(text, a.name) > helpers.fzy.score(text, b.name) + end) self._private.widget = awful.popup { From 26588dc72582120f1cb683a91b6ffcac048aaa7d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 03:45:12 +0200 Subject: [PATCH 148/185] Formatting --- widget/app_launcher/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index cb781eb1..2b507d06 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -7,14 +7,13 @@ local wibox = require("wibox") local beautiful = require("beautiful") local text_input_widget = require(... .. ".text_input") local rofi_grid_widget = require(... .. ".rofi_grid") +local helpers = require(tostring(...):match(".*bling") .. ".helpers") local dpi = beautiful.xresources.apply_dpi local string = string local table = table local math = math local ipairs = ipairs local capi = { screen = screen, mouse = mouse } -local path = ... -local helpers = require(tostring(path):match(".*bling") .. ".helpers") local app_launcher = { mt = {} } From 39a7b9659f308f892270f3b489ad7a665af2626c Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 13 Mar 2023 03:48:55 +0200 Subject: [PATCH 149/185] Is 0.05 enough? --- widget/app_launcher/rofi_grid.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index df4d7fd2..3e72a007 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -582,7 +582,7 @@ local function new() wp.pages_count = 0 wp.current_page = 1 wp.search_timer = gtimer { - timeout = 0.35, + timeout = 0.05, call_now = false, autostart = false, single_shot = true, From ed287c86e2619be64391dc25a8943c1ff6c8877d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 06:44:36 +0200 Subject: [PATCH 150/185] Makes cache smarter when reloding the app list (only add/remove what's needed instead of reseting the entire cache) --- widget/app_launcher/init.lua | 4 +- widget/app_launcher/rofi_grid.lua | 290 +++++++++++++++++------------- 2 files changed, 163 insertions(+), 131 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 2b507d06..f4e52566 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -228,8 +228,8 @@ local function build_widget(self) self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) if key == "Return" then - if self:get_rofi_grid():get_selected_widget() ~= nil then - self:get_rofi_grid():get_selected_widget().entry:run() + if self:get_rofi_grid():get_selected_entry() ~= nil then + self:get_rofi_grid():get_selected_entry():run() end end end) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 3e72a007..b48d7421 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -35,10 +35,21 @@ local function build_properties(prototype, prop_names) end end +local function has_entry(entries, name) + for _, entry in ipairs(entries) do + if entry.name == name then + return true + end + end + + return false +end + local function scroll(self, dir, page_dir) local grid = self:get_grid() if #grid.children < 1 then self._private.selected_widget = nil + self._private.selected_entry = nil return end @@ -74,7 +85,7 @@ local function scroll(self, dir, page_dir) local next_widget = grid.children[next_widget_index] if next_widget then next_widget:select() - self:emit_signal("scroll", self:get_index_of_entry(next_widget.entry)) + self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) else if dir == "up" or dir == "left" then self:page_backward(page_dir or dir) @@ -84,47 +95,43 @@ local function scroll(self, dir, page_dir) end end -local function entry_widget(self, entry) - if self._private.entries_widgets_cache[entry] then - return self._private.entries_widgets_cache[entry] +local function entry_widget(rofi_grid, entry) + if rofi_grid._private.entries_widgets_cache[entry.name] then + return rofi_grid._private.entries_widgets_cache[entry.name] end + local widget = rofi_grid._private.entry_template(entry, rofi_grid) - local widget = self._private.entry_template(entry, self) - - local rofi_grid = self - function widget:select() - if rofi_grid:get_selected_widget() then - rofi_grid:get_selected_widget():unselect() + function entry:select() + if rofi_grid:get_selected_entry() then + rofi_grid:get_selected_entry():unselect() end - rofi_grid._private.selected_widget = self - self.selected = true + + rofi_grid._private.selected_widget = widget + rofi_grid._private.selected_entry = self local index = rofi_grid:get_index_of_entry(entry) - self:emit_signal("select", index) + widget:emit_signal("select", index) rofi_grid:emit_signal("select", index) end - function widget:unselect() - self.selected = false + function entry:unselect() rofi_grid._private.selected_widget = nil + rofi_grid._private.selected_entry = nil - self:emit_signal("unselect") + widget:emit_signal("unselect") rofi_grid:emit_signal("unselect") end - function widget:is_selected() - return rofi_grid._private.selected_widget == self + function entry:is_selected() + return rofi_grid._private.selected_entry == self end - function entry:select() widget:select() end - function entry:unselect() widget:unselect() end - function entry:is_selected() return widget:is_selected() end - entry.widget = widget - widget.entry = entry - - self._private.entries_widgets_cache[entry] = widget + function widget:select() + entry:select() + end - return widget + rofi_grid._private.entries_widgets_cache[entry.name] = widget + return rofi_grid._private.entries_widgets_cache[entry.name] end function rofi_grid:set_widget_template(widget_template) @@ -177,32 +184,49 @@ function rofi_grid:set_widget_template(widget_template) local scrollbar = self:get_scrollbar() if scrollbar then + function scrollbar:set_value(value, instant) + value = math.min(value, self:get_maximum()) + value = math.max(value, self:get_minimum()) + local changed = self._private.value ~= value + + self._private.value = value + + if changed then + self:emit_signal( "property::value", value, instant) + self:emit_signal( "widget::redraw_needed" ) + end + end + self:connect_signal("scroll", function(self, new_index) - scrollbar:set_value(new_index) + scrollbar:set_value(new_index, true) end) self:connect_signal("page::forward", function(self, new_index) - scrollbar:set_value(new_index) + scrollbar:set_value(new_index, true) end) self:connect_signal("page::backward", function(self, new_index) - scrollbar:set_value(new_index) + scrollbar:set_value(new_index, true) end) self:connect_signal("search", function(self, text, new_index) scrollbar:set_maximum(math.max(2, #self:get_matched_entries())) if new_index then - scrollbar:set_value(new_index) + scrollbar:set_value(new_index, true) end end) self:connect_signal("select", function(self, new_index) - scrollbar:set_value(new_index) + scrollbar:set_value(new_index, true) end) scrollbar:connect_signal("property::value", function(_, value, instant) if instant ~= true then - self:set_selected_entry(value) + if value < self:get_index_of_entry(self:get_selected_entry()) then + self:scroll_up() + else + self:scroll_down() + end end end) end @@ -213,36 +237,43 @@ function rofi_grid:set_widget_template(widget_template) self:set_widget(widget_template) end -function rofi_grid:set_entries(entries, sort_fn) - self._private.entries = entries - self:set_sort_fn(sort_fn) - self:reset() - local scrollbar = self:get_scrollbar() - if scrollbar then - scrollbar:set_maximum(#self._private.entries) - scrollbar:set_value(1) - end - - if self._private.lazy_load_widgets == false then - for _, entry in ipairs(self._private.entries) do - self._private.entries_widgets_cache[entry] = entry_widget(self, entry) - end - end -end - function rofi_grid:add_entry(entry) table.insert(self._private.entries, entry) self:set_sort_fn() self:reset() end -function rofi_grid:set_sort_fn(sort_fn) - if sort_fn ~= nil then - self._private.sort_fn = sort_fn +function rofi_grid:set_entries(entries, sort_fn) + local old_entries_count = #self._private.entries + self._private.entries = entries + + if old_entries_count > 0 then + -- Remove old entries that are not in the new entries table + for key, entry in pairs(self._private.entries_widgets_cache) do + if has_entry(self:get_entries(), key) == false and self._private.entries_widgets_cache[key] then + self._private.entries_widgets_cache[key]:emit_signal("removed") + self._private.entries_widgets_cache[key] = nil + end + end end - if self._private.sort_fn ~= nil then - table.sort(self._private.entries, self._private.sort_fn) + + if self:get_lazy_load_widgets() == false then + if old_entries_count > 0 then + -- Add new entries that are not in the old entries table + for _, entry in ipairs(self:get_entries()) do + if self._private.entries_widgets_cache[entry.name] == nil then + self._private.entries_widgets_cache[entry.name] = entry_widget(self, entry) + end + end + else + for _, entry in ipairs(self:get_entries()) do + self._private.entries_widgets_cache[entry.name] = entry_widget(self, entry) + end + end end + + self:set_sort_fn(sort_fn) + self:reset() end function rofi_grid:refresh() @@ -259,6 +290,48 @@ function rofi_grid:refresh() end end +function rofi_grid:reset() + self:get_grid():reset() + self._private.matched_entries = self:get_entries() + self._private.entries_per_page = self._private.max_entries_per_page + self._private.pages_count = math.ceil(#self:get_entries() / self._private.entries_per_page) + self._private.current_page = 1 + + for index, entry in ipairs(self:get_entries()) do + -- Only add the entrys that are part of the first page + if index <= self._private.entries_per_page then + self:get_grid():add(entry_widget(self, entry)) + else + break + end + end + + local widget = self:get_grid():get_widgets_at(1, 1) + if widget then + widget = widget[1] + if widget then + widget:select() + end + end + + local scrollbar = self:get_scrollbar() + if scrollbar then + scrollbar:set_maximum(#self:get_entries()) + scrollbar:set_value(1) + end + + self:get_text_input():set_text("") +end + +function rofi_grid:set_sort_fn(sort_fn) + if sort_fn ~= nil then + self._private.sort_fn = sort_fn + end + if self._private.sort_fn ~= nil then + table.sort(self._private.entries, self._private.sort_fn) + end +end + function rofi_grid:search() local text = self:get_text() local old_pos = self:get_grid():get_widget_position(self:get_selected_widget()) @@ -269,9 +342,9 @@ function rofi_grid:search() self:get_grid():reset() if text == "" then - self._private.matched_entries = self._private.entries + self._private.matched_entries = self:get_entries() else - for _, entry in ipairs(self._private.entries) do + for _, entry in ipairs(self:get_entries()) do text = text:gsub( "%W", "" ) if self._private.search_fn(text:lower(), entry) then table.insert(self:get_matched_entries(), entry) @@ -304,9 +377,9 @@ function rofi_grid:search() -- it will reselect the entry whose index is the same as the entry index that was previously selected -- and if matched_entries.length < current_index it will instead select the entry with the greatest index if self._private.try_to_keep_index_after_searching then - local entry_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) - if entry_at_old_pos and entry_at_old_pos[1] then - entry_at_old_pos[1]:select() + local widget_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) + if widget_at_old_pos and widget_at_old_pos[1] then + widget_at_old_pos[1]:select() else local widget = self:get_grid().children[#self:get_grid().children] widget:select() @@ -317,21 +390,7 @@ function rofi_grid:search() widget:select() end - self:emit_signal("search", self:get_text(), self:get_index_of_entry(self:get_selected_widget().entry)) -end - -function rofi_grid:set_selected_entry(index) - local selected_widget_index = self:get_grid():index(self:get_selected_widget()) - if index == selected_widget_index then - return - end - - local page = self:get_page_of_index(index) - if self:get_current_page() ~= page then - self:set_page(page) - end - - self:get_entry_of_index(index).widget:select() + self:emit_signal("search", self:get_text(), self:get_index_of_entry(self:get_selected_entry())) end function rofi_grid:scroll_up(page_dir) @@ -365,7 +424,7 @@ function rofi_grid:page_forward(dir) elseif self._private.wrap_entry_scrolling then local widget = self:get_grid():get_widgets_at(1, 1)[1] widget:select() - self:emit_signal("scroll", self:get_index_of_entry(widget.entry)) + self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) return else return @@ -384,22 +443,22 @@ function rofi_grid:page_forward(dir) end if self:get_current_page() > 1 or self._private.wrap_page_scrolling then - local entry = nil + local widget = nil if dir == "down" then - entry = self:get_grid():get_widgets_at(1, 1)[1] + widget = self:get_grid():get_widgets_at(1, 1)[1] elseif dir == "right" then - entry = self:get_grid():get_widgets_at(pos.row, 1) - if entry then - entry = entry[1] + widget = self:get_grid():get_widgets_at(pos.row, 1) + if widget then + widget = widget[1] end - if entry == nil then - entry = self:get_grid().children[#self:get_grid().children] + if widget == nil then + widget = self:get_grid().children[#self:get_grid().children] end end - entry:select() + widget:select() end - self:emit_signal("page::forward", self:get_index_of_entry(self:get_selected_widget().entry)) + self:emit_signal("page::forward", self:get_index_of_entry(self:get_selected_entry())) end function rofi_grid:page_backward(dir) @@ -410,7 +469,7 @@ function rofi_grid:page_backward(dir) elseif self._private.wrap_entry_scrolling then local widget = self:get_grid().children[#self:get_grid().children] widget:select() - self:emit_signal("scroll", self:get_index_of_entry(widget.entry)) + self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) return else return @@ -431,28 +490,28 @@ function rofi_grid:page_backward(dir) end end - local entry = nil + local widget = nil if self:get_current_page() < self:get_pages_count() then if dir == "up" then - entry = self:get_grid().children[#self:get_grid().children] + widget = self:get_grid().children[#self:get_grid().children] else -- Keep the same row from last page local _, columns = self:get_grid():get_dimension() - entry = self:get_grid():get_widgets_at(pos.row, columns)[1] + widget = self:get_grid():get_widgets_at(pos.row, columns)[1] end elseif self._private.wrap_page_scrolling then - entry = self:get_grid().children[#self:get_grid().children] + widget = self:get_grid().children[#self:get_grid().children] end - entry:select() + widget:select() - self:emit_signal("page::backward", self:get_index_of_entry(self:get_selected_widget().entry)) + self:emit_signal("page::backward", self:get_index_of_entry(self:get_selected_entry())) end function rofi_grid:set_page(page) self:get_grid():reset() - self._private.matched_entries = self._private.entries + self._private.matched_entries = self:get_entries() self._private.entries_per_page = self._private.max_entries_per_page - self._private.pages_count = math.ceil(#self._private.entries / self._private.entries_per_page) + self._private.pages_count = math.ceil(#self:get_entries() / self._private.entries_per_page) self._private.current_page = page local max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() @@ -465,40 +524,13 @@ function rofi_grid:set_page(page) end end - local entry = self:get_grid():get_widgets_at(1, 1) - if entry then - entry = entry[1] - if entry then - entry:select() - end - end -end - -function rofi_grid:reset() - self:get_grid():reset() - self._private.matched_entries = self._private.entries - self._private.entries_per_page = self._private.max_entries_per_page - self._private.pages_count = math.ceil(#self._private.entries / self._private.entries_per_page) - self._private.current_page = 1 - - for index, entry in ipairs(self._private.entries) do - -- Only add the entrys that are part of the first page - if index <= self._private.entries_per_page then - self:get_grid():add(entry_widget(self, entry)) - else - break - end - end - - local entry = self:get_grid():get_widgets_at(1, 1) - if entry then - entry = entry[1] - if entry then - entry:select() + local widget = self:get_grid():get_widgets_at(1, 1) + if widget then + widget = widget[1] + if widget then + widget:select() end end - - self:get_text_input():set_text("") end function rofi_grid:get_scrollbar() @@ -525,10 +557,6 @@ function rofi_grid:get_current_page() return self._private.current_page end -function rofi_grid:get_entries() - return self._private.entries -end - function rofi_grid:get_matched_entries() return self._private.matched_entries end @@ -541,6 +569,10 @@ function rofi_grid:get_selected_widget() return self._private.selected_widget end +function rofi_grid:get_selected_entry() + return self._private.selected_entry +end + function rofi_grid:get_page_of_entry(entry) return math.floor((self:get_index_of_entry(entry) - 1) / self._private.entries_per_page) + 1 end From 39dc7215d1324d2c66cdb50208b1deed320f8b23 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 07:02:35 +0200 Subject: [PATCH 151/185] Add back the set_selected_entry method with a new name 'scroll_to_index' Using the scroll_up/down for scrolling by dragging the scrollbar was laggy --- widget/app_launcher/rofi_grid.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index b48d7421..7fafa9be 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -222,11 +222,7 @@ function rofi_grid:set_widget_template(widget_template) scrollbar:connect_signal("property::value", function(_, value, instant) if instant ~= true then - if value < self:get_index_of_entry(self:get_selected_entry()) then - self:scroll_up() - else - self:scroll_down() - end + self:scroll_to_index(value) end end) end @@ -393,6 +389,20 @@ function rofi_grid:search() self:emit_signal("search", self:get_text(), self:get_index_of_entry(self:get_selected_entry())) end +function rofi_grid:scroll_to_index(index) + local selected_widget_index = self:get_grid():index(self:get_selected_widget()) + if index == selected_widget_index then + return + end + + local page = self:get_page_of_index(index) + if self:get_current_page() ~= page then + self:set_page(page) + end + + self:get_entry_of_index(index):select() +end + function rofi_grid:scroll_up(page_dir) scroll(self, "up", page_dir) end From 31809096ade587d3ced5ab6d791e72bdd995b3bd Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 07:05:20 +0200 Subject: [PATCH 152/185] Add local pairs --- widget/app_launcher/rofi_grid.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 7fafa9be..8565933b 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -2,6 +2,7 @@ local gtable = require("gears.table") local gtimer = require("gears.timer") local wibox = require("wibox") local ipairs = ipairs +local pairs = pairs local table = table local math = math From 1cec46b2bb97fd833e80e6be0ee3cbe3d653afb0 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 15:01:34 +0200 Subject: [PATCH 153/185] Elseif it --- widget/app_launcher/text_input.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index ecc3d61b..a904e04f 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -205,10 +205,8 @@ local function run_keygrabber(self) self:decremeant_cursor_index() elseif key == "Right" then self:increamant_cursor_index() - else - if key:wlen() == 1 then - self:update_text(key) - end + elseif key:wlen() == 1 then + self:update_text(key) end end end) From a1c93c87a73064c6fae6bafae27e1b3b4616f71b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 15:01:39 +0200 Subject: [PATCH 154/185] Fix not being able to do shift + key --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index a904e04f..af3be76a 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -180,7 +180,7 @@ local function run_keygrabber(self) elseif key == "BackSpace" then self:delete_previous_word() end - elseif mod.Shift then + elseif mod.Shift and key:wlen() ~= 1 then if key =="Left" then self:decremeant_selection_end_index() elseif key == "Right" then From 26c6b2b12c2dbf8ec72e1823abc461d72ccda33b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 15:42:08 +0200 Subject: [PATCH 155/185] Remove print --- widget/app_launcher/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index f4e52566..b000f149 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -167,7 +167,6 @@ local function build_widget(self) widget:connect_signal("button::press", function(_, __, __, button) if button == 1 then - print(app:is_selected()) if app:is_selected() or not self.select_before_spawn then app:run() else From 89198120eae208156579c9223fecf11dafb22f70 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Wed, 15 Mar 2023 20:40:33 +0200 Subject: [PATCH 156/185] Widgets might share the same entries, so don't have any method on the entries objects --- widget/app_launcher/init.lua | 142 +++++++++++++++--------------- widget/app_launcher/rofi_grid.lua | 25 +++--- 2 files changed, 85 insertions(+), 82 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index b000f149..b4e5e194 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -167,10 +167,10 @@ local function build_widget(self) widget:connect_signal("button::press", function(_, __, __, button) if button == 1 then - if app:is_selected() or not self.select_before_spawn then - app:run() + if widget:is_selected() or not self.select_before_spawn then + widget:run() else - app:select() + widget:select() end end end) @@ -191,6 +191,20 @@ local function build_widget(self) end } end + self._private.widget = awful.popup + { + screen = self.screen, + type = self.type, + visible = false, + ontop = true, + placement = self.placement, + border_width = self.border_width, + border_color = self.border_color, + shape = self.shape, + bg = self.bg, + widget = widget_template + } + widget_template:set_search_fn(function(text, app) local matched_groups = Gio.DesktopAppInfo.search(text:lower()) for _, matched_group in ipairs(matched_groups) do @@ -201,34 +215,74 @@ local function build_widget(self) end end end) + widget_template:set_search_sort_fn(function(text, a, b) return helpers.fzy.score(text, a.name) > helpers.fzy.score(text, b.name) end) - self._private.widget = awful.popup - { - screen = self.screen, - type = self.type, - visible = false, - ontop = true, - placement = self.placement, - border_width = self.border_width, - border_color = self.border_color, - shape = self.shape, - bg = self.bg, - widget = widget_template - } + local app_launcher = self + widget_template:connect_signal("entry_widget::add", function(_, widget, app) + function widget:run() + if app.terminal == true then + local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. app.exec) + local class = app.startup_wm_class or app.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) + else + app:launch() + end + + if app_launcher.hide_on_launch then + app_launcher:hide() + end + end + + function widget:run_or_select() + if self:is_selected() then + self:run() + else + self:select() + end + end + + function widget:run_as_root() + if app.terminal == true then + local pid = awful.spawn.with_shell( + AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. + RUN_AS_ROOT_SCRIPT_PATH .. " " .. + app.exec + ) + local class = app.startup_wm_class or app.name + awful.spawn.with_shell(string.format( + [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], + pid, + class, + class + )) + else + awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. app.exec) + end + + if app_launcher.hide_on_launch then + app_launcher:hide() + end + end + end) self:get_text_input():connect_signal("key::press", function(_, mod, key, cmd) if key == "Escape" then - self:hide() + app_launcher:hide() end end) self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) if key == "Return" then - if self:get_rofi_grid():get_selected_entry() ~= nil then - self:get_rofi_grid():get_selected_entry():run() + if app_launcher:get_rofi_grid():get_selected_widget() ~= nil then + app_launcher:get_rofi_grid():get_selected_widget():run() end end end) @@ -303,56 +357,6 @@ local function generate_apps(self) end } - function app:run() - if self.terminal == true then - local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. self.exec) - local class = self.startup_wm_class or self.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - self:launch() - end - - if app_launcher.hide_on_launch then - app_launcher:hide() - end - end - - function app:run_or_select() - if self:is_selected() then - self:run() - else - self:select() - end - end - - function app:run_as_root() - if self.terminal == true then - local pid = awful.spawn.with_shell( - AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. - RUN_AS_ROOT_SCRIPT_PATH .. " " .. - self.exec - ) - local class = self.startup_wm_class or self.name - awful.spawn.with_shell(string.format( - [[xdotool search --sync --all --pid %s --name '.*' set_window --classname "%s" set_window --class "%s"]], - pid, - class, - class - )) - else - awful.spawn(RUN_AS_ROOT_SCRIPT_PATH .. " " .. self.exec) - end - - if app_launcher.hide_on_launch then - app_launcher:hide() - end - end - table.insert(entries, app) end end diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 8565933b..ba1f1832 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -102,20 +102,20 @@ local function entry_widget(rofi_grid, entry) end local widget = rofi_grid._private.entry_template(entry, rofi_grid) - function entry:select() - if rofi_grid:get_selected_entry() then - rofi_grid:get_selected_entry():unselect() + function widget:select() + if rofi_grid:get_selected_widget() then + rofi_grid:get_selected_widget():unselect() end - rofi_grid._private.selected_widget = widget - rofi_grid._private.selected_entry = self + rofi_grid._private.selected_widget = self + rofi_grid._private.selected_entry = entry local index = rofi_grid:get_index_of_entry(entry) - widget:emit_signal("select", index) + self:emit_signal("select", index) rofi_grid:emit_signal("select", index) end - function entry:unselect() + function widget:unselect() rofi_grid._private.selected_widget = nil rofi_grid._private.selected_entry = nil @@ -123,13 +123,11 @@ local function entry_widget(rofi_grid, entry) rofi_grid:emit_signal("unselect") end - function entry:is_selected() - return rofi_grid._private.selected_entry == self + function widget:is_selected() + return rofi_grid._private.selected_widget == self end - function widget:select() - entry:select() - end + rofi_grid:emit_signal("entry_widget::add", widget, entry) rofi_grid._private.entries_widgets_cache[entry.name] = widget return rofi_grid._private.entries_widgets_cache[entry.name] @@ -401,7 +399,8 @@ function rofi_grid:scroll_to_index(index) self:set_page(page) end - self:get_entry_of_index(index):select() + local index_within_page = index - (page - 1) * self._private.entries_per_page + self:get_grid().children[index_within_page]:select() end function rofi_grid:scroll_up(page_dir) From 6c76dc9b17174477e55876f73adef2c27ecaccec Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 02:04:06 +0200 Subject: [PATCH 157/185] Fix varius input handling issues with the text input widget --- widget/app_launcher/text_input.lua | 152 +++++++++++++++++------------ 1 file changed, 87 insertions(+), 65 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index af3be76a..b6ea89b1 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -28,7 +28,9 @@ local text_input = { } local properties = { - "unfocus_keys", "unfocus_on_clicked_outside", "unfocus_on_mouse_leave", "unfocus_on_tag_change", "unfocus_on_client_focus", + "unfocus_keys", + "unfocus_on_root_clicked", "unfocus_on_client_clicked", "unfocus_on_client_focus", + "unfocus_on_mouse_leave", "unfocus_on_tag_change", "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", "click_timeout", "reset_on_unfocus", @@ -118,35 +120,39 @@ local function cword_end(s, pos) return i end +local function set_mouse_cursor(cursor) + capi.root.cursor(cursor) + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = cursor + end +end + local function single_double_triple_tap(self, args) local wp = self._private - local current_time = os.time() - if current_time - wp.last_click_time <= wp.click_timeout then - wp.click_count = wp.click_count + 1 - if wp.click_count == 2 then - args.on_double_click() - elseif wp.click_count == 3 then - args.on_triple_click() - wp.click_count = 0 - end - else - args.on_single_click() - wp.click_count = 1 + if wp.click_timer == nil then + wp.click_timer = gtimer { + timeout = wp.click_timeout, + autostart = false, + call_now = false, + single_shot = true, + callback = function() + wp.click_count = 0 + end + } end - wp.last_click_time = current_time -end -local function run_mousegrabber(self) - capi.mousegrabber.run(function(m) - if m.buttons[1] then - if capi.mouse.current_widget ~= self and self.unfocus_on_clicked_outside then - self:unfocus() - return false - end - end - return true - end, "xterm") + wp.click_timer:again() + wp.click_count = wp.click_count + 1 + if wp.click_count == 1 then + args.on_single_click() + elseif wp.click_count == 2 then + args.on_double_click() + elseif wp.click_count == 3 then + args.on_triple_click() + wp.click_count = 0 + end end local function run_keygrabber(self) @@ -269,23 +275,10 @@ function text_input:set_widget_template(widget_template) wp.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) if button == 1 then - wp.press_pos = { lx = lx, ly = ly } - wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } - find_widgets_result.drawable:connect_signal("mouse::move", on_drag) - end - end) - - wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) - if button == 1 then - find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) - if not wp.selecting_text then - self:set_cursor_index_from_x_y(lx, ly) - else - wp.selecting_text = false - end single_double_triple_tap(self, { on_single_click = function() self:focus() + self:set_cursor_index_from_x_y(lx, ly) end, on_double_click = function() self:set_selection_to_word() @@ -294,26 +287,27 @@ function text_input:set_widget_template(widget_template) self:select_all() end }) + + wp.press_pos = { lx = lx, ly = ly } + wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } + find_widgets_result.drawable:connect_signal("mouse::move", on_drag) end end) - wp.text_widget:connect_signal("mouse::enter", function() - capi.root.cursor("xterm") - local wibox = capi.mouse.current_wibox - if wibox then - wibox.cursor = "xterm" + wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) + if button == 1 then + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + wp.selecting_text = false end end) + wp.text_widget:connect_signal("mouse::enter", function() + set_mouse_cursor("xterm") + end) + wp.text_widget:connect_signal("mouse::leave", function(_, find_widgets_result) if self:get_focused() == false then - capi.root.cursor("left_ptr") - local wibox = capi.mouse.current_wibox - if wibox then - wibox.cursor = "left_ptr" - end - elseif wp.unfocus_on_clicked_outside then - run_mousegrabber(self) + set_mouse_cursor("left_ptr") end find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) @@ -514,11 +508,19 @@ function text_input:hide_selection() end function text_input:select_all() + if self:get_text() == "" then + return + end + self:set_selection_start_index(0) self:set_selection_end_index(#self:get_text()) end function text_input:set_selection_to_word() + if self:get_text() == "" then + return + end + local word_start_index = cword_start(self:get_text(), self:get_cursor_index() + 1) - 1 local word_end_index = cword_end(self:get_text(), self:get_cursor_index() + 1) - 1 @@ -610,6 +612,10 @@ function text_input:set_cursor_index(index) local layout = self:get_text_widget()._private.layout local strong_pos, weak_pos = layout:get_cursor_pos(index) if strong_pos then + if strong_pos == self._private.cursor_index and self._private.mode == "insert" then + return + end + self._private.cursor_index = index self._private.mode = "insert" @@ -696,10 +702,16 @@ end function text_input:focus() local wp = self._private + if self:get_focused() == true then return end + -- Do it first, so the cursor won't change back when unfocus was called on the focused text input + capi.awesome.emit_signal("text_input::focus", self) + + set_mouse_cursor("xterm") + if self:get_mode() == "insert" then self:show_cursor() end @@ -722,7 +734,6 @@ function text_input:focus() wp.focused = true self:emit_signal("focus") - capi.awesome.emit_signal("text_input::focus", self) end function text_input:unfocus() @@ -731,24 +742,16 @@ function text_input:unfocus() return end + set_mouse_cursor("left_ptr") self:hide_cursor() self:hide_selection() if self.reset_on_unfocus == true then self:set_text("") end - awful.keygrabber.stop(wp.keygrabber) - if wp.unfocus_on_clicked_outside then - capi.mousegrabber.stop() - end - capi.root.cursor("left_ptr") - local wibox = capi.mouse.current_wibox - if wibox then - wibox.cursor = "left_ptr" - end + awful.keygrabber.stop(wp.keygrabber) wp.focused = false self:emit_signal("unfocus", self:get_text()) - capi.awesome.emit_signal("text_input::unfocus", self) end function text_input:toggle() @@ -771,7 +774,6 @@ local function new() wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) wp.cursor_index = 0 wp.mode = "insert" - wp.last_click_time = 0 wp.click_count = 0 wp.cursor_x = 0 @@ -784,10 +786,11 @@ local function new() wp.selection_opacity = 0 wp.selecting_text = false - wp.unfocus_keys = { } - wp.unfocus_on_clicked_outside = false wp.click_timeout = 0.3 + wp.unfocus_keys = { } + wp.unfocus_on_root_clicked = false + wp.unfocus_on_client_clicked = false wp.unfocus_on_mouse_leave = false wp.unfocus_on_tag_change = false wp.unfocus_on_other_text_input_focus = false @@ -844,6 +847,25 @@ local function new() end end) + awful.mouse.append_global_mousebindings({ + awful.button({"Any"}, 1, function() + if wp.unfocus_on_root_clicked then + widget:unfocus() + end + end), + awful.button({"Any"}, 3, function() + if wp.unfocus_on_root_clicked then + widget:unfocus() + end + end) + }) + + capi.client.connect_signal("button::press", function() + if wp.unfocus_on_client_clicked then + widget:unfocus() + end + end) + return widget end From 566a5a9bd99beee21d39fd1d67ab1de4115612f4 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:37:55 +0200 Subject: [PATCH 158/185] Add context to the unfocus signal --- widget/app_launcher/text_input.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index b6ea89b1..b91b96af 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -736,7 +736,7 @@ function text_input:focus() self:emit_signal("focus") end -function text_input:unfocus() +function text_input:unfocus(context) local wp = self._private if self:get_focused() == false then return @@ -751,7 +751,7 @@ function text_input:unfocus() awful.keygrabber.stop(wp.keygrabber) wp.focused = false - self:emit_signal("unfocus", self:get_text()) + self:emit_signal("unfocus", context or "normal", self:get_text()) end function text_input:toggle() From 92df6d97958df14ca82b47e2de6df086be218969 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:47:02 +0200 Subject: [PATCH 159/185] Fix getting the same value twice --- widget/app_launcher/text_input.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index b91b96af..e8b5fd51 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -234,8 +234,9 @@ function text_input:set_widget_template(widget_template) end function wp.text_widget:draw(context, cr, width, height) - -- Selection bg local _, logical_rect = self._private.layout:get_pixel_extents() + + -- Selection bg cr:set_source(gcolor.change_opacity(wp.selection_bg, wp.selection_opacity)) cr:rectangle( wp.selection_start_x, @@ -246,7 +247,6 @@ function text_input:set_widget_template(widget_template) cr:fill() -- Cursor - local _, logical_rect = self._private.layout:get_pixel_extents() cr:set_source(gcolor.change_opacity(wp.cursor_bg, wp.cursor_opacity)) cr:set_line_width(wp.cursor_width) cr:move_to(wp.cursor_x, logical_rect.y - 3) From 0c6a1ca4f5d1b9b947d0dfab0179e5106f528044 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:47:16 +0200 Subject: [PATCH 160/185] Better handling for when clicked position it outsidd the bounds --- widget/app_launcher/text_input.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index e8b5fd51..ac6f02ca 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -638,7 +638,12 @@ function text_input:set_cursor_index_from_x_y(x, y) if index then self:set_cursor_index(index) else - self:set_cursor_index(#self:get_text()) + local _, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.width then + self:set_cursor_index(0) + else + self:set_cursor_index(#self:get_text()) + end end end From 070150e90232c5599873c62e06b66276bbee2d81 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:56:08 +0200 Subject: [PATCH 161/185] Fix cursor blinking too fast sometimes --- widget/app_launcher/text_input.lua | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index ac6f02ca..4174479f 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -724,19 +724,25 @@ function text_input:focus() run_keygrabber(self) if wp.cursor_blink then - gtimer.start_new(wp.cursor_blink_rate, function() - if self:get_focused() == true then - if self._private.cursor_opacity == 1 then - self:hide_cursor() - elseif self:get_mode() == "insert" then - self:show_cursor() + if wp.cursor_blink_timer == nil then + wp.cursor_blink_timer = gtimer { + timeout = wp.cursor_blink_rate, + autostart = false, + call_now = false, + single_shot = false, + callback = function() + if self._private.cursor_opacity == 1 then + self:hide_cursor() + elseif self:get_mode() == "insert" then + self:show_cursor() + end end - return true - end - return false - end) + } + end + wp.cursor_blink_timer:start() end + wp.focused = true self:emit_signal("focus") end @@ -749,6 +755,7 @@ function text_input:unfocus(context) set_mouse_cursor("left_ptr") self:hide_cursor() + wp.cursor_blink_timer:stop() self:hide_selection() if self.reset_on_unfocus == true then self:set_text("") From ff8af1521826616489f1a662909673c9dcf4d700 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:56:30 +0200 Subject: [PATCH 162/185] Fix #92df6d97958df14ca82b47e2de6df086be218969 --- widget/app_launcher/text_input.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 4174479f..c47f8fab 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -638,8 +638,8 @@ function text_input:set_cursor_index_from_x_y(x, y) if index then self:set_cursor_index(index) else - local _, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() - if x < logical_rect.width then + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then self:set_cursor_index(0) else self:set_cursor_index(#self:get_text()) From 6a52784b46a640e2aaa2d8f109d4ec968faa070b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 04:57:38 +0200 Subject: [PATCH 163/185] Hide scrollbar when grid is empty --- widget/app_launcher/rofi_grid.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index ba1f1832..1db784a3 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -311,8 +311,13 @@ function rofi_grid:reset() local scrollbar = self:get_scrollbar() if scrollbar then - scrollbar:set_maximum(#self:get_entries()) - scrollbar:set_value(1) + if #self:get_grid().children <= 0 then + self:get_scrollbar():set_visible(false) + else + self:get_scrollbar():set_visible(true) + scrollbar:set_maximum(#self:get_entries()) + scrollbar:set_value(1) + end end self:get_text_input():set_text("") @@ -385,6 +390,12 @@ function rofi_grid:search() widget:select() end + if #self:get_grid().children <= 0 then + self:get_scrollbar():set_visible(false) + else + self:get_scrollbar():set_visible(true) + end + self:emit_signal("search", self:get_text(), self:get_index_of_entry(self:get_selected_entry())) end From d09bf3ba7e672e91dc8c15cf8f291f19324b783f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 05:22:56 +0200 Subject: [PATCH 164/185] I was keeping the cache, but overriding the entries table which caused the widgets to keep a refrence to an entry that was now nil --- widget/app_launcher/rofi_grid.lua | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 1db784a3..9edd24b8 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -238,30 +238,25 @@ function rofi_grid:add_entry(entry) self:reset() end -function rofi_grid:set_entries(entries, sort_fn) - local old_entries_count = #self._private.entries - self._private.entries = entries - - if old_entries_count > 0 then - -- Remove old entries that are not in the new entries table - for key, entry in pairs(self._private.entries_widgets_cache) do - if has_entry(self:get_entries(), key) == false and self._private.entries_widgets_cache[key] then +function rofi_grid:set_entries(new_entries, sort_fn) + -- Remove old entries that are not in the new entries table + for index, entry in pairs(self:get_entries()) do + if has_entry(new_entries, entry.name) == false then + table.remove(self._private.entries, index) + + if self._private.entries_widgets_cache[key] then self._private.entries_widgets_cache[key]:emit_signal("removed") self._private.entries_widgets_cache[key] = nil end end end - if self:get_lazy_load_widgets() == false then - if old_entries_count > 0 then - -- Add new entries that are not in the old entries table - for _, entry in ipairs(self:get_entries()) do - if self._private.entries_widgets_cache[entry.name] == nil then - self._private.entries_widgets_cache[entry.name] = entry_widget(self, entry) - end - end - else - for _, entry in ipairs(self:get_entries()) do + -- Add new entries that are not in the old entries table + for _, entry in ipairs(new_entries) do + if has_entry(self:get_entries(), entry.name) == false then + table.insert(self._private.entries, entry) + + if self:get_lazy_load_widgets() == false then self._private.entries_widgets_cache[entry.name] = entry_widget(self, entry) end end From 331712ff7589d5dd7653be5432f4c19ccacb26e1 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 08:09:03 +0200 Subject: [PATCH 165/185] Handle favorites directly in the rofi grid widget --- widget/app_launcher/init.lua | 35 +----------------------- widget/app_launcher/rofi_grid.lua | 45 +++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index b4e5e194..c19021d3 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -288,27 +288,6 @@ local function build_widget(self) end) end -local function default_sort_fn(self, a, b) - local is_a_favorite = has_value(self.favorites, a.id) - local is_b_favorite = has_value(self.favorites, b.id) - - -- Sort the favorite apps first - if is_a_favorite and not is_b_favorite then - return true - elseif not is_a_favorite and is_b_favorite then - return false - end - - -- Sort alphabetically if specified - if self.sort_alphabetically then - return a.name:lower() < b.name:lower() - elseif self.reverse_sort_alphabetically then - return b.name:lower() > a.name:lower() - else - return true - end -end - local function generate_apps(self) local entries = {} @@ -363,13 +342,7 @@ local function generate_apps(self) end end - self:get_rofi_grid():set_entries(entries, self.sort_fn) -end - -function app_launcher:set_favorites(favorites) - self.favorites = favorites - self:get_rofi_grid():set_sort_fn(self.sort_fn) - self:refresh() + self:get_rofi_grid():set_entries(entries) end function app_launcher:show() @@ -421,12 +394,6 @@ local function new(args) local ret = gobject {} - args.sort_fn = default_value(args.sort_fn, function(a, b) - return default_sort_fn(ret, a, b) - end) - args.sort_alphabetically = default_value(args.sort_alphabetically, true) - args.reverse_sort_alphabetically = default_value(args.reverse_sort_alphabetically, false) - args.favorites = default_value(args.favorites, {}) args.skip_names = default_value(args.skip_names, {}) args.skip_commands = default_value(args.skip_commands, {}) args.skip_empty_icons = default_value(args.skip_empty_icons, false) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 9edd24b8..7b61e303 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -9,7 +9,7 @@ local math = math local rofi_grid = { mt = {} } local properties = { - "entries", "page", "lazy_load_widgets", + "entries", "favorites", "page", "lazy_load_widgets", "widget_template", "entry_template", "sort_fn", "search_fn", "search_sort_fn", "sort_alphabetically","reverse_sort_alphabetically,", @@ -36,6 +36,15 @@ local function build_properties(prototype, prop_names) end end +local function has_value(tab, val) + for _, value in ipairs(tab) do + if val:lower():find(value:lower(), 1, true) then + return true + end + end + return false +end + local function has_entry(entries, name) for _, entry in ipairs(entries) do if entry.name == name then @@ -133,6 +142,27 @@ local function entry_widget(rofi_grid, entry) return rofi_grid._private.entries_widgets_cache[entry.name] end +local function default_sort_fn(self, a, b) + local is_a_favorite = has_value(self.favorites, a.id) + local is_b_favorite = has_value(self.favorites, b.id) + + -- Sort the favorite apps first + if is_a_favorite and not is_b_favorite then + return true + elseif not is_a_favorite and is_b_favorite then + return false + end + + -- Sort alphabetically if specified + if self.sort_alphabetically then + return a.name:lower() < b.name:lower() + elseif self.reverse_sort_alphabetically then + return b.name:lower() > a.name:lower() + else + return true + end +end + function rofi_grid:set_widget_template(widget_template) self._private.text_input = widget_template:get_children_by_id("text_input_role")[1] self._private.grid = widget_template:get_children_by_id("grid_role")[1] @@ -266,6 +296,14 @@ function rofi_grid:set_entries(new_entries, sort_fn) self:reset() end +function rofi_grid:set_favorites(favorites) + self._private.favorites = favorites + if self:get_entries() and #self:get_entries() > 1 then + self:set_sort_fn() + self:refresh() + end +end + function rofi_grid:refresh() local max_entry_index_to_include = self._private.entries_per_page * self:get_current_page() local min_entry_index_to_include = max_entry_index_to_include - self._private.entries_per_page @@ -617,7 +655,10 @@ local function new() wp.entries_widgets_cache = setmetatable({}, { __mode = "v" }) wp.entries = {} - wp.sort_fn = nil + wp.favorites = {} + wp.sort_fn = function(a, b) + return default_sort_fn(widget, a, b) + end wp.sort_alphabetically = true wp.reverse_sort_alphabetically = false wp.try_to_keep_index_after_searching = false From aa25cfd4b6fcafb4895566fe3df51f0f3501a330 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Thu, 16 Mar 2023 08:20:00 +0200 Subject: [PATCH 166/185] Add default_search_sort_fn and default_search_fn to rofi grid --- widget/app_launcher/init.lua | 4 ---- widget/app_launcher/rofi_grid.lua | 25 ++++++++++++++++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index c19021d3..e43ad677 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -216,10 +216,6 @@ local function build_widget(self) end end) - widget_template:set_search_sort_fn(function(text, a, b) - return helpers.fzy.score(text, a.name) > helpers.fzy.score(text, b.name) - end) - local app_launcher = self widget_template:connect_signal("entry_widget::add", function(_, widget, app) function widget:run() diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 7b61e303..58500701 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -1,5 +1,8 @@ local gtable = require("gears.table") local gtimer = require("gears.timer") +local helpers = require(tostring(...):match(".*bling") .. ".helpers") +local fzy_has_match = helpers.fzy.has_match +local fzy_score = helpers.fzy.score local wibox = require("wibox") local ipairs = ipairs local pairs = pairs @@ -142,6 +145,17 @@ local function entry_widget(rofi_grid, entry) return rofi_grid._private.entries_widgets_cache[entry.name] end +local function default_search_sort_fn(text, a, b) + return fzy_score(text, a.name) > fzy_score(text, b.name) +end + +local function default_search_fn(text, entry) + if fzy_has_match(text, entry.name) then + return true + end + return false +end + local function default_sort_fn(self, a, b) local is_a_favorite = has_value(self.favorites, a.id) local is_b_favorite = has_value(self.favorites, b.id) @@ -656,15 +670,20 @@ local function new() wp.entries = {} wp.favorites = {} + wp.sort_alphabetically = true + wp.reverse_sort_alphabetically = false wp.sort_fn = function(a, b) return default_sort_fn(widget, a, b) end - wp.sort_alphabetically = true - wp.reverse_sort_alphabetically = false + wp.search_fn = function(text, entry) + return default_search_fn(text, entry) + end + wp.search_sort_fn = function(text, a, b) + return default_search_sort_fn(text, a, b) + end wp.try_to_keep_index_after_searching = false wp.wrap_page_scrolling = true wp.wrap_entry_scrolling = true - wp.search_fn = nil wp.lazy_load_widgets = false wp.text = "" From 35611e44d2750178e57d3264e9741e10a7d00251 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 17 Mar 2023 00:55:57 +0200 Subject: [PATCH 167/185] Add context to the select signal --- widget/app_launcher/rofi_grid.lua | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 58500701..e04071d2 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -97,7 +97,7 @@ local function scroll(self, dir, page_dir) local next_widget = grid.children[next_widget_index] if next_widget then - next_widget:select() + next_widget:select("scroll") self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) else if dir == "up" or dir == "left" then @@ -114,7 +114,7 @@ local function entry_widget(rofi_grid, entry) end local widget = rofi_grid._private.entry_template(entry, rofi_grid) - function widget:select() + function widget:select(context) if rofi_grid:get_selected_widget() then rofi_grid:get_selected_widget():unselect() end @@ -123,8 +123,8 @@ local function entry_widget(rofi_grid, entry) rofi_grid._private.selected_entry = entry local index = rofi_grid:get_index_of_entry(entry) - self:emit_signal("select", index) - rofi_grid:emit_signal("select", index) + self:emit_signal("select", index, context) + rofi_grid:emit_signal("select", index, context) end function widget:unselect() @@ -352,7 +352,7 @@ function rofi_grid:reset() if widget then widget = widget[1] if widget then - widget:select() + widget:select("new_page") end end @@ -426,15 +426,15 @@ function rofi_grid:search() if self._private.try_to_keep_index_after_searching then local widget_at_old_pos = self:get_grid():get_widgets_at(old_pos.row, old_pos.col) if widget_at_old_pos and widget_at_old_pos[1] then - widget_at_old_pos[1]:select() + widget_at_old_pos[1]:select("search") else local widget = self:get_grid().children[#self:get_grid().children] - widget:select() + widget:select("search") end -- Otherwise select the first entry on the list elseif self:get_grid().children[1] then local widget = self:get_grid().children[1] - widget:select() + widget:select("search") end if #self:get_grid().children <= 0 then @@ -458,7 +458,7 @@ function rofi_grid:scroll_to_index(index) end local index_within_page = index - (page - 1) * self._private.entries_per_page - self:get_grid().children[index_within_page]:select() + self:get_grid().children[index_within_page]:select("scroll") end function rofi_grid:scroll_up(page_dir) @@ -491,7 +491,7 @@ function rofi_grid:page_forward(dir) max_entry_index_to_include = self._private.entries_per_page elseif self._private.wrap_entry_scrolling then local widget = self:get_grid():get_widgets_at(1, 1)[1] - widget:select() + widget:select("new_page") self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) return else @@ -523,7 +523,7 @@ function rofi_grid:page_forward(dir) widget = self:get_grid().children[#self:get_grid().children] end end - widget:select() + widget:select("new_page") end self:emit_signal("page::forward", self:get_index_of_entry(self:get_selected_entry())) @@ -536,7 +536,7 @@ function rofi_grid:page_backward(dir) self._private.current_page = self:get_pages_count() elseif self._private.wrap_entry_scrolling then local widget = self:get_grid().children[#self:get_grid().children] - widget:select() + widget:select("new_page") self:emit_signal("scroll", self:get_index_of_entry(self:get_selected_entry())) return else @@ -570,7 +570,7 @@ function rofi_grid:page_backward(dir) elseif self._private.wrap_page_scrolling then widget = self:get_grid().children[#self:get_grid().children] end - widget:select() + widget:select("new_page") self:emit_signal("page::backward", self:get_index_of_entry(self:get_selected_entry())) end @@ -596,7 +596,7 @@ function rofi_grid:set_page(page) if widget then widget = widget[1] if widget then - widget:select() + widget:select("new_page") end end end From bbba4cdf42fbd0d2bca730f4f56344d3a5febf64 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 17 Mar 2023 03:05:20 +0200 Subject: [PATCH 168/185] Make use of #35611e --- widget/app_launcher/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index e43ad677..6ea817bb 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -170,7 +170,7 @@ local function build_widget(self) if widget:is_selected() or not self.select_before_spawn then widget:run() else - widget:select() + widget:select("press") end end end) @@ -237,11 +237,11 @@ local function build_widget(self) end end - function widget:run_or_select() + function widget:run_or_select(context) if self:is_selected() then self:run() else - self:select() + self:select(context) end end From 053cb47b74be1855dada557c50e0f3c4df809709 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Fri, 17 Mar 2023 11:27:52 +0200 Subject: [PATCH 169/185] Improve text selection handling when cursor it outside text extents --- widget/app_launcher/text_input.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index c47f8fab..3b640135 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -584,7 +584,12 @@ function text_input:set_selection_start_index_from_x_y(x, y) if index then self:set_selection_start_index(index) else - self:set_selection_start_index(#self:get_text()) + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then + self:set_selection_start_index(0) + else + self:set_selection_start_index(#self:get_text()) + end end end @@ -593,6 +598,13 @@ function text_input:set_selection_end_index_from_x_y(x, y) local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) if index then self:set_selection_end_index(index + trailing) + else + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then + self:set_selection_end_index(0) + else + self:set_selection_end_index(#self:get_text()) + end end end From cbc6f46cce01cf8d13ffc3f2fc7d11c2d0e49da2 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 18 Mar 2023 03:26:23 +0200 Subject: [PATCH 170/185] Can't select text if empty --- widget/app_launcher/text_input.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 3b640135..a0d0ef38 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -529,6 +529,10 @@ function text_input:set_selection_to_word() end function text_input:set_selection_start_index(index) + if #self:get_text() == 0 then + return + end + index = math.max(math.min(index, #self:get_text()), 0) local layout = self:get_text_widget()._private.layout @@ -548,6 +552,10 @@ function text_input:set_selection_start_index(index) end function text_input:set_selection_end_index(index) + if #self:get_text() == 0 then + return + end + index = math.max(math.min(index, #self:get_text()), 0) local layout = self:get_text_widget()._private.layout From 7d4afecb3c8e11938271784762cc99c051be5f9d Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 18 Mar 2023 03:26:31 +0200 Subject: [PATCH 171/185] Change default click timeout to 0.1 --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index a0d0ef38..df458564 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -818,7 +818,7 @@ local function new() wp.selection_opacity = 0 wp.selecting_text = false - wp.click_timeout = 0.3 + wp.click_timeout = 0.1 wp.unfocus_keys = { } wp.unfocus_on_root_clicked = false From 36cd1581cbe93b5e05975a195aa74758790b5331 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sat, 18 Mar 2023 03:27:16 +0200 Subject: [PATCH 172/185] This wasn't working if the mouse left the text input before releasing LMB --- widget/app_launcher/text_input.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index df458564..54043d4b 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -264,11 +264,10 @@ function text_input:set_widget_template(widget_template) end local function on_drag(_, lx, ly) - if not wp.selecting_text and (lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly) then - self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) - self:set_selection_end_index(wp.selection_start) - wp.selecting_text = true - elseif wp.selecting_text then + if (lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly) then + if self:get_mode() ~= "overwrite" then + self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) + end self:set_selection_end_index_from_x_y(lx - wp.offset.x, ly - wp.offset.y) end end @@ -297,7 +296,6 @@ function text_input:set_widget_template(widget_template) wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) if button == 1 then find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) - wp.selecting_text = false end end) @@ -816,7 +814,6 @@ local function new() wp.selection_start_y = 0 wp.selection_end_y = 0 wp.selection_opacity = 0 - wp.selecting_text = false wp.click_timeout = 0.1 From 86f5382bfd1f9f73c31b950c6d8aa264497552eb Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Mar 2023 03:45:27 +0200 Subject: [PATCH 173/185] Only set the mode to overwrite if there was actual text being selected --- widget/app_launcher/text_input.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 54043d4b..dd235667 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -264,7 +264,7 @@ function text_input:set_widget_template(widget_template) end local function on_drag(_, lx, ly) - if (lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly) then + if lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly then if self:get_mode() ~= "overwrite" then self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) end @@ -537,14 +537,8 @@ function text_input:set_selection_start_index(index) local strong_pos, weak_pos = layout:get_caret_pos(index) if strong_pos then self._private.selection_start = index - self._private.mode = "overwrite" - self._private.selection_start_x = strong_pos.x / Pango.SCALE self._private.selection_start_y = strong_pos.y / Pango.SCALE - - self:show_selection() - self:hide_cursor() - self:get_text_widget():emit_signal("widget::redraw_needed") end end @@ -559,9 +553,15 @@ function text_input:set_selection_end_index(index) local layout = self:get_text_widget()._private.layout local strong_pos, weak_pos = layout:get_caret_pos(index) if strong_pos then + if self:get_mode() ~= "overwrite" and index ~= self._private.selection_start then + self._private.mode = "overwrite" + self:show_selection() + self:hide_cursor() + end + + self._private.selection_end = index self._private.selection_end_x = strong_pos.x / Pango.SCALE self._private.selection_end_y = strong_pos.y / Pango.SCALE - self._private.selection_end = index self:get_text_widget():emit_signal("widget::redraw_needed") end end From 69dd122d58e9cde422e4c84f489d3373d7bcc4ae Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Mar 2023 04:00:40 +0200 Subject: [PATCH 174/185] Increase click timeout --- widget/app_launcher/text_input.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index dd235667..17b8831f 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -815,7 +815,7 @@ local function new() wp.selection_end_y = 0 wp.selection_opacity = 0 - wp.click_timeout = 0.1 + wp.click_timeout = 0.2 wp.unfocus_keys = { } wp.unfocus_on_root_clicked = false From ee5438c0f26ae7ae403aa6130ba2e8ba6ff661a0 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 19 Mar 2023 08:35:01 +0200 Subject: [PATCH 175/185] Fix cursor reblinking when already in insert mode and pressing the text input again --- widget/app_launcher/text_input.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 17b8831f..1e3a8d2e 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -634,6 +634,10 @@ function text_input:set_cursor_index(index) return end + if self:get_focused() and self:get_mode() ~= "insert" then + self:show_cursor() + end + self._private.cursor_index = index self._private.mode = "insert" From 80b9062839d9c28d0c16a53f622dded40f46e7e7 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Sun, 26 Mar 2023 18:22:59 +0300 Subject: [PATCH 176/185] Implmement the select_or_exec function at the rofi grid widget level --- widget/app_launcher/init.lua | 21 ++++----------------- widget/app_launcher/rofi_grid.lua | 11 +++++++++++ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 6ea817bb..4a6fd3dd 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -167,11 +167,7 @@ local function build_widget(self) widget:connect_signal("button::press", function(_, __, __, button) if button == 1 then - if widget:is_selected() or not self.select_before_spawn then - widget:run() - else - widget:select("press") - end + widget:select_or_exec() end end) @@ -218,7 +214,7 @@ local function build_widget(self) local app_launcher = self widget_template:connect_signal("entry_widget::add", function(_, widget, app) - function widget:run() + function widget:exec() if app.terminal == true then local pid = awful.spawn.with_shell(AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. app.exec) local class = app.startup_wm_class or app.name @@ -237,15 +233,7 @@ local function build_widget(self) end end - function widget:run_or_select(context) - if self:is_selected() then - self:run() - else - self:select(context) - end - end - - function widget:run_as_root() + function widget:exec_as_root() if app.terminal == true then local pid = awful.spawn.with_shell( AWESOME_SENSIBLE_TERMINAL_SCRIPT_PATH .. " -e " .. @@ -278,7 +266,7 @@ local function build_widget(self) self:get_text_input():connect_signal("key::release", function(_, mod, key, cmd) if key == "Return" then if app_launcher:get_rofi_grid():get_selected_widget() ~= nil then - app_launcher:get_rofi_grid():get_selected_widget():run() + app_launcher:get_rofi_grid():get_selected_widget():exec() end end end) @@ -393,7 +381,6 @@ local function new(args) args.skip_names = default_value(args.skip_names, {}) args.skip_commands = default_value(args.skip_commands, {}) args.skip_empty_icons = default_value(args.skip_empty_icons, false) - args.select_before_spawn = default_value(args.select_before_spawn, true) args.hide_on_left_clicked_outside = default_value(args.hide_on_left_clicked_outside, true) args.hide_on_right_clicked_outside = default_value(args.hide_on_right_clicked_outside, true) args.hide_on_launch = default_value(args.hide_on_launch, true) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index e04071d2..c18c2c38 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -139,6 +139,17 @@ local function entry_widget(rofi_grid, entry) return rofi_grid._private.selected_widget == self end + -- Should be overriden + function widget:exec() end + + function widget:select_or_exec(context) + if self:is_selected() then + self:exec() + else + self:select(context) + end + end + rofi_grid:emit_signal("entry_widget::add", widget, entry) rofi_grid._private.entries_widgets_cache[entry.name] = widget From 92ff78314b598957e3702b91295b4d70c33ddc6f Mon Sep 17 00:00:00 2001 From: Ksaper Date: Tue, 28 Mar 2023 02:06:57 +0300 Subject: [PATCH 177/185] Remove whitespace --- widget/app_launcher/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 4a6fd3dd..0bd5879c 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -406,7 +406,7 @@ local function new(args) args.text_input_bg_color = default_value(args.text_input_bg_color, "#000000") args.text_input_text_color = default_value(args.text_input_text_color, "#FFFFFF") args.text_input_selection_bg = default_value(args.text_input_selection_bg, "#FF0000") - args.text_input_placeholder = default_value(args.text_input_placeholder, "Search: ") + args.text_input_placeholder = default_value(args.text_input_placeholder, "Search:") args.app_normal_color = default_value(args.app_normal_color, "#000000") args.app_selected_color = default_value(args.app_selected_color, "#FFFFFF") From e80db42445fd3de150827c1aeaf1414d8ddb400a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 3 Apr 2023 17:11:47 +0300 Subject: [PATCH 178/185] Forgot to commit this --- widget/app_launcher/text_input.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 1e3a8d2e..db4385e3 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -644,9 +644,6 @@ function text_input:set_cursor_index(index) self._private.cursor_x = strong_pos.x / Pango.SCALE self._private.cursor_y = strong_pos.y / Pango.SCALE - if self:get_focused() then - self:show_cursor() - end self:hide_selection() self:get_text_widget():emit_signal("widget::redraw_needed") From 0452b69a4d2631348759a32eb9b3a44c154c0cf5 Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 3 Apr 2023 17:12:11 +0300 Subject: [PATCH 179/185] Fix text obscure --- widget/app_launcher/text_input.lua | 50 +++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index db4385e3..90b755fb 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -225,7 +225,7 @@ function text_input:set_widget_template(widget_template) wp.text_widget.forced_width = math.huge local text_draw = wp.text_widget.draw if self:get_initial() then - self:set_text(self:get_initial()) + self:replace_text(self:get_initial()) end local placeholder_widget = widget_template:get_children_by_id("placeholder_role") @@ -333,13 +333,18 @@ function text_input:set_pattern(pattern) self._private.pattern = text_input.patterns[pattern] end +function text_input:set_obscure(obscure) + self._private.obscure = obscure + self:set_text(self:get_text()) +end + function text_input:toggle_obscure() self:set_obscure(not self._private.obscure) end function text_input:set_initial(initial) self._private.initial = initial - self:set_text(initial) + self:replace_text(initial) end function text_input:update_text(text) @@ -351,10 +356,25 @@ function text_input:update_text(text) end function text_input:set_text(text) - local text_widget = self:get_text_widget() + self._private.text_buffer = text + + if self:get_obscure() then + self:get_text_widget():set_text(string.rep("*", #text)) + else + self:get_text_widget():set_text(text) + end +end - text_widget:set_text(text) - if text_widget:get_text() == "" then +function text_input:replace_text(text) + self._private.text_buffer = text + + if self:get_obscure() then + self:set_text(string.rep("*", #text)) + else + self:set_text(text) + end + + if self:get_text() == "" then self:set_cursor_index(0) else self:set_cursor_index(#text) @@ -372,12 +392,12 @@ function text_input:insert_text(text) if wp.pattern then new_text = new_text:match(wp.pattern) if new_text then - self:get_text_widget():set_text(new_text) + self:set_text(new_text) self:set_cursor_index(self:get_cursor_index() + #text) self:emit_signal("property::text", self:get_text()) end else - self:get_text_widget():set_text(new_text) + self:set_text(new_text) self:set_cursor_index(self:get_cursor_index() + #text) self:emit_signal("property::text", self:get_text()) end @@ -400,12 +420,12 @@ function text_input:overwrite_text(text) if wp.pattern then new_text = new_text:match(wp.pattern) if new_text then - self:get_text_widget():set_text(new_text) + self:set_text(new_text) self:set_cursor_index(#left_text) self:emit_signal("property::text", self:get_text()) end else - self:get_text_widget():set_text(new_text) + self:set_text(new_text) self:set_cursor_index(#left_text) self:emit_signal("property::text", self:get_text()) end @@ -441,7 +461,7 @@ function text_input:delete_next_word() local left_text = old_text:sub(1, cursor_index) local right_text = old_text:sub(cword_end(old_text, cursor_index + 1)) - self:get_text_widget():set_text(left_text .. right_text) + self:set_text(left_text .. right_text) self:emit_signal("property::text", self:get_text()) end @@ -451,7 +471,7 @@ function text_input:delete_previous_word() local wstart = cword_start(old_text, cursor_index + 1) - 1 local left_text = old_text:sub(1, wstart) local right_text = old_text:sub(cursor_index + 1) - self:get_text_widget():set_text(left_text .. right_text) + self:set_text(left_text .. right_text) self:set_cursor_index(wstart) self:emit_signal("property::text", self:get_text()) end @@ -470,7 +490,7 @@ function text_input:delete_text_before_cursor() local old_text = self:get_text() local left_text = old_text:sub(1, cursor_index - 1) local right_text = old_text:sub(cursor_index + 1) - self:get_text_widget():set_text(left_text .. right_text) + self:set_text(left_text .. right_text) self:set_cursor_index(cursor_index - 1) self:emit_signal("property::text", self:get_text()) end @@ -482,13 +502,13 @@ function text_input:delete_text_after_cursor() local old_text = self:get_text() local left_text = old_text:sub(1, cursor_index) local right_text = old_text:sub(cursor_index + 2) - self:get_text_widget():set_text(left_text .. right_text) + self:set_text(left_text .. right_text) self:emit_signal("property::text", self:get_text()) end end function text_input:get_text() - return self:get_text_widget():get_text() + return self._private.text_buffer or "" end function text_input:get_text_widget() @@ -777,7 +797,7 @@ function text_input:unfocus(context) wp.cursor_blink_timer:stop() self:hide_selection() if self.reset_on_unfocus == true then - self:set_text("") + self:replace_text("") end awful.keygrabber.stop(wp.keygrabber) From d8897a2616776188e00e260190e5c0c61963f92b Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 3 Apr 2023 21:48:57 +0300 Subject: [PATCH 180/185] Fix cursor position after overwriting text --- widget/app_launcher/text_input.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index 90b755fb..b5c00cd5 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -421,12 +421,12 @@ function text_input:overwrite_text(text) new_text = new_text:match(wp.pattern) if new_text then self:set_text(new_text) - self:set_cursor_index(#left_text) + self:set_cursor_index(#left_text + 1) self:emit_signal("property::text", self:get_text()) end else self:set_text(new_text) - self:set_cursor_index(#left_text) + self:set_cursor_index(#left_text + 1) self:emit_signal("property::text", self:get_text()) end end From c44dc018c7b4afdf7a41286b475c5ede1c32166a Mon Sep 17 00:00:00 2001 From: Ksaper Date: Mon, 3 Apr 2023 22:46:45 +0300 Subject: [PATCH 181/185] Improve dragging handling --- widget/app_launcher/text_input.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/text_input.lua b/widget/app_launcher/text_input.lua index b5c00cd5..7b60fe86 100644 --- a/widget/app_launcher/text_input.lua +++ b/widget/app_launcher/text_input.lua @@ -12,6 +12,7 @@ local gtimer = require("gears.timer") local gcolor = require("gears.color") local wibox = require("wibox") local beautiful = require("beautiful") +local abs = math.abs local ipairs = ipairs local string = string local capi = { @@ -264,11 +265,12 @@ function text_input:set_widget_template(widget_template) end local function on_drag(_, lx, ly) - if lx ~= wp.press_pos.lx or ly ~= wp.press_pos.ly then + lx, ly = wp.hierarchy:get_matrix_from_device():transform_point(lx, ly) + if abs(lx - wp.press_pos.lx) > 2 or abs(ly - wp.press_pos.ly) > 2 then if self:get_mode() ~= "overwrite" then self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) end - self:set_selection_end_index_from_x_y(lx - wp.offset.x, ly - wp.offset.y) + self:set_selection_end_index_from_x_y(lx, ly) end end @@ -288,7 +290,7 @@ function text_input:set_widget_template(widget_template) }) wp.press_pos = { lx = lx, ly = ly } - wp.offset = { x = find_widgets_result.x, y = find_widgets_result.y } + wp.hierarchy = find_widgets_result.hierarchy find_widgets_result.drawable:connect_signal("mouse::move", on_drag) end end) From 17d171eb1d906c918c186a9f3de34ef603555dd4 Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Wed, 28 Jun 2023 17:52:41 +0300 Subject: [PATCH 182/185] Fix using the wrong key --- widget/app_launcher/rofi_grid.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index c18c2c38..162c45a4 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -299,9 +299,9 @@ function rofi_grid:set_entries(new_entries, sort_fn) if has_entry(new_entries, entry.name) == false then table.remove(self._private.entries, index) - if self._private.entries_widgets_cache[key] then - self._private.entries_widgets_cache[key]:emit_signal("removed") - self._private.entries_widgets_cache[key] = nil + if self._private.entries_widgets_cache[entry.name] then + self._private.entries_widgets_cache[entry.name]:emit_signal("removed") + self._private.entries_widgets_cache[entry.name] = nil end end end From f96d8371177b1a8da34905cd7ff7bd4137b9471f Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Wed, 28 Jun 2023 17:57:28 +0300 Subject: [PATCH 183/185] Refresh the app list everytime :show() is called Watching the /usr/share/applications directory doesn't work across all distors, i.e on NixOS That folder exists on /run/current-system/sw/share/applications or (~/.nix-profile/share/applications for user apps), but it's just a syslink to a path in the store, and that path changes across rebuilts. --- widget/app_launcher/init.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 0bd5879c..412a82df 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -272,11 +272,9 @@ local function build_widget(self) end) end -local function generate_apps(self) +function app_launcher:refresh_app_list() local entries = {} - local app_launcher = self - local app_info = Gio.AppInfo local apps = app_info.get_all() for _, app in ipairs(apps) do @@ -330,6 +328,8 @@ local function generate_apps(self) end function app_launcher:show() + self:refresh_app_list() + if self.show_on_focused_screen then self:get_widget().screen = awful.screen.focused() end @@ -457,14 +457,8 @@ local function new(args) ) end - awful.spawn.easy_async_with_shell("pkill -f 'inotifywait -m /usr/share/applications -e modify'", function() - awful.spawn.with_line_callback("inotifywait -m /usr/share/applications -e modify", {stdout = function() - generate_apps(ret) - end}) - end) - build_widget(ret) - generate_apps(ret) + ret:refresh_app_list() return ret end From b4cb55a52513aba92d7547cacc2034436440a166 Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Wed, 28 Jun 2023 21:39:58 +0300 Subject: [PATCH 184/185] Use uid instead of name to identify if an entry already exists Some apps have the same name, i.e nemo and nautilus are both called 'Files' Before this fix only one of them would end up in the app launcher --- widget/app_launcher/init.lua | 1 + widget/app_launcher/rofi_grid.lua | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 412a82df..333ec3fb 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -302,6 +302,7 @@ function app_launcher:refresh_app_list() end local app = { + uid = desktop_app_info:get_filename(), desktop_app_info = desktop_app_info, path = desktop_app_info:get_filename(), id = id, diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 162c45a4..70ba0441 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -48,9 +48,9 @@ local function has_value(tab, val) return false end -local function has_entry(entries, name) - for _, entry in ipairs(entries) do - if entry.name == name then +local function has_entry(entries, entry) + for _, looped_entry in ipairs(entries) do + if looped_entry.uid == entry.uid then return true end end @@ -109,8 +109,8 @@ local function scroll(self, dir, page_dir) end local function entry_widget(rofi_grid, entry) - if rofi_grid._private.entries_widgets_cache[entry.name] then - return rofi_grid._private.entries_widgets_cache[entry.name] + if rofi_grid._private.entries_widgets_cache[entry.uid] then + return rofi_grid._private.entries_widgets_cache[entry.uid] end local widget = rofi_grid._private.entry_template(entry, rofi_grid) @@ -152,8 +152,8 @@ local function entry_widget(rofi_grid, entry) rofi_grid:emit_signal("entry_widget::add", widget, entry) - rofi_grid._private.entries_widgets_cache[entry.name] = widget - return rofi_grid._private.entries_widgets_cache[entry.name] + rofi_grid._private.entries_widgets_cache[entry.uid] = widget + return rofi_grid._private.entries_widgets_cache[entry.uid] end local function default_search_sort_fn(text, a, b) @@ -296,23 +296,23 @@ end function rofi_grid:set_entries(new_entries, sort_fn) -- Remove old entries that are not in the new entries table for index, entry in pairs(self:get_entries()) do - if has_entry(new_entries, entry.name) == false then + if has_entry(new_entries, entry) == false then table.remove(self._private.entries, index) - if self._private.entries_widgets_cache[entry.name] then - self._private.entries_widgets_cache[entry.name]:emit_signal("removed") - self._private.entries_widgets_cache[entry.name] = nil + if self._private.entries_widgets_cache[entry.uid] then + self._private.entries_widgets_cache[entry.uid]:emit_signal("removed") + self._private.entries_widgets_cache[entry.uid] = nil end end end -- Add new entries that are not in the old entries table for _, entry in ipairs(new_entries) do - if has_entry(self:get_entries(), entry.name) == false then + if has_entry(self:get_entries(), entry) == false then table.insert(self._private.entries, entry) if self:get_lazy_load_widgets() == false then - self._private.entries_widgets_cache[entry.name] = entry_widget(self, entry) + self._private.entries_widgets_cache[entry.uid] = entry_widget(self, entry) end end end From 0f0a7f73b5f93f511f3f1133955168b497b8b23e Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Sat, 19 Oct 2024 21:23:41 +0300 Subject: [PATCH 185/185] Fix crash caused by api change renaming some grid props --- widget/app_launcher/init.lua | 4 ++-- widget/app_launcher/rofi_grid.lua | 10 +++++----- widget/tabbed_misc/custom_tasklist.lua | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/widget/app_launcher/init.lua b/widget/app_launcher/init.lua index 333ec3fb..806ff022 100644 --- a/widget/app_launcher/init.lua +++ b/widget/app_launcher/init.lua @@ -88,8 +88,8 @@ local function build_widget(self) orientation = "horizontal", homogeneous = true, spacing = dpi(15), - forced_num_cols = self.apps_per_column, - forced_num_rows = self.apps_per_row, + column_count = self.apps_per_column, + row_count = self.apps_per_row, }, { layout = wibox.container.rotate, diff --git a/widget/app_launcher/rofi_grid.lua b/widget/app_launcher/rofi_grid.lua index 70ba0441..147c0edd 100644 --- a/widget/app_launcher/rofi_grid.lua +++ b/widget/app_launcher/rofi_grid.lua @@ -73,23 +73,23 @@ local function scroll(self, dir, page_dir) if grid_orientation == "horizontal" then next_widget_index = grid:index(self:get_selected_widget()) - 1 elseif grid_orientation == "vertical" then - next_widget_index = grid:index(self:get_selected_widget()) - grid.forced_num_cols + next_widget_index = grid:index(self:get_selected_widget()) - grid.column_count end elseif dir == "down" then if grid_orientation == "horizontal" then next_widget_index = grid:index(self:get_selected_widget()) + 1 elseif grid_orientation == "vertical" then - next_widget_index = grid:index(self:get_selected_widget()) + grid.forced_num_cols + next_widget_index = grid:index(self:get_selected_widget()) + grid.column_count end elseif dir == "left" then if grid_orientation == "horizontal" then - next_widget_index = grid:index(self:get_selected_widget()) - grid.forced_num_rows + next_widget_index = grid:index(self:get_selected_widget()) - grid.row_count elseif grid_orientation == "vertical" then next_widget_index = grid:index(self:get_selected_widget()) - 1 end elseif dir == "right" then if grid_orientation == "horizontal" then - next_widget_index = grid:index(self:get_selected_widget()) + grid.forced_num_rows + next_widget_index = grid:index(self:get_selected_widget()) + grid.row_count elseif grid_orientation == "vertical" then next_widget_index = grid:index(self:get_selected_widget()) + 1 end @@ -281,7 +281,7 @@ function rofi_grid:set_widget_template(widget_template) end) end - self._private.max_entries_per_page = self:get_grid().forced_num_cols * self:get_grid().forced_num_rows + self._private.max_entries_per_page = self:get_grid().column_count * self:get_grid().row_count self._private.entries_per_page = self._private.max_entries_per_page self:set_widget(widget_template) diff --git a/widget/tabbed_misc/custom_tasklist.lua b/widget/tabbed_misc/custom_tasklist.lua index ac223771..bfa93e9d 100644 --- a/widget/tabbed_misc/custom_tasklist.lua +++ b/widget/tabbed_misc/custom_tasklist.lua @@ -11,14 +11,14 @@ local function tabobj_support(self, c, index, clients) end local group = c.bling_tabbed - + -- TODO: Allow customization here local layout_v = wibox.widget { vertical_spacing = dpi(2), horizontal_spacing = dpi(2), layout = wibox.layout.grid.horizontal, - forced_num_rows = 2, - forced_num_cols = 2, + row_count = 2, + column_count = 2, homogeneous = true }