Finished work on application launcher and fixed some bugs + cleanup and documentation
This commit is contained in:
@@ -2,16 +2,18 @@
|
|||||||
-- This is the application launcher --
|
-- This is the application launcher --
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
-- Awesome Libs
|
-- Awesome libs
|
||||||
local awful = require("awful")
|
local abutton = require("awful.button")
|
||||||
local Gio = require("lgi").Gio
|
local aspawn = require("awful.spawn")
|
||||||
local gfilesystem = require("gears").filesystem
|
|
||||||
local dpi = require("beautiful").xresources.apply_dpi
|
|
||||||
local gears = require("gears")
|
|
||||||
local wibox = require("wibox")
|
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
|
local dpi = require("beautiful").xresources.apply_dpi
|
||||||
|
local gcolor = require("gears.color")
|
||||||
|
local gfilesystem = require("gears").filesystem
|
||||||
|
local Gio = require("lgi").Gio
|
||||||
local gtable = require("gears.table")
|
local gtable = require("gears.table")
|
||||||
|
local wibox = require("wibox")
|
||||||
|
|
||||||
|
-- Third party libs
|
||||||
local json = require("src.lib.json-lua.json-lua")
|
local json = require("src.lib.json-lua.json-lua")
|
||||||
local cm = require("src.modules.context_menu.init")
|
local cm = require("src.modules.context_menu.init")
|
||||||
|
|
||||||
@@ -20,10 +22,24 @@ local capi = {
|
|||||||
mouse = mouse,
|
mouse = mouse,
|
||||||
}
|
}
|
||||||
|
|
||||||
local icondir = gears.filesystem.get_configuration_dir() .. "src/assets/icons/context_menu/"
|
local icondir = gfilesystem.get_configuration_dir() .. "src/assets/icons/context_menu/"
|
||||||
|
|
||||||
local application_grid = { mt = {} }
|
local application_grid = { mt = {} }
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Make sure that the config folder exists and the applications.json
|
||||||
|
This is done here once because it would be unnecessary to do it for every instance
|
||||||
|
]]
|
||||||
|
do
|
||||||
|
local dir = gfilesystem.get_configuration_dir() .. "src/config"
|
||||||
|
gfilesystem.make_directories(dir)
|
||||||
|
dir = dir .. "/applications.json"
|
||||||
|
if not gfilesystem.file_readable(dir) then
|
||||||
|
aspawn("touch " .. dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--#region wibox.widget.base boilerplate
|
||||||
function application_grid:layout(_, width, height)
|
function application_grid:layout(_, width, height)
|
||||||
if self._private.widget then
|
if self._private.widget then
|
||||||
return { base.place_widget_at(self._private.widget, 0, 0, width, height) }
|
return { base.place_widget_at(self._private.widget, 0, 0, width, height) }
|
||||||
@@ -44,6 +60,12 @@ function application_grid:get_widget()
|
|||||||
return self._private.widget
|
return self._private.widget
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Calculate the levenshtein distance between two strings to determine how similar they are
|
||||||
|
I stole this from a random github gist
|
||||||
|
]]
|
||||||
local function levenshtein_distance(str1, str2)
|
local function levenshtein_distance(str1, str2)
|
||||||
local len1 = string.len(str1)
|
local len1 = string.len(str1)
|
||||||
local len2 = string.len(str2)
|
local len2 = string.len(str2)
|
||||||
@@ -85,12 +107,17 @@ local function levenshtein_distance(str1, str2)
|
|||||||
return matrix[len1][len2]
|
return matrix[len1][len2]
|
||||||
end
|
end
|
||||||
|
|
||||||
function application_grid:get_applications_from_file()
|
---Gets all .desktop files found and filters them based on their visibility
|
||||||
|
---It used Gio.AppInfo and Gio.DesktopAppInfo to get the information
|
||||||
|
---@return table
|
||||||
|
local function get_applications_from_file()
|
||||||
local list = {}
|
local list = {}
|
||||||
local app_info = Gio.AppInfo
|
local app_info = Gio.AppInfo
|
||||||
|
--Get all .desktop files
|
||||||
local apps = app_info.get_all()
|
local apps = app_info.get_all()
|
||||||
for _, app in ipairs(apps) do
|
for _, app in ipairs(apps) do
|
||||||
if app.should_show(app) then -- check no display
|
if app.should_show(app) then -- check no display
|
||||||
|
--Create a new .desktop object
|
||||||
local desktop_app_info = Gio.DesktopAppInfo.new(app_info.get_id(app))
|
local desktop_app_info = Gio.DesktopAppInfo.new(app_info.get_id(app))
|
||||||
local app_widget = wibox.widget {
|
local app_widget = wibox.widget {
|
||||||
{
|
{
|
||||||
@@ -100,7 +127,9 @@ function application_grid:get_applications_from_file()
|
|||||||
{ -- Icon
|
{ -- Icon
|
||||||
valign = "center",
|
valign = "center",
|
||||||
halign = "center",
|
halign = "center",
|
||||||
image = Get_gicon_path(app_info.get_icon(app)),
|
image = Get_gicon_path(app_info.get_icon(app)) or
|
||||||
|
Get_gicon_path(app_info.get_icon(app),
|
||||||
|
Gio.DesktopAppInfo.get_string(desktop_app_info, "X-AppImage-Old-Icon")) or "",
|
||||||
resize = true,
|
resize = true,
|
||||||
widget = wibox.widget.imagebox
|
widget = wibox.widget.imagebox
|
||||||
},
|
},
|
||||||
@@ -138,17 +167,14 @@ function application_grid:get_applications_from_file()
|
|||||||
categories = Gio.DesktopAppInfo.get_categories(desktop_app_info) or "",
|
categories = Gio.DesktopAppInfo.get_categories(desktop_app_info) or "",
|
||||||
terminal = Gio.DesktopAppInfo.get_string(desktop_app_info, "Terminal") == "true",
|
terminal = Gio.DesktopAppInfo.get_string(desktop_app_info, "Terminal") == "true",
|
||||||
actions = Gio.DesktopAppInfo.list_actions(desktop_app_info),
|
actions = Gio.DesktopAppInfo.list_actions(desktop_app_info),
|
||||||
|
desktop_file = Gio.DesktopAppInfo.get_filename(desktop_app_info) or "",
|
||||||
border_color = Theme_config.application_launcher.application.border_color,
|
border_color = Theme_config.application_launcher.application.border_color,
|
||||||
border_width = Theme_config.application_launcher.application.border_width,
|
border_width = Theme_config.application_launcher.application.border_width,
|
||||||
bg = Theme_config.application_launcher.application.bg,
|
bg = Theme_config.application_launcher.application.bg,
|
||||||
fg = Theme_config.application_launcher.application.fg,
|
fg = Theme_config.application_launcher.application.fg,
|
||||||
desktop_file = Gio.DesktopAppInfo.get_filename(desktop_app_info) or "",
|
shape = Theme_config.application_launcher.application.shape,
|
||||||
shape = function(cr, width, height)
|
|
||||||
gears.shape.rounded_rect(cr, width, height, dpi(8))
|
|
||||||
end,
|
|
||||||
widget = wibox.container.background
|
widget = wibox.container.background
|
||||||
}
|
}
|
||||||
|
|
||||||
local context_menu = cm {
|
local context_menu = cm {
|
||||||
widget_template = wibox.widget {
|
widget_template = wibox.widget {
|
||||||
{
|
{
|
||||||
@@ -183,34 +209,33 @@ function application_grid:get_applications_from_file()
|
|||||||
entries = {
|
entries = {
|
||||||
{
|
{
|
||||||
name = "Execute as sudo",
|
name = "Execute as sudo",
|
||||||
icon = gears.color.recolor_image(icondir .. "launch.svg",
|
icon = gcolor.recolor_image(icondir .. "launch.svg",
|
||||||
Theme_config.application_launcher.application.cm_icon_color),
|
Theme_config.application_launcher.application.cm_icon_color),
|
||||||
callback = function()
|
callback = function()
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
awful.spawn("/home/crylia/.config/awesome/src/scripts/start_as_admin.sh " .. app_widget.exec)
|
aspawn("/home/crylia/.config/awesome/src/scripts/start_as_admin.sh " .. app_widget.exec)
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "Pin to dock",
|
name = "Pin to dock",
|
||||||
icon = gears.color.recolor_image(icondir .. "pin.svg",
|
icon = gcolor.recolor_image(icondir .. "pin.svg",
|
||||||
Theme_config.application_launcher.application.cm_icon_color),
|
Theme_config.application_launcher.application.cm_icon_color),
|
||||||
callback = function()
|
callback = function()
|
||||||
local dir = gears.filesystem.get_configuration_dir() .. "src/config"
|
-- Open dock.js and read all its content into a table, add the new app into the table and write it back
|
||||||
gfilesystem.make_directories(dir)
|
local file_path = gfilesystem.get_configuration_dir() .. "src/config/dock.json"
|
||||||
if not gfilesystem.file_readable(dir) then
|
local handler = io.open(file_path, "r")
|
||||||
os.execute("touch " .. dir .. "/dock.json")
|
if not handler then return end
|
||||||
end
|
|
||||||
local handler = io.open(dir .. "/dock.json", "r")
|
local dock_table = json:decode(handler:read("a")) or {}
|
||||||
if not handler then
|
|
||||||
return
|
handler:close()
|
||||||
end
|
assert(type(dock_table) == "table", "dock_table is not a table")
|
||||||
local dock_table = json:decode(handler:read("a")) or {}
|
|
||||||
handler:close()
|
|
||||||
|
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
|
||||||
table.insert(dock_table, {
|
table.insert(dock_table, {
|
||||||
name = app_widget.name or "",
|
name = app_widget.name or "",
|
||||||
icon = Get_gicon_path(app_info.get_icon(app)) or "",
|
icon = Get_gicon_path(app_info.get_icon(app)) or
|
||||||
|
Get_gicon_path(app_info.get_icon(app),
|
||||||
|
Gio.DesktopAppInfo.get_string(desktop_app_info, "X-AppImage-Old-Icon")) or "",
|
||||||
comment = app_widget.comment or "",
|
comment = app_widget.comment or "",
|
||||||
exec = app_widget.exec or "",
|
exec = app_widget.exec or "",
|
||||||
keywords = app_widget.keywords or "",
|
keywords = app_widget.keywords or "",
|
||||||
@@ -219,11 +244,12 @@ function application_grid:get_applications_from_file()
|
|||||||
actions = app_widget.actions or "",
|
actions = app_widget.actions or "",
|
||||||
desktop_file = Gio.DesktopAppInfo.get_filename(desktop_app_info) or ""
|
desktop_file = Gio.DesktopAppInfo.get_filename(desktop_app_info) or ""
|
||||||
})
|
})
|
||||||
|
|
||||||
local dock_encoded = json:encode(dock_table)
|
local dock_encoded = json:encode(dock_table)
|
||||||
handler = io.open("/home/crylia/.config/awesome/src/config/dock.json", "w")
|
handler = io.open(file_path, "w")
|
||||||
if not handler then
|
|
||||||
return
|
if not handler then return end
|
||||||
end
|
|
||||||
handler:write(dock_encoded)
|
handler:write(dock_encoded)
|
||||||
handler:close()
|
handler:close()
|
||||||
capi.awesome.emit_signal("dock::changed")
|
capi.awesome.emit_signal("dock::changed")
|
||||||
@@ -231,7 +257,7 @@ function application_grid:get_applications_from_file()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "Add to desktop",
|
name = "Add to desktop",
|
||||||
icon = gears.color.recolor_image(icondir .. "desktop.svg",
|
icon = grecolor_image(icondir .. "desktop.svg",
|
||||||
Theme_config.application_launcher.application.cm_icon_color),
|
Theme_config.application_launcher.application.cm_icon_color),
|
||||||
callback = function()
|
callback = function()
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
@@ -246,23 +272,23 @@ function application_grid:get_applications_from_file()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Hide context menu when the mouse leaves it
|
||||||
context_menu:connect_signal("mouse::leave", function()
|
context_menu:connect_signal("mouse::leave", function()
|
||||||
context_menu.visible = false
|
context_menu.visible = false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Execute command on left click and hide launcher
|
-- Execute command on left click and hide launcher, right click to show context menu
|
||||||
app_widget:buttons(
|
app_widget:buttons(
|
||||||
gears.table.join(
|
gtable.join(
|
||||||
awful.button({
|
abutton({
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
button = 1,
|
button = 1,
|
||||||
on_release = function()
|
on_release = function()
|
||||||
Gio.AppInfo.launch_uris_async(app)
|
Gio.AppInfo.launch_uris_async(app)
|
||||||
--!Change!
|
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
end
|
end
|
||||||
}),
|
}),
|
||||||
awful.button({
|
abutton({
|
||||||
modifiers = {},
|
modifiers = {},
|
||||||
button = 3,
|
button = 3,
|
||||||
on_release = function()
|
on_release = function()
|
||||||
@@ -271,13 +297,16 @@ function application_grid:get_applications_from_file()
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
Hover_signal(app_widget)
|
Hover_signal(app_widget)
|
||||||
table.insert(list, app_widget)
|
table.insert(list, app_widget)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.app_list = list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Takes the search filter and returns a list of applications in the correct order
|
||||||
|
---@param search_filter any
|
||||||
function application_grid:set_applications(search_filter)
|
function application_grid:set_applications(search_filter)
|
||||||
local filter = search_filter or self.filter or ""
|
local filter = search_filter or self.filter or ""
|
||||||
-- Reset to first position
|
-- Reset to first position
|
||||||
@@ -292,30 +321,34 @@ function application_grid:set_applications(search_filter)
|
|||||||
spacing = dpi(10),
|
spacing = dpi(10),
|
||||||
id = "grid",
|
id = "grid",
|
||||||
-- 200 is the application element width + 10 spacing
|
-- 200 is the application element width + 10 spacing
|
||||||
forced_num_cols = math.floor((capi.mouse.screen.geometry.width / 100 * 60) / (200)),
|
forced_num_cols = math.floor((capi.mouse.screen.geometry.width / 100 * 60) / 200),
|
||||||
forced_num_rows = 7,
|
forced_num_rows = 7,
|
||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
layout = wibox.layout.grid
|
layout = wibox.layout.grid
|
||||||
}
|
}
|
||||||
|
|
||||||
local dir = gfilesystem.get_configuration_dir() .. "src/config/applications.json"
|
-- Read the dock.json file and get all apps, these are needed to read/write the launch count
|
||||||
if not gfilesystem.file_readable(dir) then return end
|
local handler = io.open(gfilesystem.get_configuration_dir() .. "src/config/applications.json", "r")
|
||||||
|
|
||||||
local handler = io.open(dir, "r")
|
|
||||||
if not handler then return end
|
if not handler then return end
|
||||||
|
|
||||||
local dock_encoded = handler:read("a") or "{}"
|
local dock_encoded = handler:read("a") or "{}"
|
||||||
local dock = json:decode(dock_encoded)
|
local dock = json:decode(dock_encoded)
|
||||||
if type(dock) ~= "table" then return end
|
|
||||||
|
assert(type(dock) == "table", "dock is not a table")
|
||||||
|
|
||||||
local mylist = {}
|
local mylist = {}
|
||||||
|
|
||||||
for _, application in ipairs(self.app_list) do
|
for _, application in ipairs(self.app_list) do
|
||||||
-- Match the filter
|
-- Match the filter for the name, categories and keywords
|
||||||
if string.match(string.lower(application.name or ""), string.lower(filter)) or
|
if string.match(string.lower(application.name or ""), string.lower(filter)) or
|
||||||
string.match(string.lower(application.categories or ""), string.lower(filter)) or
|
string.match(string.lower(application.categories or ""), string.lower(filter)) or
|
||||||
string.match(string.lower(application.keywords or ""), string.lower(filter)) then
|
string.match(string.lower(application.keywords or ""), string.lower(filter)) then
|
||||||
|
|
||||||
|
-- If there are no elements in the table, set everything to 0
|
||||||
if #dock == 0 then
|
if #dock == 0 then
|
||||||
application.counter = 0
|
application.counter = 0
|
||||||
end
|
end
|
||||||
|
-- Read the counter for the matching app
|
||||||
for _, app in ipairs(dock) do
|
for _, app in ipairs(dock) do
|
||||||
if app.desktop_file == application.desktop_file then
|
if app.desktop_file == application.desktop_file then
|
||||||
application.counter = app.counter or 0
|
application.counter = app.counter or 0
|
||||||
@@ -329,14 +362,16 @@ function application_grid:set_applications(search_filter)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Sort the applications using the levenshtein algorithm
|
||||||
table.sort(mylist, function(a, b)
|
table.sort(mylist, function(a, b)
|
||||||
return levenshtein_distance(filter, a.name) < levenshtein_distance(filter, b.name)
|
return levenshtein_distance(filter, a.name) < levenshtein_distance(filter, b.name)
|
||||||
end)
|
end)
|
||||||
--sort mytable by counter
|
--Sort the applications using the counter
|
||||||
table.sort(mylist, function(a, b)
|
table.sort(mylist, function(a, b)
|
||||||
return a.counter > b.counter
|
return a.counter > b.counter
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Add the apps one by one into the grid and read its position
|
||||||
for _, app in ipairs(mylist) do
|
for _, app in ipairs(mylist) do
|
||||||
grid:add(app)
|
grid:add(app)
|
||||||
|
|
||||||
@@ -360,6 +395,7 @@ function application_grid:set_applications(search_filter)
|
|||||||
self:set_widget(grid)
|
self:set_widget(grid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Move the curser up by one, making sure it doesn't go out of bounds
|
||||||
function application_grid:move_up()
|
function application_grid:move_up()
|
||||||
self._private.curser.y = self._private.curser.y - 1
|
self._private.curser.y = self._private.curser.y - 1
|
||||||
if self._private.curser.y < 1 then
|
if self._private.curser.y < 1 then
|
||||||
@@ -368,6 +404,7 @@ function application_grid:move_up()
|
|||||||
capi.awesome.emit_signal("update::selected")
|
capi.awesome.emit_signal("update::selected")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Move the curser down by one, making sure it doesn't go out of bounds
|
||||||
function application_grid:move_down()
|
function application_grid:move_down()
|
||||||
self._private.curser.y = self._private.curser.y + 1
|
self._private.curser.y = self._private.curser.y + 1
|
||||||
local grid_rows, _ = self:get_widget():get_dimension()
|
local grid_rows, _ = self:get_widget():get_dimension()
|
||||||
@@ -377,6 +414,7 @@ function application_grid:move_down()
|
|||||||
capi.awesome.emit_signal("update::selected")
|
capi.awesome.emit_signal("update::selected")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Move the curser left by one, making sure it doesn't go out of bounds
|
||||||
function application_grid:move_left()
|
function application_grid:move_left()
|
||||||
self._private.curser.x = self._private.curser.x - 1
|
self._private.curser.x = self._private.curser.x - 1
|
||||||
if self._private.curser.x < 1 then
|
if self._private.curser.x < 1 then
|
||||||
@@ -385,6 +423,7 @@ function application_grid:move_left()
|
|||||||
capi.awesome.emit_signal("update::selected")
|
capi.awesome.emit_signal("update::selected")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Move the curser right by one, making sure it doesn't go out of bounds
|
||||||
function application_grid:move_right()
|
function application_grid:move_right()
|
||||||
self._private.curser.x = self._private.curser.x + 1
|
self._private.curser.x = self._private.curser.x + 1
|
||||||
local _, grid_cols = self:get_widget():get_dimension()
|
local _, grid_cols = self:get_widget():get_dimension()
|
||||||
@@ -394,22 +433,28 @@ function application_grid:move_right()
|
|||||||
capi.awesome.emit_signal("update::selected")
|
capi.awesome.emit_signal("update::selected")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Execute the currently selected app and add to the launch count
|
||||||
function application_grid:execute()
|
function application_grid:execute()
|
||||||
|
-- Get the app at the current x,y
|
||||||
local selected_widget = self:get_widget():get_widgets_at(self._private.curser.y,
|
local selected_widget = self:get_widget():get_widgets_at(self._private.curser.y,
|
||||||
self._private.curser.x)[1]
|
self._private.curser.x)[1]
|
||||||
|
-- Launch the application async
|
||||||
Gio.AppInfo.launch_uris_async(Gio.AppInfo.create_from_commandline(selected_widget.exec, nil, 0))
|
Gio.AppInfo.launch_uris_async(Gio.AppInfo.create_from_commandline(selected_widget.exec, nil, 0))
|
||||||
|
|
||||||
local dir = gfilesystem.get_configuration_dir() .. "src/config/applications.json"
|
local file_path = gfilesystem.get_configuration_dir() .. "src/config/applications.json"
|
||||||
if not gfilesystem.file_readable(dir) then return end
|
local handler = io.open(file_path, "r")
|
||||||
|
|
||||||
local handler = io.open(dir, "r")
|
|
||||||
if not handler then return end
|
if not handler then return end
|
||||||
|
|
||||||
local dock_encoded = handler:read("a") or "{}"
|
local dock_encoded = handler:read("a") or "{}"
|
||||||
local dock = json:decode(dock_encoded)
|
local dock = json:decode(dock_encoded)
|
||||||
if type(dock) ~= "table" then return end
|
|
||||||
|
assert(type(dock) == "table", "dock is not a table")
|
||||||
|
|
||||||
|
-- Increase the counter by one then rewrite to the file, its a bit hacky but it works
|
||||||
for _, prog in ipairs(dock) do
|
for _, prog in ipairs(dock) do
|
||||||
if prog.desktop_file:match(selected_widget.desktop_file) then
|
if prog.desktop_file:match(selected_widget.desktop_file) then
|
||||||
prog.counter = prog.counter + 1
|
prog.counter = prog.counter + 1
|
||||||
|
-- I don't like goto's, but its the easiest way here(PR is welcome).
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -423,12 +468,13 @@ function application_grid:execute()
|
|||||||
end
|
end
|
||||||
::continue::
|
::continue::
|
||||||
handler:close()
|
handler:close()
|
||||||
handler = io.open(dir, "w")
|
handler = io.open(file_path, "w")
|
||||||
if not handler then return end
|
if not handler then return end
|
||||||
handler:write(json:encode_pretty(dock))
|
handler:write(json:encode_pretty(dock))
|
||||||
handler:close()
|
handler:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Reset the grid cursor
|
||||||
function application_grid:reset()
|
function application_grid:reset()
|
||||||
self._private.curser = {
|
self._private.curser = {
|
||||||
x = 1,
|
x = 1,
|
||||||
@@ -444,20 +490,7 @@ function application_grid.new(args)
|
|||||||
|
|
||||||
gtable.crush(w, application_grid, true)
|
gtable.crush(w, application_grid, true)
|
||||||
|
|
||||||
w._private.curser = {
|
w.app_list = get_applications_from_file()
|
||||||
x = 1,
|
|
||||||
y = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Create folder and file if it doesn't exist
|
|
||||||
local dir = gfilesystem.get_configuration_dir() .. "src/config"
|
|
||||||
gfilesystem.make_directories(dir)
|
|
||||||
dir = dir .. "/applications.json"
|
|
||||||
if not gfilesystem.file_readable(dir) then
|
|
||||||
os.execute("touch " .. dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
w:get_applications_from_file()
|
|
||||||
|
|
||||||
w:set_applications()
|
w:set_applications()
|
||||||
return w
|
return w
|
||||||
|
|||||||
@@ -3,47 +3,98 @@
|
|||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
-- Awesome Libs
|
-- Awesome Libs
|
||||||
local awful = require("awful")
|
|
||||||
local dpi = require("beautiful").xresources.apply_dpi
|
|
||||||
local gears = require("gears")
|
|
||||||
local wibox = require("wibox")
|
|
||||||
local gshape = require("gears.shape")
|
|
||||||
local gtable = require("gears.table")
|
|
||||||
local gobject = require("gears.object")
|
|
||||||
local abutton = require("awful.button")
|
local abutton = require("awful.button")
|
||||||
|
local akeygrabber = require("awful.keygrabber")
|
||||||
|
local aplacement = require("awful.placement")
|
||||||
|
local apopup = require("awful.popup")
|
||||||
|
local awidget = require("awful.widget")
|
||||||
|
local dpi = require("beautiful").xresources.apply_dpi
|
||||||
|
local gtable = require("gears.table")
|
||||||
|
local wibox = require("wibox")
|
||||||
|
|
||||||
|
-- Own libs
|
||||||
|
local app_grid = require("src.modules.application_launcher.application")
|
||||||
|
|
||||||
local capi = {
|
local capi = {
|
||||||
awesome = awesome,
|
awesome = awesome,
|
||||||
mouse = mouse,
|
mouse = mouse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- This grid object is shared to avoid having multipe unnecessary instances
|
||||||
|
local application_grid = app_grid {}
|
||||||
|
|
||||||
local application_launcher = { mt = {} }
|
local application_launcher = { mt = {} }
|
||||||
|
|
||||||
|
|
||||||
application_launcher.application_grid = require("src.modules.application_launcher.application") {}
|
|
||||||
|
|
||||||
|
|
||||||
function application_launcher.new(args)
|
function application_launcher.new(args)
|
||||||
args = args or {}
|
args = args or {}
|
||||||
|
|
||||||
local ret = gobject { enable_properties = true }
|
-- Create a new inputbox
|
||||||
|
local searchbar = awidget.inputbox {
|
||||||
gtable.crush(ret, application_launcher, true)
|
|
||||||
|
|
||||||
local searchbar = awful.widget.inputbox {
|
|
||||||
hint_text = "Search...",
|
hint_text = "Search...",
|
||||||
valign = "center",
|
valign = "center",
|
||||||
halign = "left",
|
halign = "left",
|
||||||
}
|
}
|
||||||
|
|
||||||
searchbar:buttons(
|
-- Application launcher popup
|
||||||
gtable.join {
|
local application_container = apopup {
|
||||||
|
widget = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
searchbar,
|
||||||
|
widget = wibox.container.margin,
|
||||||
|
margins = 5,
|
||||||
|
},
|
||||||
|
widget = wibox.container.constraint,
|
||||||
|
strategy = "exact",
|
||||||
|
height = dpi(50),
|
||||||
|
},
|
||||||
|
widget = wibox.container.background,
|
||||||
|
bg = Theme_config.application_launcher.searchbar.bg,
|
||||||
|
fg = Theme_config.application_launcher.searchbar.fg,
|
||||||
|
border_color = Theme_config.application_launcher.searchbar.border_color,
|
||||||
|
border_width = Theme_config.application_launcher.searchbar.border_width,
|
||||||
|
shape = Theme_config.application_launcher.searchbar.shape,
|
||||||
|
id = "searchbar_bg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
application_grid,
|
||||||
|
layout = require("src.lib.overflow_widget.overflow").vertical,
|
||||||
|
scrollbar_width = 0,
|
||||||
|
step = dpi(100),
|
||||||
|
},
|
||||||
|
spacing = dpi(10),
|
||||||
|
layout = wibox.layout.fixed.vertical
|
||||||
|
},
|
||||||
|
margins = dpi(20),
|
||||||
|
widget = wibox.container.margin
|
||||||
|
},
|
||||||
|
height = args.screen.geometry.height / 100 * 60,
|
||||||
|
strategy = "exact",
|
||||||
|
widget = wibox.container.constraint,
|
||||||
|
},
|
||||||
|
ontop = true,
|
||||||
|
visible = false,
|
||||||
|
stretch = false,
|
||||||
|
screen = args.screen,
|
||||||
|
placement = aplacement.centered,
|
||||||
|
bg = Theme_config.application_launcher.bg,
|
||||||
|
border_color = Theme_config.application_launcher.border_color,
|
||||||
|
border_width = Theme_config.application_launcher.border_width
|
||||||
|
}
|
||||||
|
|
||||||
|
gtable.crush(application_container, application_launcher, true)
|
||||||
|
|
||||||
|
-- Focus the searchbar when its left clicked
|
||||||
|
searchbar:buttons(gtable.join {
|
||||||
abutton({}, 1, function()
|
abutton({}, 1, function()
|
||||||
searchbar:focus()
|
searchbar:focus()
|
||||||
end)
|
end)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
|
--#region Hover signals to change the cursor to a text cursor
|
||||||
local old_cursor, old_wibox
|
local old_cursor, old_wibox
|
||||||
searchbar:connect_signal("mouse::enter", function()
|
searchbar:connect_signal("mouse::enter", function()
|
||||||
local wid = capi.mouse.current_wibox
|
local wid = capi.mouse.current_wibox
|
||||||
@@ -56,168 +107,102 @@ function application_launcher.new(args)
|
|||||||
old_wibox.cursor = old_cursor
|
old_wibox.cursor = old_cursor
|
||||||
old_wibox = nil
|
old_wibox = nil
|
||||||
end)
|
end)
|
||||||
|
--#endregion
|
||||||
|
|
||||||
local applicaton_launcher = wibox.widget {
|
-- Get a reference to the searchbar background value
|
||||||
{
|
local searchbar_bg = application_container.widget:get_children_by_id("searchbar_bg")[1]
|
||||||
{
|
|
||||||
{
|
|
||||||
{
|
|
||||||
{
|
|
||||||
searchbar,
|
|
||||||
widget = wibox.container.margin,
|
|
||||||
margins = 5,
|
|
||||||
id = "marg"
|
|
||||||
},
|
|
||||||
widget = wibox.container.constraint,
|
|
||||||
strategy = "exact",
|
|
||||||
width = 400,
|
|
||||||
height = 50,
|
|
||||||
id = "const"
|
|
||||||
},
|
|
||||||
widget = wibox.container.background,
|
|
||||||
bg = Theme_config.application_launcher.searchbar.bg,
|
|
||||||
fg = Theme_config.application_launcher.searchbar.fg,
|
|
||||||
border_color = Theme_config.application_launcher.searchbar.border_color,
|
|
||||||
border_width = Theme_config.application_launcher.searchbar.border_width,
|
|
||||||
shape = gshape.rounded_rect,
|
|
||||||
id = "searchbar_bg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ret.application_grid,
|
|
||||||
spacing = dpi(10),
|
|
||||||
layout = require("src.lib.overflow_widget.overflow").vertical,
|
|
||||||
scrollbar_width = 0,
|
|
||||||
step = dpi(100),
|
|
||||||
id = "scroll_bar",
|
|
||||||
},
|
|
||||||
spacing = dpi(10),
|
|
||||||
layout = wibox.layout.fixed.vertical
|
|
||||||
},
|
|
||||||
margins = dpi(20),
|
|
||||||
widget = wibox.container.margin
|
|
||||||
},
|
|
||||||
height = args.screen.geometry.height / 100 * 60,
|
|
||||||
--width = s.geometry.width / 100 * 60,
|
|
||||||
strategy = "exact",
|
|
||||||
widget = wibox.container.constraint
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.application_container = awful.popup {
|
-- Toggle visible for the application launcher and init the searchbar
|
||||||
widget = applicaton_launcher,
|
capi.awesome.connect_signal("application_launcher::show", function()
|
||||||
ontop = true,
|
if capi.mouse.screen == args.screen then
|
||||||
visible = false,
|
|
||||||
stretch = false,
|
|
||||||
screen = args.screen,
|
|
||||||
shape = function(cr, width, height)
|
|
||||||
gears.shape.rounded_rect(cr, width, height, dpi(12))
|
|
||||||
end,
|
|
||||||
placement = awful.placement.centered,
|
|
||||||
bg = Theme_config.application_launcher.bg,
|
|
||||||
border_color = Theme_config.application_launcher.border_color,
|
|
||||||
border_width = Theme_config.application_launcher.border_width
|
|
||||||
}
|
|
||||||
|
|
||||||
local searchbar_bg = applicaton_launcher:get_children_by_id("searchbar_bg")[1]
|
|
||||||
|
|
||||||
capi.awesome.connect_signal(
|
|
||||||
"application_launcher::show",
|
|
||||||
function()
|
|
||||||
capi.awesome.emit_signal("update::selected")
|
capi.awesome.emit_signal("update::selected")
|
||||||
if capi.mouse.screen == args.screen then
|
if capi.mouse.screen == args.screen then
|
||||||
ret.application_container.visible = not ret.application_container.visible
|
application_container.visible = not application_container.visible
|
||||||
end
|
end
|
||||||
if ret.application_container.visible then
|
if application_container.visible then
|
||||||
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_active
|
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_active
|
||||||
searchbar:focus()
|
searchbar:focus()
|
||||||
else
|
else
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
awful.keygrabber.stop()
|
akeygrabber.stop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
end)
|
||||||
|
|
||||||
searchbar:connect_signal(
|
-- Execute the currently selected application, reset the searchbar and hide the launcher
|
||||||
"submit",
|
searchbar:connect_signal("submit", function(_, text)
|
||||||
function(_, text)
|
application_grid:execute()
|
||||||
ret.application_grid:execute()
|
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
ret.application_grid:set_applications(searchbar:get_text())
|
application_grid:set_applications(searchbar:get_text())
|
||||||
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
||||||
end
|
end)
|
||||||
)
|
|
||||||
|
|
||||||
searchbar:connect_signal(
|
-- Hide the application launcher when the keygrabber stops and reset the searchbar
|
||||||
"stopped",
|
searchbar:connect_signal("stopped", function(_, stop_key)
|
||||||
function(_, stop_key)
|
|
||||||
if stop_key == "Escape" then
|
if stop_key == "Escape" then
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
end
|
end
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
ret.application_grid:set_applications(searchbar:get_text())
|
application_grid:set_applications(searchbar:get_text())
|
||||||
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
||||||
end
|
end)
|
||||||
)
|
|
||||||
|
|
||||||
searchbar:connect_signal(
|
-- When started change the background for the searchbar
|
||||||
"started",
|
searchbar:connect_signal("started", function()
|
||||||
function()
|
|
||||||
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_active
|
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_active
|
||||||
end
|
end)
|
||||||
)
|
|
||||||
|
|
||||||
searchbar:connect_signal(
|
-- On every keypress in the searchbar check for certain inputs
|
||||||
"inputbox::key_pressed",
|
searchbar:connect_signal("inputbox::key_pressed", function(_, modkey, key)
|
||||||
function(_, modkey, key)
|
if key == "Escape" then -- Escape to stop the keygrabber, hide the launcher and reset the searchbar
|
||||||
if key == "Escape" then
|
|
||||||
searchbar:stop()
|
searchbar:stop()
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
ret.application_grid:reset()
|
application_grid:reset()
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
elseif key == "Down" or key == "Right" then
|
elseif key == "Down" or key == "Right" then --If down or right is pressed initiate the grid navigation
|
||||||
if key == "Down" then
|
if key == "Down" then
|
||||||
ret.application_grid:move_down()
|
application_grid:move_down()
|
||||||
elseif key == "Right" then
|
elseif key == "Right" then
|
||||||
ret.application_grid:move_right()
|
application_grid:move_right()
|
||||||
end
|
end
|
||||||
searchbar:stop()
|
searchbar:stop()
|
||||||
awful.keygrabber.run(function(mod, key2, event)
|
--New keygrabber to allow for key navigation
|
||||||
|
akeygrabber.run(function(mod, key2, event)
|
||||||
if event == "press" then
|
if event == "press" then
|
||||||
if key2 == "Down" then
|
if key2 == "Down" then
|
||||||
ret.application_grid:move_down()
|
application_grid:move_down()
|
||||||
elseif key2 == "Up" then
|
elseif key2 == "Up" then
|
||||||
local old_y = ret.application_grid._private.curser.y
|
local old_y = application_grid._private.curser.y
|
||||||
ret.application_grid:move_up()
|
application_grid:move_up()
|
||||||
if old_y - ret.application_grid._private.curser.y == 0 then
|
if old_y - application_grid._private.curser.y == 0 then
|
||||||
searchbar:focus()
|
searchbar:focus()
|
||||||
end
|
end
|
||||||
elseif key2 == "Left" then
|
elseif key2 == "Left" then
|
||||||
ret.application_grid:move_left()
|
application_grid:move_left()
|
||||||
elseif key2 == "Right" then
|
elseif key2 == "Right" then
|
||||||
ret.application_grid:move_right()
|
application_grid:move_right()
|
||||||
elseif key2 == "Return" then
|
elseif key2 == "Return" then
|
||||||
awful.keygrabber.stop()
|
akeygrabber.stop()
|
||||||
ret.application_grid:execute()
|
application_grid:execute()
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
ret.application_grid:reset()
|
application_grid:reset()
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
ret.application_grid:set_applications(searchbar:get_text())
|
application_grid:set_applications(searchbar:get_text())
|
||||||
elseif key2 == "Escape" then
|
elseif key2 == "Escape" then
|
||||||
capi.awesome.emit_signal("application_launcher::show")
|
capi.awesome.emit_signal("application_launcher::show")
|
||||||
ret.application_grid:reset()
|
application_grid:reset()
|
||||||
searchbar:set_text("")
|
searchbar:set_text("")
|
||||||
ret.application_grid:set_applications(searchbar:get_text())
|
application_grid:set_applications(searchbar:get_text())
|
||||||
awful.keygrabber.stop()
|
akeygrabber.stop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
searchbar_bg.border_color = Theme_config.application_launcher.searchbar.border_color
|
||||||
end
|
end
|
||||||
ret.application_grid:set_applications(searchbar:get_text())
|
-- Update the applications in the grid
|
||||||
end
|
application_grid:set_applications(searchbar:get_text())
|
||||||
)
|
end)
|
||||||
|
|
||||||
return ret
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function application_launcher.mt:__call(...)
|
function application_launcher.mt:__call(...)
|
||||||
|
|||||||
Reference in New Issue
Block a user