2022-08-31 12:20:58 +00:00
-- This file is part of Reno desktop.
--
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
--
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
2022-07-23 19:10:02 +00:00
-- Dismal - not your average run prompt
2022-08-31 12:20:58 +00:00
-- Reference implementation of a generic widget
2022-07-23 19:10:02 +00:00
local awmtk2 = require ( " awmtk2 " )
local wibox = require ( " wibox " )
local gears = require ( " gears " )
local awful = require ( " awful " )
2022-09-07 17:43:25 +00:00
local ask = require ( " asckey " )
2022-07-23 19:10:02 +00:00
2023-01-19 15:23:50 +00:00
local xdg_search = function ( name , rlimit , sorting_method )
local ranked_results = { }
if sorting_method == " usage " then
local filter = { }
local keys = { }
for k , v in pairs ( xdg.apps ) do
if not v.count then
v.count = 0
end
if v.name : lower ( ) : find ( name , nil , true ) then
if not filter [ v.count ] then
table.insert ( keys , v.count )
filter [ v.count ] = { }
end
table.insert ( filter [ v.count ] , { k , v } )
end
end
table.sort ( keys , function ( a , b ) return a > b end )
local count = 0
local exit = false
for k = 1 , rlimit do
local i = keys [ k ]
if not filter [ i ] then
break
end
for _ , v in pairs ( filter [ i ] ) do
table.insert ( ranked_results , v )
count = count + 1
if count >= rlimit then
exit = true
break
end
end
if exit == true then
break
end
2022-07-23 19:10:02 +00:00
end
2023-01-19 15:23:50 +00:00
elseif sorting_method == " recent " then
local most_recent = 0
for k , v in pairs ( xdg.apps ) do
if v.name : lower ( ) : find ( name , nil , true ) and v.atime and v.atime >= most_recent then
most_recent = v.atime
table.insert ( ranked_results , 1 , { k , v } )
end
if # ranked_results > rlimit then
table.remove ( ranked_results , rlimit + 1 )
end
2022-07-23 19:10:02 +00:00
end
end
2023-01-19 15:23:50 +00:00
return ranked_results
2022-07-23 19:10:02 +00:00
end
return function ( args )
2022-09-06 22:40:51 +00:00
local style = awmtk2.create_style ( " dismal " ,
2023-01-19 13:42:20 +00:00
awmtk2.generic . popup , args.style , args.vertical )
2022-07-23 19:10:02 +00:00
local templates = awmtk2.create_template_lib ( " dismal " , awmtk2.templates , args.templates )
2023-01-16 12:47:51 +00:00
local t = awmtk2.build_templates ( templates , style , args.vertical )
local dismal = awful.popup ( t.popup ( {
id = " dismal_root " ,
layout = wibox.layout . fixed.horizontal ,
spacing = style.container . spacing
2022-07-23 19:10:02 +00:00
} ) )
2023-03-10 23:35:55 +00:00
local available_positions = {
top_left = awful.placement . top_left ,
top_right = awful.placement . top_right ,
bottom_left = awful.placement . bottom_left ,
bottom_right = awful.placement . bottom_right ,
left = awful.placement . left ,
right = awful.placement . right ,
top = awful.placement . top ,
bottom = awful.placement . bottom ,
centered = awful.placement . centered
}
local honor_workarea = true
if ( args.ignore_wibars ) then
honor_workarea = false
end
local popup_position = ( args.pos and available_positions [ args.pos ] )
or available_positions [ " top_left " ]
2023-01-19 15:23:50 +00:00
local button_cache = gears.cache ( function ( name , exec , description , icon , key )
2022-07-23 19:10:02 +00:00
-- beacause apparently cache doesn't allow nil values
if icon == " " then icon = nil end
2022-08-11 19:34:25 +00:00
-- contents of our button
2022-07-23 19:10:02 +00:00
local widget = wibox.widget ( t.button ( t.article ( {
icon = icon ,
resize = true ,
title = name ,
description = description ,
2023-01-16 12:47:51 +00:00
icon_size = ( style.button . height and
( style.button . height - style.button . margins ) ) or
( 34 - style.button . margins )
2022-07-23 19:10:02 +00:00
} ) , {
2023-01-16 12:47:51 +00:00
forced_height = style.button . height or 30
2022-07-23 19:10:02 +00:00
} ) )
local exec = exec : gsub ( " %%%w " , " " )
2022-08-11 19:34:25 +00:00
-- theme-declared highlight function, because why not
-- maybe you'd like to add duck noises to button presses idk.
widget : connect_signal ( " button::press " , style.button . onpress )
widget : connect_signal ( " button::release " , style.button . onrelease )
widget : connect_signal ( " mouses::leave " , style.button . onrelease )
2023-01-19 15:23:50 +00:00
local bump = function ( )
if not xdg.apps [ key ] . count then
xdg.apps [ key ] . count = 0
end
xdg.apps [ key ] . count = xdg.apps [ key ] . count + 1
xdg.apps [ key ] . atime = os.time ( )
end
2022-07-23 19:10:02 +00:00
widget : buttons (
gears.table . join (
awful.button ( { } , 1 , function ( )
awful.spawn ( exec )
2023-01-16 12:47:51 +00:00
dismal.visible = false
2022-07-23 19:10:02 +00:00
-- i know it's deprecated, you literally give me no option
awful.keygrabber . stop ( )
2023-01-19 15:23:50 +00:00
bump ( )
2022-07-23 19:10:02 +00:00
end ) ,
awful.button ( { " Control " } , 1 , function ( )
awful.spawn ( { global.terminal , " -e " , exec } )
2023-01-16 12:47:51 +00:00
dismal.visible = false
2022-07-23 19:10:02 +00:00
awful.keygrabber . stop ( )
2023-01-19 15:23:50 +00:00
bump ( )
2022-07-23 19:10:02 +00:00
end ) ,
awful.button ( { } , 3 , function ( )
awful.spawn ( exec )
2023-01-19 15:23:50 +00:00
bump ( )
2022-07-23 19:10:02 +00:00
end ) ,
awful.button ( { " Control " } , 3 , function ( )
awful.spawn ( { global.terminal , " -e " , exec } )
2023-01-19 15:23:50 +00:00
bump ( )
2022-07-23 19:10:02 +00:00
end )
)
)
return widget
end )
2023-01-16 12:47:51 +00:00
local launchpad
do -- Primary area
launchpad = wibox.widget ( {
t.container {
t.textbox ( {
text = " " ,
id = " inputbox "
} ) ,
layout = wibox.layout . fixed.vertical ,
} ,
t.container ( {
id = " resultbox " ,
layout = wibox.layout . fixed.vertical ,
spacing = style.container . spacing
} , {
bg = style.container . bg_highlight ,
bgimage = style.container . bgimage_highlight ,
forced_width = style.button . width or 180
} ) ,
2023-01-19 15:23:50 +00:00
t.container ( {
t.button (
t.textbox ( {
markup = " Most used "
} ) , {
id = " usage "
} ) ,
t.button (
t.textbox ( {
markup = " Recent "
} ) , {
id = " recent "
} ) ,
id = " buttonbox " ,
spacing = style.container . spacing ,
layout = wibox.layout . flex.horizontal
} ) ,
2023-01-16 12:47:51 +00:00
t.textbox ( {
markup = " <b>Enter</b> - run \n <b>Ctrl+Enter</b> - run in terminal "
} ) ,
spacing = style.container . spacing ,
layout = wibox.layout . fixed.vertical
} )
local results_list = launchpad : get_children_by_id ( " resultbox " ) [ 1 ]
local input_field = launchpad : get_children_by_id ( " inputbox " ) [ 1 ]
2023-01-19 15:23:50 +00:00
local usage_sort = launchpad : get_children_by_id ( " usage " ) [ 1 ]
local recent_sort = launchpad : get_children_by_id ( " recent " ) [ 1 ]
local sorting_method = " usage "
usage_sort : set_bg ( style.bg_focus )
if style.button . onpress then
style.button . onpress ( usage_sort )
end
usage_sort : connect_signal ( " button::press " , function ( )
recent_sort : set_bg ( style.bg_normal )
if style.button . onrelease then
style.button . onrelease ( recent_sort )
end
usage_sort : set_bg ( style.bg_focus )
if style.button . onpress then
style.button . onpress ( usage_sort )
end
sorting_method = " usage "
end )
recent_sort : connect_signal ( " button::press " , function ( )
usage_sort : set_bg ( style.bg_normal )
if style.button . onrelease then
style.button . onrelease ( usage_sort )
end
recent_sort : set_bg ( style.bg_focus )
if style.button . onpress then
style.button . onpress ( recent_sort )
end
sorting_method = " recent "
end )
2023-01-16 12:47:51 +00:00
root.keys ( gears.table . join (
root.keys ( ) ,
ask.k ( " :dismal.run " , function ( )
results_list : reset ( )
2023-01-19 15:23:50 +00:00
dismal.visible = true
if dismal.visible then
2023-03-10 23:35:55 +00:00
popup_position ( dismal , { honor_workarea = honor_workarea } )
2023-01-16 12:47:51 +00:00
awful.prompt . run {
prompt = " <b>Run: </b> " ,
textbox = input_field ,
hooks = {
{ { " Control " } , " Return " , function ( cmd )
awful.spawn ( { global.terminal , " -e " , cmd } )
end } ,
{ { } , " Return " , function ( cmd )
awful.spawn . with_shell ( cmd )
end } ,
{ { } , ' Escape ' , function ( )
input_field.markup = " "
end } ,
} ,
done_callback = function ( )
2023-01-19 15:23:50 +00:00
dismal.visible = false
2023-01-16 12:47:51 +00:00
end ,
changed_callback = function ( command )
2023-01-19 15:23:50 +00:00
local results = xdg_search ( command , args.result_limit or 5 , sorting_method )
2023-01-16 12:47:51 +00:00
results_list : reset ( )
for _ , v in pairs ( results ) do
2023-01-19 15:23:50 +00:00
results_list : add ( button_cache : get (
v [ 2 ] . name ,
v [ 2 ] . exec ,
v [ 2 ] . description or " " ,
v [ 2 ] . icon or " " ,
v [ 1 ]
2023-01-16 12:47:51 +00:00
) )
end
end ,
completion_callback = function ( before , after , ncomp )
return awful.completion . shell ( before , after , ncomp , global.shell )
end ,
history_path = ( gears.filesystem . get_xdg_cache_home ( ) or " /tmp/ " ) .. " .dismal_history " ,
history_max = 50
}
end
end , { description = " open run menu " , group = " widgets " } )
) )
end
2023-01-19 15:23:50 +00:00
local dismal_root = dismal.widget : get_children_by_id ( " dismal_root " ) [ 1 ]
dismal_root : add ( launchpad )
2023-01-16 12:47:51 +00:00
return dismal
2022-07-23 19:10:02 +00:00
end