diff --git a/awesome/src/assets/icons/calendar/add_ical.svg b/awesome/src/assets/icons/calendar/add_ical.svg new file mode 100644 index 0000000..80a6f75 --- /dev/null +++ b/awesome/src/assets/icons/calendar/add_ical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/awesome/src/assets/icons/calendar/add_task.svg b/awesome/src/assets/icons/calendar/add_task.svg new file mode 100644 index 0000000..bb280a8 --- /dev/null +++ b/awesome/src/assets/icons/calendar/add_task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/awesome/src/assets/icons/calendar/calendar.svg b/awesome/src/assets/icons/calendar/calendar.svg new file mode 100644 index 0000000..6d5006c --- /dev/null +++ b/awesome/src/assets/icons/calendar/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/awesome/src/assets/icons/calendar/chevron-left.svg b/awesome/src/assets/icons/calendar/chevron-left.svg new file mode 100644 index 0000000..53831bc --- /dev/null +++ b/awesome/src/assets/icons/calendar/chevron-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/src/assets/icons/calendar/chevron-right.svg b/awesome/src/assets/icons/calendar/chevron-right.svg new file mode 100644 index 0000000..b2da475 --- /dev/null +++ b/awesome/src/assets/icons/calendar/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/src/assets/userpfp/crylia.png b/awesome/src/assets/userpfp/crylia.png index 44f69c6..671fbe2 100644 Binary files a/awesome/src/assets/userpfp/crylia.png and b/awesome/src/assets/userpfp/crylia.png differ diff --git a/awesome/src/bindings/global_keys.lua b/awesome/src/bindings/global_keys.lua index 2e12be0..40e30dc 100644 --- a/awesome/src/bindings/global_keys.lua +++ b/awesome/src/bindings/global_keys.lua @@ -235,7 +235,7 @@ return gears.table.join( awful.key( {}, "XF86AudioMute", - function() + function(c) awful.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle") awesome.emit_signal("widget::volume_osd:rerun") end, @@ -244,11 +244,11 @@ return gears.table.join( awful.key( {}, "XF86MonBrightnessUp", - function() + function(c) awful.spawn.easy_async_with_shell( - "xfpm-power-backlight-helper --get-brightness", + "pkexec xfpm-power-backlight-helper --get-brightness", function(stdout) - awful.spawn(awful.util.getdir("config") .. "src/scripts/backlight.sh set " .. + awful.spawn("pkexec xfpm-power-backlight-helper --set-brightness " .. tostring(tonumber(stdout) + BACKLIGHT_SEPS)) awesome.emit_signal("brightness::update") end @@ -259,11 +259,12 @@ return gears.table.join( awful.key( {}, "XF86MonBrightnessDown", - function() + function(c) awful.spawn.easy_async_with_shell( - "xfpm-power-backlight-helper --get-brightness", + "pkexec xfpm-power-backlight-helper --get-brightness", function(stdout) - awful.spawn(awful.util.getdir("config") .. "src/scripts/backlight.sh set " .. + awful.spawn( + "pkexec xfpm-power-backlight-helper --set-brightness " .. tostring(tonumber(stdout) - BACKLIGHT_SEPS)) awesome.emit_signal("brightness::update") end diff --git a/awesome/src/core/rules.lua b/awesome/src/core/rules.lua index 36403c0..84c7f8d 100644 --- a/awesome/src/core/rules.lua +++ b/awesome/src/core/rules.lua @@ -38,18 +38,25 @@ awful.rules.rules = { titlebars_enabled = true } }, - rule_any = { - class = { - 'Wine', - 'dolphin-emu', - 'Steam', - 'Citra', + { + id = "games", + rule_any = { + class = { + "steam_app_.%d+", + "gta5.exe", + }, }, - name = { 'Steam' } - }, - properties = { - skip_decoration = true, - placement = awful.placement.centered + properties = { + tag = "9", + switchtotag = true, + fullscreen = true, + screen = screen[1], + floating = true, + }, + focus = true, + callback = function(c) + awful.screen.focused() + end } } diff --git a/awesome/src/core/signals.lua b/awesome/src/core/signals.lua index a45c2ec..02c19f3 100644 --- a/awesome/src/core/signals.lua +++ b/awesome/src/core/signals.lua @@ -95,9 +95,6 @@ tag.connect_signal( ---@param icon_override_hover string | nil the hover effect color function Hover_signal(widget, bg_override, fg_override, border_override, icon_override, icon_override_hover) local old_wibox, old_cursor, old_bg, old_fg, old_border - - local r, g, b - widget.bg = widget.bg or "#000000" widget.fg = widget.fg or "#000000" widget.border_color = widget.border_color or "#000000" @@ -107,62 +104,72 @@ function Hover_signal(widget, bg_override, fg_override, border_override, icon_ov widget.icon = widget:get_children_by_id("icon")[1] end - --[[ local r_timed_bg = rubato.timed { duration = 0.5 } - local g_timed_bg = rubato.timed { duration = 0.5 } - local b_timed_bg = rubato.timed { duration = 0.5 } + local _, rb, gb, bb = widget.bg:get_rgba() + local _, rf, gf, bf = widget.fg:get_rgba() + local rbo, gbo, bbo = color.utils.hex_to_rgba(widget.border_color) + + local r_timed_bg = rubato.timed { duration = 0.3, pos = math.floor(rb * 255) } + local g_timed_bg = rubato.timed { duration = 0.3, pos = math.floor(gb * 255) } + local b_timed_bg = rubato.timed { duration = 0.3, pos = math.floor(bb * 255) } + + local r_timed_fg = rubato.timed { duration = 0.3, pos = math.floor(rf * 255) } + local g_timed_fg = rubato.timed { duration = 0.3, pos = math.floor(gf * 255) } + local b_timed_fg = rubato.timed { duration = 0.3, pos = math.floor(bf * 255) } + + local r_timed_border = rubato.timed { duration = 0.3, pos = math.floor(rbo) } + local g_timed_border = rubato.timed { duration = 0.3, pos = math.floor(gbo) } + local b_timed_border = rubato.timed { duration = 0.3, pos = math.floor(bbo) } local function update_bg() widget:set_bg("#" .. color.utils.rgba_to_hex { r_timed_bg.pos, g_timed_bg.pos, b_timed_bg.pos }) end + local function update_fg() + widget:set_fg("#" .. color.utils.rgba_to_hex { r_timed_fg.pos, g_timed_fg.pos, b_timed_fg.pos }) + end + + local function update_border() + widget:set_border_color("#" .. color.utils.rgba_to_hex { r_timed_border.pos, g_timed_border.pos, b_timed_border.pos }) + end + r_timed_bg:subscribe(update_bg) g_timed_bg:subscribe(update_bg) b_timed_bg:subscribe(update_bg) + r_timed_fg:subscribe(update_fg) + g_timed_fg:subscribe(update_fg) + b_timed_fg:subscribe(update_fg) + + r_timed_border:subscribe(update_border) + g_timed_border:subscribe(update_border) + b_timed_border:subscribe(update_border) + local function set_bg(newbg) - r_timed_bg.target, g_timed_bg.target, b_timed_bg.target = color.utils.hex_to_rgba(newbg) - end ]] - - local mouse_enter = function() - _, r, g, b, _ = widget.bg:get_rgba() - old_bg = RGB_to_hex(r, g, b) - if bg_override or old_bg then - widget:set_bg(bg_override or old_bg .. "dd") - end - _, r, g, b, _ = widget.fg:get_rgba() - old_fg = RGB_to_hex(r, g, b) - if fg_override or old_fg then - widget:set_fg(fg_override or old_fg .. "dd") - end - old_border = widget.border_color - if border_override or old_border then - widget.border_color = border_override or old_border .. "dd" - end - if icon and widget.icon and icon_override and icon_override_hover then - widget.icon.image = gears.color.recolor_image(icon, icon_override_hover) - end - local w = mouse.current_wibox - if w then - old_cursor, old_wibox = w.cursor, w - w.cursor = "hand1" - end + r_timed_bg.target, g_timed_bg.target, b_timed_bg.target = newbg[1], newbg[2], newbg[3] end - --[[ local button_press = function() - if old_bg or bg_override then - if bg_override then - bg_override = bg_override .. "bb" - end - widget.bg = bg_override or old_bg .. "bb" - end - if fg_override or old_fg then - if fg_override then - fg_override = fg_override .. "bb" - end - widget.fg = fg_override or old_fg .. "bb" - end + local function set_fg(newfg) + r_timed_fg.target, g_timed_fg.target, b_timed_fg.target = newfg[1], newfg[2], newfg[3] end + local function set_border(newborder) + r_timed_border.target, g_timed_border.target, b_timed_border.target = newborder[1], newborder[2], newborder[3] + end + + local _, rbg, gbg, bbg, abg = widget.bg:get_rgba() + old_bg = RGB_to_hex(rbg, gbg, bbg) + local _, rfg, gfg, bfg, afg = widget.fg:get_rgba() + old_fg = RGB_to_hex(rfg, gfg, bfg) + old_border = widget.border_color + local rborder, gborder, bborder = color.utils.hex_to_rgba(old_border) + + local function match_hex(hex1, hex2) + local r1, g1, b1 = color.utils.hex_to_rgba(hex1) + local r2, g2, b2 = color.utils.hex_to_rgba(hex2) + return math.abs(r1 - r2) <= 100 and math.abs(g1 - g2) <= 100 and math.abs(b1 - b2) <= 100 + end + + --[[ local button_release = function() if old_bg or bg_override then if bg_override then @@ -179,14 +186,18 @@ function Hover_signal(widget, bg_override, fg_override, border_override, icon_ov end ]] local mouse_leave = function() + if old_bg then - widget:set_bg(old_bg) + local r, g, b = color.utils.hex_to_rgba(old_bg) + set_bg({ r, g, b }) end if old_fg then - widget:set_fg(old_fg) + local r, g, b = color.utils.hex_to_rgba(old_fg) + set_fg({ r, g, b }) end if old_border then - widget.border_color = old_border + local r, g, b = color.utils.hex_to_rgba(old_border) + set_border({ r, g, b }) end if old_wibox then old_wibox.cursor = old_cursor @@ -197,8 +208,76 @@ function Hover_signal(widget, bg_override, fg_override, border_override, icon_ov end end + local mouse_enter = function() + _, rbg, gbg, bbg, abg = widget.bg:get_rgba() + if not match_hex(RGB_to_hex(rbg, gbg, bbg), old_bg) then + old_bg = RGB_to_hex(rbg, gbg, bbg) + set_bg({ rbg * 0.9 * 255, gbg * 0.9 * 255, bbg * 0.9 * 255 }) + end + if old_bg then + if bg_override then + rbg, gbg, bbg = color.utils.hex_to_rgba(bg_override) + set_bg({ rbg, gbg, bbg }) + else + set_bg({ rbg * 0.9 * 255, gbg * 0.9 * 255, bbg * 0.9 * 255 }) + end + end + + _, rfg, gfg, bfg, afg = widget.fg:get_rgba() + if not match_hex(RGB_to_hex(rfg, gfg, bfg), old_fg) then + old_fg = RGB_to_hex(rfg, gfg, bfg) + set_fg({ rfg * 0.9 * 255, gfg * 0.9 * 255, bfg * 0.9 * 255 }) + end + if fg_override or old_fg then + if fg_override then + rfg, gfg, bfg = color.utils.hex_to_rgba(fg_override) + set_fg({ rfg, gfg, bfg }) + else + set_fg({ rfg * 0.9 * 255, gfg * 0.9 * 255, bfg * 0.9 * 255 }) + end + end + + if not match_hex(old_border, widget.border_color) then + old_border = widget.border_color + rborder, gborder, bborder = color.utils.hex_to_rgba(old_border) + end + if border_override or old_border then + if border_override then + rborder, gborder, bborder = color.utils.hex_to_rgba(border_override) + set_border({ rborder, gborder, bborder }) + else + set_border({ rborder * 0.9, gborder * 0.9, bborder * 0.9 }) + end + end + if icon and widget.icon and icon_override and icon_override_hover then + widget.icon.image = gears.color.recolor_image(icon, icon_override_hover) + end + local w = mouse.current_wibox + if w then + old_cursor, old_wibox = w.cursor, w + w.cursor = "hand1" + end + --widget:connect_signal("mouse::leave", mouse_leave) + end + + local button_press = function() + --[[ if old_bg or bg_override then + if bg_override then + bg_override = bg_override .. "bb" + end + widget.bg = bg_override or old_bg .. "bb" + end + if fg_override or old_fg then + if fg_override then + fg_override = fg_override .. "bb" + end + widget.fg = fg_override or old_fg .. "bb" + end ]] + --widget:disconnect_signal("mouse::leave", mouse_leave) + end + widget:connect_signal("mouse::enter", mouse_enter) - --widget:connect_signal("button::press", button_press) + widget:connect_signal("button::press", button_press) --widget:connect_signal("button::release", button_release) widget:connect_signal("mouse::leave", mouse_leave) end diff --git a/awesome/src/lib/overflow_widget/overflow.lua b/awesome/src/lib/overflow_widget/overflow.lua index 7e36a5d..69d55f4 100644 --- a/awesome/src/lib/overflow_widget/overflow.lua +++ b/awesome/src/lib/overflow_widget/overflow.lua @@ -18,6 +18,8 @@ local gshape = require('gears.shape') local gobject = require('gears.object') local mousegrabber = mousegrabber +local rubato = require("src.lib.rubato") + local overflow = { mt = {} } -- Determine the required space to draw the layout's children and, if necessary, @@ -305,6 +307,9 @@ end -- @tparam number scroll_factor The scroll factor. -- @propemits true false +overflow.rubato_timed = rubato.timed { duration = 0.2 } + +local first_call = true function overflow:set_scroll_factor(factor) local current = self._private.scroll_factor local interval = self._private.used_in_dir - self._private.avail_in_dir @@ -318,10 +323,22 @@ function overflow:set_scroll_factor(factor) return end - self._private.scroll_factor = math.min(1, math.max(factor, 0)) - self:emit_signal("widget::layout_changed") - self:emit_signal("property::scroll_factor", factor) + local function update_scroll() + self._private.scroll_factor = overflow.rubato_timed.pos + self:emit_signal("widget::layout_changed") + self:emit_signal("property::scroll_factor", factor) + end + + -- Make sure it only subscribes once + if first_call then + overflow.rubato_timed:subscribe(update_scroll) + --first_call = false + end + + -- Set the target to the new target + remaining target from last scroll. This makes it scroll faster and correctly. + overflow.rubato_timed.target = math.min(1, + math.max(factor + (overflow.rubato_timed.target - overflow.rubato_timed.pos), 0)) end function overflow:get_scroll_factor() diff --git a/awesome/src/lib/rubato b/awesome/src/lib/rubato index d486ee3..b40ecae 160000 --- a/awesome/src/lib/rubato +++ b/awesome/src/lib/rubato @@ -1 +1 @@ -Subproject commit d486ee33a8b96ba76a4d948e341c7e89ba0554a5 +Subproject commit b40ecae70b01b9917df2ba192f058f5e0b768798 diff --git a/awesome/src/modules/application_launcher/init.lua b/awesome/src/modules/application_launcher/init.lua index a64bb0f..7b8cc10 100644 --- a/awesome/src/modules/application_launcher/init.lua +++ b/awesome/src/modules/application_launcher/init.lua @@ -22,7 +22,7 @@ return function(s) spacing = dpi(10), layout = require("src.lib.overflow_widget.overflow").vertical, scrollbar_width = 0, - step = dpi(50), + step = dpi(100), id = "scroll_bar", }, spacing = dpi(10), diff --git a/awesome/src/modules/calendar/calendar.lua b/awesome/src/modules/calendar/calendar.lua new file mode 100644 index 0000000..5cfb5a1 --- /dev/null +++ b/awesome/src/modules/calendar/calendar.lua @@ -0,0 +1,1085 @@ +-------------------------------------- +-- This is the application launcher -- +-------------------------------------- + +-- Awesome Libs +local awful = require("awful") +local dpi = require("beautiful").xresources.apply_dpi +local gears = require("gears") +local wibox = require("wibox") +local ical_parser = require("src.tools.ical_parser") + +local icondir = awful.util.getdir("config") .. "src/assets/icons/calendar/" + +--- Month name lookup table +local months_table = { + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "January", + "February", +} + +--- Table to easily shift back every month by two months +local month_convert = { + 11, + 12, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, +} + +--- Weekdays name lookup table +local weekdays_table = { + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", +} + +--- Create a date object from the current date +local os_date = os.date("%d%m%Y") +local date = { + day = math.floor(os_date:sub(1, 2)), + month = month_convert[math.floor(os_date:sub(3, 4))], + year = math.floor(os_date:sub(5, 8)) +} + +---Calculates the weekday of a given date +---@param day number|string? as number, usually from date.da +---@param month number|string? as number, usually from date.month +---@param year number|string? as number, usually from date.year +---@return number|nil weekday as number +local function get_date_weekday(day, month, year) + if not (day and month and year) then return end + + if (month == 11) or (month == 12) then + year = year - 1 + end + + -- No idea how the algorithm works, but since it works -> don't touch it! + local w = ((day + math.floor(2.6 * month - 0.2) - 2 * tonumber(tostring(year):match("([0-9]+)[0-9][0-9]")) + + tonumber(tostring(year):match("[0-9][0-9]([0-9]+)")) + + math.floor(tonumber(tostring(year):match("[0-9][0-9]([0-9]+)")) / 4) + + math.floor(tonumber(tostring(year):match("([0-9]+)[0-9][0-9]")) / 4)) % 7) + --TODO: Add user variable to choose between Sunday and Monday weekstart + if w == 0 then w = 7 end + return w +end + +---Returns the length of the month from a lookup table and also check for leap years +---@param month number? as number, usually from date.month, can also be a string +---@param year number? as number, usually from date.year, can also be a string +---@return integer|nil month_length as integer +local function get_last_day_of_month(month, year) + if not (month and year) then return end + + month = tonumber(month) + local last_day = { + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31, + 31, + 28, + } + --In this calculcation February is the 12th of last year + if (month == 12) and (math.floor(year % 4) == 0) then + return 29 + else + return last_day[month] + end +end + +---Simple function to calculate how many weeks will need to be displayed in a calendar +---@param month number? as number, usually from date.month, can also be a string +---@param year number? as number, usually from date.year, can also be a string +---@return number|nil weeks ammount of weeks between 4-6 +local function get_weeks_in_month(month, year) + if not (month and year) then return end + return math.ceil((get_last_day_of_month(month, year) + get_date_weekday(1, month, year) - 1) / 7) +end + +---Gets the last month and accounts for year changes +---@param d date date object +---@return table|nil date returns a date object +local function get_last_month(d) + if not (d) then return end + if d.month == 1 then + return { month = 12, year = d.year - 1 } + else + return { month = d.month - 1, year = d.year } + end +end + +---Simple function to create a widget 7x for each day of the week +---@return table weeks_widget All weekdays names as a widget +local function create_weekdays() + local weekdays = { layout = wibox.layout.flex.horizontal } + for i = 1, 7 do + table.insert(weekdays, wibox.widget { + { + text = weekdays_table[i], + align = "center", + valign = "center", + widget = wibox.widget.textbox, + }, + bg = Theme_config.calendar.weekdays.bg, + fg = Theme_config.calendar.weekdays.fg, + widget = wibox.container.background, + }) + end + return weekdays +end + +---Create tasks from ical object +---@return table|nil tasks All tasks as a table +local function create_tasks() + if not ical_parser or not ical_parser.VCALENDAR then return end + local tasks = {} + --TODO: Initialize the timezone from ical.VCALENDAR.VTIMEZONE and make sure the time is correct + + -- Sort every VEVENT in ical by date into the tasks table. + for _, calendar in ipairs(ical_parser.VCALENDAR) do + for _, event in ipairs(calendar.VEVENT) do + local start_time = event.DTSTART.DTSTART + start_time.month = month_convert[start_time.month] + local end_time + if event.DTEND then + end_time = event.DTEND.DTEND + end_time.month = month_convert[end_time.month] + end + + if event.RRULE then + if event.RRULE.FREQ == "DAILY" then + elseif event.RRULE.FREQ == "WEEKLY" then + -- An event will always start on the day it first occurs. + local event_start = start_time + local event_end = event.RRULE.UNTIL + + local year_counter = event_start.year + local month_counter = event_start.month + if month_counter == 11 then + year_counter = year_counter - 1 + elseif month_counter == 12 then + year_counter = year_counter - 1 + end + local day_counter = event_start.day + local task = {} + while (year_counter <= event_end.year) or (month_counter <= event_end.month) or (day_counter <= event_end.day) do + -- First event will always be right since we start on its starting day + task = { + date_start = { + year = year_counter, + month = month_counter, + day = day_counter, + hour = event_start.hour or 0, + minute = event_start.minute or 0, + second = event_start.second or 0 + }, + date_end = { + year = year_counter, + month = month_counter, + day = day_counter, + hour = end_time.hour or 0, + minute = end_time.minute or 0, + second = end_time.second or 0 + }, + summary = event.SUMMARY, + location = event.LOCATION, + } + + if event.VALARM then + task.alarm = { + time = event.VALARM.TRIGGER.TRIGGER + } + local alarm_time = task.alarm.time:match("([-]?%d+)") + local alarm_unit = task.alarm.time:match("(%a)") + if alarm_unit == "W" then + alarm_time = alarm_time * 604800 + elseif alarm_unit == "D" then + alarm_time = alarm_time * 86400 + elseif alarm_unit == "H" then + alarm_time = alarm_time * 3600 + elseif alarm_unit == "M" then + alarm_time = alarm_time * 60 + end + + --[[ gears.timer { + autostart = true, + callback = function() + if alarm_time then + require("naughty").notification { + app_name = "Task Alarm", + title = task.summary, + message = task.description, + urgency = "normal", + timeout = 5, + icon = icondir .. "/alarm.png", + } + gears.timer:stop() + end + end + } ]] + end + + table.insert(tasks, task) + + day_counter = day_counter + 7 + local month_length = get_last_day_of_month(month_counter, year_counter) + if day_counter > month_length then + day_counter = day_counter - month_length + month_counter = month_counter + 1 + end + if month_counter == 11 then + year_counter = year_counter + 1 + end + if month_counter > 13 then + month_counter = 1 + end + end + elseif event.RRULE.FREQ == "MONTHLY" then + elseif event.RRULE.FREQ == "YEARLY" then + if not end_time then + end_time = { + year = start_time.year + 1000, + } + end + + local task = {} + for i = start_time.year, end_time.year, 1 do + task = { + date_start = { + year = i, + month = start_time.month, + day = start_time.day, + hour = start_time.hour or 0, + minute = start_time.minute or 0, + second = start_time.second or 0 + }, + date_end = { + year = i, + month = start_time.month, + day = start_time.day, + hour = end_time.hour or 0, + minute = end_time.minute or 0, + second = end_time.second or 0 + }, + summary = event.SUMMARY, + location = event.LOCATION, + description = event.DESCRIPTION, + url = event.URL, + } + + if event.VALARM then + task.alarm = { + time = event.VALARM.TRIGGER.TRIGGER + } + local alarm_time = task.alarm.time:match("([-]?%d+)") + local alarm_unit = task.alarm.time:match("(%a)") + if alarm_unit == "W" then + alarm_time = alarm_time * 604800 + elseif alarm_unit == "D" then + alarm_time = alarm_time * 86400 + elseif alarm_unit == "H" then + alarm_time = alarm_time * 3600 + elseif alarm_unit == "M" then + alarm_time = alarm_time * 60 + end + + --[[ gears.timer { + timeout = os.time(task.date_start) - os.time() + alarm_time, + autostart = true, + callback = function() + require("naughty").notification { + app_name = "Task Alarm", + title = task.summary, + message = task.description, + urgency = "normal", + timeout = 5, + icon = icondir .. "/alarm.png", + } + end + } ]] + end + + table.insert(tasks, task) + end + end + else + local task = { + date_start = { + year = start_time.year, + month = start_time.month, + day = start_time.day, + hour = start_time.hour or 0, + minute = start_time.minute or 0, + second = start_time.second or 0 + }, + date_end = { + year = start_time.year, + month = start_time.month, + day = start_time.day, + hour = start_time.hour or 0, + minute = start_time.minute or 0, + second = start_time.second or 0 + }, + summary = event.SUMMARY, + location = event.LOCATION, + description = event.DESCRIPTION, + url = event.URL, + } + + if event.VALARM then + task.alarm = { + time = event.VALARM.TRIGGER.TRIGGER + } + local alarm_time = task.alarm.time:match("([-]?%d+)") + local alarm_unit = task.alarm.time:match("(%a)") + if alarm_unit == "W" then + alarm_time = alarm_time * 604800 + elseif alarm_unit == "D" then + alarm_time = alarm_time * 86400 + elseif alarm_unit == "H" then + alarm_time = alarm_time * 3600 + elseif alarm_unit == "M" then + alarm_time = alarm_time * 60 + end + + --[[ gears.timer { + timeout = os.time(task.date_start) - os.time() + alarm_time, + autostart = true, + callback = function() + require("naughty").notification { + app_name = "Task Alarm", + title = task.summary, + message = task.description, + urgency = "normal", + timeout = 5, + icon = icondir .. "/alarm.png", + } + end + } ]] + end + table.insert(tasks, task) + end + end + end + + + return tasks +end + +local tasks = create_tasks() + +local selected_day = { + year = date.year, + month = date.month, + day = date.day, + col = 1, + row = 1, +} + +return function(s) + -- The calendar grid + local calendar_matrix = wibox.widget { layout = wibox.layout.grid } + + local weeks = wibox.widget { layout = wibox.layout.fixed.vertical } + + ---Main function to create the calendar widget + ---Probably needs some refractor at some point since it's a bit messy + ---@return widget calendar_widget + local function create_calendar() + + calendar_matrix:reset() + + --- Months table holds every month with their starting week day, length(30/31 or 28/29), the last week day and the name + local months = {} + for m_num, month in ipairs(months_table) do + months[m_num] = { + name = month, + first_day = get_date_weekday("01", m_num, date.year), + length = get_last_day_of_month(m_num, date.year), + last_day = get_date_weekday(get_last_day_of_month(m_num, date.year), m_num, date.year), + weeks = get_weeks_in_month(m_num, date.year) + } + end + + local function get_tasks_for_day(day, month, year) + if not tasks or #tasks == 0 then return end + local tasks_layout = { + layout = require("src.lib.overflow_widget.overflow").vertical, + scrollbar_width = 0, + step = dpi(50), + spacing = dpi(2) + } + for _, task in ipairs(tasks) do + if (task.date_start.year == year) and (task.date_start.month == month) and (task.date_start.day == day) then + table.insert(tasks_layout, wibox.widget { + { + { + text = task.summary, + align = "left", + halign = "center", + font = "JetBrainsMono Nerd Font, bold 10", + widget = wibox.widget.textbox + }, + margins = dpi(2), + widget = wibox.container.margin + }, + fg = Theme_config.calendar.task.fg, + bg = Theme_config.calendar.task.bg, + shape = Theme_config.calendar.task.shape, + forced_height = dpi(20), + widget = wibox.container.background + }) + end + end + return tasks_layout + end + + if months[date.month].first_day ~= 1 then + -- Fill previous month days, i doubles as the day + local column = 1 + local last_month = get_last_month(date) + local prev_month = date.month + local prev_year = date.year + if date.month == 1 then + prev_month = 12 + last_month = months[12].length + else + last_month = months[date.month - 1].length + end + if date.month == 11 then + prev_year = date.year - 1 + end + prev_month = prev_month - 1 + for i = last_month - months[date.month].first_day + 2, last_month, 1 do + local border = Theme_config.calendar.day.border_color + local bg = Theme_config.calendar.day.bg_unfocus + local fg = Theme_config.calendar.day.fg_unfocus + if column == selected_day.col and 1 == selected_day.row then + border = Theme_config.calendar.day.today_border_color + bg = Theme_config.calendar.day.today_bg_focus + fg = Theme_config.calendar.day.today_fg_focus + end + local y = tonumber(os.date("%Y")) + local m = month_convert[tonumber(os.date("%m"))] + if m == 1 then + m = 12 + end + if (i == date.day) and (m == prev_month) and (date.year == y) then + bg = Theme_config.calendar.day.bg_focus + fg = Theme_config.calendar.day.fg_focus + end + local day = wibox.widget { + { + { + { + { + { + { + { -- Day + widget = wibox.widget.textbox, + align = "center", + valign = "center", + text = math.floor(i), + id = "day_text", + }, + widget = wibox.container.margin, + margins = dpi(2), + }, + id = "day_bg", + widget = wibox.container.background, + bg = bg, + shape = Theme_config.calendar.day.shape, + fg = fg, + }, + widget = wibox.container.place, + valign = "center", + halign = "center", + }, + { + get_tasks_for_day(math.floor(i), prev_month, prev_year), + widget = wibox.container.margin, + margins = dpi(4) + }, + id = "tasks", + spacing = dpi(4), + layout = wibox.layout.fixed.vertical + }, + widget = wibox.container.margin, + top = dpi(4) + }, + id = "background", + widget = wibox.container.background, + bg = Theme_config.calendar.day.bg_unfocus, + fg = Theme_config.calendar.day.fg_unfocus, + border_color = border, + border_width = Theme_config.calendar.day.border_width, + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(0)) + end + }, + widget = wibox.container.constraint, + width = dpi(100), + height = dpi(120), + strategy = "exact" + } + + -- update selected_day if the day is clicked + day:buttons( + gears.table.join( + awful.button({}, 1, function() + selected_day.col = column + selected_day.row = 1 + day:emit_signal("day::update_selected") + end) + ) + ) + + day:connect_signal("day::update_selected", function() + if column == selected_day.col and 1 == selected_day.row then + awesome.emit_signal("day::reset_border") + day.background.border_color = Theme_config.calendar.day.today_border_color + end + end) + + awesome.connect_signal("day::reset_border", function() + day.background.border_color = Theme_config.calendar.day.border_color + end) + + calendar_matrix:add_widget_at(day, 1, column) + column = column + 1 + end + end + + --Actual month days + local row = 1 + local col = months[date.month].first_day + for i = 1, months[date.month].length, 1 do + + local border = Theme_config.calendar.day.border_color + local fg = Theme_config.calendar.day.fg + local bg = Theme_config.calendar.day.bg + if col == selected_day.col and row == selected_day.row then + border = Theme_config.calendar.day.today_border_color + end + + local m = month_convert[tonumber(os.date("%m"))] + local y = tonumber(os.date("%Y")) + if (i == date.day) and (date.month == m) and (date.year == y) then + bg = Theme_config.calendar.day.bg_focus + fg = Theme_config.calendar.day.fg_focus + end + + local day = wibox.widget { + { + { + { + { + { + { + { -- Day + widget = wibox.widget.textbox, + align = "center", + valign = "center", + text = math.floor(i), + id = "day_text", + }, + widget = wibox.container.margin, + margins = dpi(2), + }, + id = "day_bg", + widget = wibox.container.background, + bg = bg, + shape = Theme_config.calendar.day.shape, + fg = fg, + }, + widget = wibox.container.place, + valign = "center", + halign = "center", + }, + { + get_tasks_for_day(math.floor(i), date.month, date.year), + widget = wibox.container.margin, + margins = dpi(4) + }, + id = "tasks", + spacing = dpi(4), + layout = wibox.layout.fixed.vertical + }, + widget = wibox.container.margin, + top = dpi(4) + }, + id = "background", + widget = wibox.container.background, + bg = Theme_config.calendar.day.bg, + fg = Theme_config.calendar.day.fg, + border_color = border, + border_width = Theme_config.calendar.day.border_width, + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(0)) + end + }, + widget = wibox.container.constraint, + width = dpi(100), + height = dpi(120), + strategy = "exact" + } + + -- update selected_day if the day is clicked + day:buttons( + gears.table.join( + awful.button({}, 1, function() + selected_day.col = col + selected_day.row = row + day:emit_signal("day::update_selected") + end) + ) + ) + + day:connect_signal("day::update_selected", function() + if col == selected_day.col and row == selected_day.row then + awesome.emit_signal("day::reset_border") + day.background.border_color = Theme_config.calendar.day.today_border_color + end + end) + + awesome.connect_signal("day::reset_border", function() + day.background.border_color = Theme_config.calendar.day.border_color + end) + + calendar_matrix:add_widget_at(day, row, col) + col = col + 1 + if col == 8 then + col = 1 + row = row + 1 + end + end + + --next month + local next_month = date.month + if date.month == 12 then + next_month = 1 + else + next_month = next_month + 1 + end + + if months[date.month].last_day ~= 7 then + for i = 1, 7 - months[date.month].last_day, 1 do + local border = Theme_config.calendar.day.border_color + local fg = Theme_config.calendar.day.fg_unfocus + local bg = Theme_config.calendar.day.bg_unfocus + if i == selected_day.col and months[date.month].weeks == selected_day.row then + border = Theme_config.calendar.day.today_border_color + end + local m = month_convert[tonumber(os.date("%m")) + 1] + if m == 13 then + m = 1 + end + local y = tonumber(os.date("%Y")) + if (i == date.day) and (next_month == m) and (date.year == y) then + bg = Theme_config.calendar.day.bg_focus + fg = Theme_config.calendar.day.fg_focus + end + local day = wibox.widget { + { + { + { + { + { + { + { -- Day + widget = wibox.widget.textbox, + align = "center", + valign = "center", + text = math.floor(i), + id = "day_text", + }, + widget = wibox.container.margin, + margins = dpi(2), + }, + id = "day_bg", + widget = wibox.container.background, + bg = bg, + shape = Theme_config.calendar.day.shape, + fg = fg, + }, + widget = wibox.container.place, + valign = "center", + halign = "center", + }, + { + get_tasks_for_day(math.floor(i), next_month, date.year), + widget = wibox.container.margin, + margins = dpi(4) + }, + id = "tasks", + spacing = dpi(4), + layout = wibox.layout.fixed.vertical + }, + widget = wibox.container.margin, + top = dpi(4) + }, + id = "background", + widget = wibox.container.background, + bg = Theme_config.calendar.day.bg_unfocus, + fg = Theme_config.calendar.day.fg_unfocus, + border_color = border, + border_width = Theme_config.calendar.day.border_width, + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(0)) + end + }, + widget = wibox.container.constraint, + width = dpi(100), + height = dpi(120), + strategy = "exact" + } + + -- update selected_day if the day is clicked + day:buttons( + gears.table.join( + awful.button({}, 1, function() + selected_day.col = i + selected_day.row = months[date.month].weeks + day:emit_signal("day::update_selected") + end) + ) + ) + + day:connect_signal("day::update_selected", function() + if i == selected_day.col and months[date.month].weeks == selected_day.row then + awesome.emit_signal("day::reset_border") + day.background.border_color = Theme_config.calendar.day.today_border_color + end + end) + + awesome.connect_signal("day::reset_border", function() + day.background.border_color = Theme_config.calendar.day.border_color + end) + calendar_matrix:add_widget_at(day, months[date.month].weeks, months[date.month].last_day + i) + end + end + + return calendar_matrix + end + + local function create_calendar_week_num() + weeks:reset() + local actual_fucking_date = date.month + 2 + if date.month == 11 then + actual_fucking_date = 1 + elseif date.month == 12 then + actual_fucking_date = 2 + end + local start_week = actual_fucking_date * 4 - 3 + local weeknum = actual_fucking_date * 4 - 3 + if get_date_weekday("01", date.month, date.year) ~= 1 then + weeknum = weeknum - 1 + end + if actual_fucking_date == 1 then + weeknum = 52 + end + for i = start_week, start_week + get_weeks_in_month(date.month, date.year) - 1, 1 do + weeks:add(wibox.widget { + { + { + text = weeknum, + id = "num", + align = "center", + valign = "top", + widget = wibox.widget.textbox, + }, + id = "background", + fg = Theme_config.calendar.day.fg_unfocus, + widget = wibox.container.background, + }, + strategy = "exact", + height = dpi(120), + width = dpi(40), + widget = wibox.container.constraint + }) + if weeknum == 52 then + weeknum = 1 + else + weeknum = weeknum + 1 + end + end + return weeks + end + + --- Calendar widget + local calendar = wibox.widget { + { + { + { + { + { + { + { + widget = wibox.widget.imagebox, + resize = false, + image = gears.color.recolor_image(icondir .. "add_ical.svg", Theme_config.calendar.add_ical.fg_focus), + halign = "center", + valign = "center" + }, + id = "add_ical", + shape = Theme_config.calendar.add_ical.shape, + bg = Theme_config.calendar.add_ical.bg, + widget = wibox.container.background + }, + widget = wibox.container.margin, + margins = dpi(4) + }, + { + { + { + widget = wibox.widget.imagebox, + resize = false, + image = gears.color.recolor_image(icondir .. "add_task.svg", Theme_config.calendar.add_task.fg), + halign = "center", + valign = "center" + }, + id = "add_task", + shape = Theme_config.calendar.add_task.shape, + bg = Theme_config.calendar.add_task.bg, + widget = wibox.container.background + }, + widget = wibox.container.margin, + margins = dpi(4) + }, + layout = wibox.layout.fixed.vertical + }, + widget = wibox.container.constraint, + strategy = "exact", + height = dpi(75) + }, + create_calendar_week_num(), + id = "weekdaysnum", + layout = wibox.layout.fixed.vertical + }, + { + { + { --Header + { -- Month switcher + { -- Prev arrow + widget = wibox.widget.imagebox, + resize = true, + image = icondir .. "chevron-left.svg", + valign = "center", + halign = "center", + id = "prev_month", + }, + { + { -- Month + widget = wibox.widget.textbox, + text = months_table[date.month], + id = "month", + valign = "center", + align = "center" + }, + widget = wibox.container.constraint, + strategy = "exact", + width = dpi(150) + }, + { -- Next arrow + widget = wibox.widget.imagebox, + resize = true, + image = icondir .. "chevron-right.svg", + valign = "center", + halign = "center", + id = "next_month", + }, + layout = wibox.layout.fixed.horizontal + }, + nil, + { -- Year switcher + { -- Prev arrow + widget = wibox.widget.imagebox, + resize = true, + image = icondir .. "chevron-left.svg", + valign = "center", + halign = "center", + id = "prev_year" + }, + { + { -- Month + widget = wibox.widget.textbox, + text = date.year, + id = "year", + valign = "center", + align = "center" + }, + widget = wibox.container.constraint, + strategy = "exact", + width = dpi(150) + }, + { -- Next arrow + widget = wibox.widget.imagebox, + resize = true, + image = icondir .. "chevron-right.svg", + valign = "center", + halign = "center", + id = "next_year" + }, + layout = wibox.layout.fixed.horizontal + }, + layout = wibox.layout.align.horizontal + }, + widget = wibox.container.constraint, + height = dpi(40), + strategy = "exact" + }, + { -- Weekdays + create_weekdays(), + widget = wibox.container.background + }, + create_calendar(), + id = "calendar", + spacing = dpi(5), + layout = wibox.layout.fixed.vertical + }, + id = "lay1", + layout = wibox.layout.fixed.horizontal, + }, + widget = wibox.container.background, + bg = Theme_config.calendar.bg, + border_color = Theme_config.calendar.border_color, + border_width = Theme_config.calendar.border_width, + border_strategy = "inner", + fg = Theme_config.calendar.fg, + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(4)) + end + } + + local add_ical = calendar:get_children_by_id("add_ical")[1] + local add_task = calendar:get_children_by_id("add_task")[1] + + add_ical:buttons( + gears.table.join( + awful.button({}, 1, function() + awful.spawn.easy_async_with_shell( + "zenity --file-selection --title='Select an iCalendar file' --file-filter='iCalendar File | *.ics'", + function(path_to_file) + path_to_file = string.gsub(path_to_file, "\n", "") + if not path_to_file then return end + ical_parser.new(path_to_file) + tasks = create_tasks() + calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num() + calendar:get_children_by_id("calendar")[1].children[3] = create_calendar() + end + ) + end) + ) + ) + + Hover_signal(add_ical) + Hover_signal(add_task) + + --- Popup that contains the calendar + local cal_popup = awful.popup { + widget = calendar, + screen = s, + ontop = true, + bg = "#00000000", + visible = false + } + + --- Calendar switch month back + calendar:get_children_by_id("prev_month")[1]:buttons( + gears.table.join( + awful.button({}, 1, function() + date.month = date.month - 1 + if date.month == 0 then + date.month = 12 + end + if date.month == 10 then + date.year = date.year - 1 + end + calendar:get_children_by_id("month")[1].text = months_table[date.month] + calendar:get_children_by_id("year")[1].text = date.year + calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num() + calendar:get_children_by_id("calendar")[1].children[3] = create_calendar() + end) + ) + ) + + --- Calendar switch month forward + calendar:get_children_by_id("next_month")[1]:buttons( + gears.table.join( + awful.button({}, 1, function() + date.month = date.month + 1 + if date.month == 13 then + date.month = 1 + end + if date.month == 11 then + date.year = date.year + 1 + end + calendar:get_children_by_id("month")[1].text = months_table[date.month] + calendar:get_children_by_id("year")[1].text = date.year + calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num() + calendar:get_children_by_id("calendar")[1].children[3] = create_calendar() + end) + ) + ) + + --- Calendar switch year back + calendar:get_children_by_id("prev_year")[1]:buttons( + gears.table.join( + awful.button({}, 1, function() + date.year = date.year - 1 + calendar:get_children_by_id("year")[1].text = date.year + calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num() + calendar:get_children_by_id("calendar")[1].children[3] = create_calendar() + end) + ) + ) + + --- Calendar switch year forward + calendar:get_children_by_id("next_year")[1]:buttons( + gears.table.join( + awful.button({}, 1, function() + date.year = date.year + 1 + calendar:get_children_by_id("year")[1].text = date.year + calendar:get_children_by_id("weekdaysnum")[1].children[2] = create_calendar_week_num() + calendar:get_children_by_id("calendar")[1].children[3] = create_calendar() + end) + ) + ) + + --- Toggle calendar visibility + awesome.connect_signal("calendar::toggle", function(widget) + if s == mouse.screen then + cal_popup.x = 3765 + cal_popup.y = 60 + cal_popup.visible = not cal_popup.visible + end + end) + +end diff --git a/awesome/src/modules/crylia_bar/dock.lua b/awesome/src/modules/crylia_bar/dock.lua index b26c236..8517259 100644 --- a/awesome/src/modules/crylia_bar/dock.lua +++ b/awesome/src/modules/crylia_bar/dock.lua @@ -3,6 +3,7 @@ -------------------------------------------------------------------------------------------------------------- -- Awesome Libs local awful = require("awful") +local async = require("async") local dpi = require("beautiful").xresources.apply_dpi local Gio = require("lgi").Gio local gears = require("gears") @@ -25,7 +26,7 @@ return function(screen) ---@param size number The size of the widget ---@return widox.widget | nil The widget or nil if the program is not found local function create_dock_element(program, size) - if not program then return end + local dock_element = wibox.widget { { { @@ -164,15 +165,14 @@ return function(screen) end local prog = json:decode(data:read("a")) - if (not prog) or prog == "" then return end + data:close() for _, pr in ipairs(prog) do local indicators = { layout = wibox.layout.flex.horizontal, spacing = dpi(5) } local col = Theme_config.dock.indicator_bg for _, c in ipairs(client.get()) do local icon_name = pr.icon - if not c.class then return end - if icon_name:match(string.lower(c.class)) or c.class:match(string.lower(icon_name)) or - (string.lower(c.name) == string.lower(icon_name)) or c.name:match(string.lower(icon_name)) then + if icon_name:match(string.lower(c.class or c.name)) or c.class:match(string.lower(icon_name)) or + c.name:match(string.lower(icon_name)) then if c == client.focus then col = Theme_config.dock.indicator_focused_bg elseif c.urgent then @@ -253,9 +253,7 @@ return function(screen) return end local dock_data = json:decode(data:read("a")) - if (not dock_data) or dock_data == "" then - return - end + data:close() for _, program in ipairs(dock_data) do table.insert(dock_elements, create_dock_element(program, User_config.dock_icon_size)) end diff --git a/awesome/src/modules/init.lua b/awesome/src/modules/init.lua index bb70c77..6013147 100644 --- a/awesome/src/modules/init.lua +++ b/awesome/src/modules/init.lua @@ -27,5 +27,6 @@ awful.screen.connect_for_each_screen( require("src.modules.notification-center.init")(s) require("src.modules.window_switcher.init")(s) require("src.modules.application_launcher.init")(s) + require("src.modules.calendar.calendar")(s) end ) diff --git a/awesome/src/modules/notification-center/init.lua b/awesome/src/modules/notification-center/init.lua index 0719124..9d18df2 100644 --- a/awesome/src/modules/notification-center/init.lua +++ b/awesome/src/modules/notification-center/init.lua @@ -130,11 +130,46 @@ return function(s) widget = wibox.container.place, } + local rubato = require("src.lib.rubato") + + local rubato_timed = rubato.timed { duration = 1, pos = 0 } + local toggle_button = wibox.widget { { - left_button, - right_button, - widget = wibox.layout.flex.horizontal + id = "background", + widget = wibox.widget { + fit = function(_, width, height) + return width, height + end, + draw = function(_, _, cr, width, height) + -- Clear for next drawing + --cr:set_operator(cairo.Operator.CLEAR); + local function move_dnd() + cr:set_source(gears.color(Theme_config.notification_center.dnd.bg)); + cr:paint(); + cr:set_source(gears.color(Theme_config.notification_center.dnd.disabled)) + cr:move_to(rubato_timed.pos, 0) + local x = rubato_timed.pos + local y = 5 + local newwidth = width / 2 - 10 + local newheight = height - 10 + + local radius = height / 6.0 + local degrees = math.pi / 180.0; + + cr:new_sub_path() + cr:arc(x + newwidth - radius, y + radius, radius, -90 * degrees, 0 * degrees) + cr:arc(x + newwidth - radius, y + newheight - radius, radius, 0 * degrees, 90 * degrees) + cr:arc(x + radius, y + newheight - radius, radius, 90 * degrees, 180 * degrees) + cr:arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) + cr:close_path() + cr:fill() + end + + rubato_timed:subscribe(move_dnd) + rubato_timed.target = width / 2 + 5 + end + } }, active = false, widget = wibox.container.background, @@ -152,22 +187,20 @@ return function(s) "button::press", function() if toggle_button.active then - left_button.visible = true - right_button.visible = false toggle_button.active = not toggle_button.active toggle_button.border_color = Theme_config.notification_center.dnd.border_disabled User_config.dnd = false + rubato_timed.target = 5 else - left_button.visible = false - right_button.visible = true toggle_button.active = not toggle_button.active toggle_button.border_color = Theme_config.notification_center.dnd.border_enabled User_config.dnd = true + rubato_timed.target = 50 end end ) - local dnd = wibox.widget { -- Clear all button + local dnd = wibox.widget { { { { @@ -348,12 +381,22 @@ return function(s) end ) + local function mouse_leave() + notification_center.visible = false + end + + awesome.connect_signal("notification_center::block_mouse_events", function() + notification_center:disconnect_signal("mouse::leave", mouse_leave) + end) + + awesome.connect_signal("notification_center::unblock_mouse_events", function() + notification_center:connect_signal("mouse::leave", mouse_leave) + end) + -- Hide notification_center when mouse leaves it notification_center:connect_signal( "mouse::leave", - function() - notification_center.visible = false - end + mouse_leave ) -- Clear all notifications on button press diff --git a/awesome/src/modules/notification-center/notification_list.lua b/awesome/src/modules/notification-center/notification_list.lua index 51475f7..eb7efa9 100644 --- a/awesome/src/modules/notification-center/notification_list.lua +++ b/awesome/src/modules/notification-center/notification_list.lua @@ -15,7 +15,7 @@ local icondir = awful.util.getdir("config") .. "src/assets/icons/notifications/" local nl = {} nl.notification_list = { layout = require("src.lib.overflow_widget.overflow").vertical, scrollbar_width = 0, - step = dpi(20), spacing = dpi(20) } + step = dpi(100), spacing = dpi(20) } -- @param {table} notification -- @return {widget} notifications_list diff --git a/awesome/src/modules/notification-center/status_bars.lua b/awesome/src/modules/notification-center/status_bars.lua index abe2b98..19ac63f 100644 --- a/awesome/src/modules/notification-center/status_bars.lua +++ b/awesome/src/modules/notification-center/status_bars.lua @@ -90,6 +90,14 @@ return function() margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) + awesome.connect_signal( "update::cpu_usage", function(cpu_usage) @@ -160,7 +168,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "update::cpu_temp", function(cpu_temp) @@ -239,7 +253,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "update::ram_widget", function(MemTotal, _, MemAvailable) @@ -312,7 +332,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "update::gpu_usage", function(gpu_usage) @@ -383,7 +409,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "update::gpu_temp", function(gpu_temp) @@ -472,7 +504,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "audio::get", function(muted, volume) @@ -563,7 +601,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "microphone::get", function(muted, volume) @@ -647,22 +691,20 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( - "brightness::get", - function(brightness) - local icon = icondir .. "brightness/brightness" - if brightness >= 0 and brightness < 34 then - icon = icon .. "-low" - elseif brightness >= 34 and brightness < 67 then - icon = icon .. "-medium" - elseif brightness >= 67 then - icon = icon .. "-high" - end - w:get_children_by_id("icon1")[1].image = gears.color.recolor_image(icon .. ".svg", + "update::backlight", + function(backlight, backlight_icon) + w:get_children_by_id("icon1")[1].image = gears.color.recolor_image(backlight_icon, Theme_config.notification_center.status_bar.backlight_color) - tooltip.text = "Backlight: " .. brightness .. "%" - rubato_timer.target = brightness + tooltip.text = "Backlight: " .. backlight .. "%" + rubato_timer.target = backlight end ) elseif widget == "battery" then @@ -728,7 +770,13 @@ return function() preferred_alignments = "middle", margins = dpi(10) } + w:connect_signal("mouse::enter", function() + awesome.emit_signal("notification_center::block_mouse_events") + end) + w:connect_signal("mouse::leave", function() + awesome.emit_signal("notification_center::unblock_mouse_events") + end) awesome.connect_signal( "update::battery_widget", function(battery, battery_icon) diff --git a/awesome/src/modules/volume_controller.lua b/awesome/src/modules/volume_controller.lua index 7216706..42131a1 100644 --- a/awesome/src/modules/volume_controller.lua +++ b/awesome/src/modules/volume_controller.lua @@ -68,11 +68,13 @@ return function(s) Theme_config.volume_controller.device_icon_color) device.bg = Theme_config.volume_controller.device_headphones_selected_bg device.fg = Theme_config.volume_controller.device_headphones_selected_fg + Hover_signal(device) else device:get_children_by_id("icon")[1].image = gears.color.recolor_image(icondir .. "headphones.svg", Theme_config.volume_controller.device_headphones_selected_icon_color) device.bg = Theme_config.volume_controller.device_bg device.fg = Theme_config.volume_controller.device_headphones_fg + Hover_signal(device) end end ) @@ -85,16 +87,17 @@ return function(s) Theme_config.volume_controller.device_icon_color) device.bg = Theme_config.volume_controller.device_headphones_selected_bg device.fg = Theme_config.volume_controller.device_headphones_selected_fg + Hover_signal(device) else device:get_children_by_id("icon")[1].image = gears.color.recolor_image(icondir .. "headphones.svg", Theme_config.volume_controller.device_headphones_selected_icon_color) device.bg = Theme_config.volume_controller.device_bg device.fg = Theme_config.volume_controller.device_headphones_fg + Hover_signal(device) end end ) awesome.emit_signal("update::bg_sink", node) - Hover_signal(device) else device:connect_signal( "button::press", @@ -115,11 +118,13 @@ return function(s) Theme_config.volume_controller.device_icon_color) device.bg = Theme_config.volume_controller.device_microphone_selected_bg device.fg = Theme_config.volume_controller.device_microphone_selected_fg + Hover_signal(device) else device:get_children_by_id("icon")[1].image = gears.color.recolor_image(icondir .. "microphone.svg", Theme_config.volume_controller.device_microphone_selected_icon_color) device.bg = Theme_config.volume_controller.device_bg device.fg = Theme_config.volume_controller.device_microphone_fg + Hover_signal(device) end end ) @@ -132,16 +137,17 @@ return function(s) Theme_config.volume_controller.device_icon_color) device.bg = Theme_config.volume_controller.device_microphone_selected_bg device.fg = Theme_config.volume_controller.device_microphone_selected_fg + Hover_signal(device) else device:get_children_by_id("icon")[1].image = gears.color.recolor_image(icondir .. "microphone.svg", Theme_config.volume_controller.device_microphone_selected_icon_color) device.bg = Theme_config.volume_controller.device_bg device.fg = Theme_config.volume_controller.device_microphone_fg + Hover_signal(device) end end ) awesome.emit_signal("update::bg_source", node) - Hover_signal(device) end return device end diff --git a/awesome/src/theme/theme_config.lua b/awesome/src/theme/theme_config.lua index bbe03f7..31cddf0 100644 --- a/awesome/src/theme/theme_config.lua +++ b/awesome/src/theme/theme_config.lua @@ -144,6 +144,52 @@ Theme_config.tasklist = { ]] -- --#region Module Settings +Theme_config.calendar = { + bg = color["Grey900"], + fg = color["Grey100"], + border_color = color["Grey800"], + border_width = dpi(2), + day = { + today_border_color = color["Blue200"], + bg = color["Grey900"], + bg_focus = color["Teal200"], + bg_unfocus = color["Grey900"], + fg = color["Grey100"], + fg_focus = color["Grey900"], + fg_unfocus = color["Grey600"], + border_color = color["Grey800"], + border_width = dpi(2), + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(4)) + end, + }, + task = { + bg = color["Purple200"], + fg = color["Grey900"], + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(4)) + end, + }, + weekdays = { + bg = color["Grey900"], + fg = color["Blue200"] + }, + add_ical = { + bg = color["Red200"], + fg = color["Grey900"], + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(4)) + end, + }, + add_task = { + bg = color["LightBlue200"], + fg = color["Grey900"], + shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, dpi(4)) + end, + } +} + Theme_config.notification = { border_color = color["Grey800"], border_width = dpi(4), diff --git a/awesome/src/theme/user_config.lua b/awesome/src/theme/user_config.lua index c5c42aa..a14fe93 100644 --- a/awesome/src/theme/user_config.lua +++ b/awesome/src/theme/user_config.lua @@ -20,9 +20,11 @@ User_config = { "picom --experimental-backends", "xfce4-power-manager", "light-locker --lock-on-suspend --lock-on-lid &", + "spotify", + "discord", "/usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1", "setxkbmap -option caps:swapescape", - "protonmail-bridge" + "jamesdsp" }, --[[ @@ -45,7 +47,7 @@ User_config = { "MEDIA_PLAYER" More information at: https://lazka.github.io/pgi-docs/UPowerGlib-1.0/enums.html#UPowerGlib.DeviceKind.KEYBOARD ]] -- - battery_kind = "BATTERY", + battery_kind = "LINE_POWER", --[[ If your battery is not found you can specify its path here. @@ -191,8 +193,8 @@ User_config = { "ram_usage", "microphone", "volume", - "backlight", - "battery" + "gpu_temp", + "gpu_usage" }, --[[ @@ -275,8 +277,10 @@ User_config = { "Tasklist" }, right_bar = { - "Battery", - "Bluetooth", + "Gpu Usage", + "Gpu Temperature", + "Cpu Usage", + "Cpu Temperature", "Audio", "Keyboard Layout", "Date", diff --git a/awesome/src/tools/gio_icon_lookup.lua b/awesome/src/tools/gio_icon_lookup.lua index b0479d8..c43411a 100644 --- a/awesome/src/tools/gio_icon_lookup.lua +++ b/awesome/src/tools/gio_icon_lookup.lua @@ -17,7 +17,6 @@ Gtk.IconTheme.set_custom_theme(gtk_theme, User_config.icon_theme) ---@param app Gio.AppInfo ---@return string path function Get_gicon_path(app) - if not app then return "" end local icon_info = gtk_theme:lookup_by_gicon(app, 64, 0) if icon_info then local path = icon_info:get_filename() @@ -37,12 +36,12 @@ function Get_icon(class, name) name = string.lower(name or "") for _, app in ipairs(app_list) do local desktop_app_info = Gio.DesktopAppInfo.new(app_info.get_id(app)) - local icon_string = Gio.DesktopAppInfo.get_string(desktop_app_info, "Name") + local icon_string = Gio.DesktopAppInfo.get_string(desktop_app_info, "Icon") if icon_string then icon_string = string.lower(icon_string) if icon_string:match(class) or class:match(icon_string) then return Get_gicon_path(app_info.get_icon(app)) - elseif (icon_string == name) or icon_string:match(name) or name:match(icon_string) then + elseif icon_string:match(name) or name:match(icon_string) then return Get_gicon_path(app_info.get_icon(app)) end end diff --git a/awesome/src/tools/helpers/backlight.lua b/awesome/src/tools/helpers/backlight.lua index 66a814f..c80388c 100644 --- a/awesome/src/tools/helpers/backlight.lua +++ b/awesome/src/tools/helpers/backlight.lua @@ -3,7 +3,7 @@ local awful = require("awful") BACKLIGHT_MAX_BRIGHTNESS = 0 BACKLIGHT_SEPS = 0 awful.spawn.easy_async_with_shell( - "xfpm-power-backlight-helper --get-max-brightness", + "pkexec xfpm-power-backlight-helper --get-max-brightness", function(stdout) BACKLIGHT_MAX_BRIGHTNESS = tonumber(stdout) BACKLIGHT_SEPS = BACKLIGHT_MAX_BRIGHTNESS / 100 @@ -15,7 +15,7 @@ awesome.connect_signal( "brightness::update", function() awful.spawn.easy_async_with_shell( - "xfpm-power-backlight-helper --get-brightness", + "pkexec xfpm-power-backlight-helper --get-brightness", function(value) awesome.emit_signal("brightness::get", math.floor((tonumber(value) - 1) / (BACKLIGHT_MAX_BRIGHTNESS - 1) * 100)) awesome.emit_signal("brightness::rerun") @@ -23,5 +23,3 @@ awesome.connect_signal( ) end ) - -awesome.emit_signal("brightness::update") diff --git a/awesome/src/tools/helpers/init.lua b/awesome/src/tools/helpers/init.lua index ac312c2..87470cb 100644 --- a/awesome/src/tools/helpers/init.lua +++ b/awesome/src/tools/helpers/init.lua @@ -5,4 +5,4 @@ require("src.tools.helpers.ram") require("src.tools.helpers.gpu_usage") require("src.tools.helpers.gpu_temp") require("src.tools.helpers.audio") -require("src.tools.helpers.backlight") +--require("src.tools.helpers.backlight") diff --git a/awesome/src/tools/ical_parser.lua b/awesome/src/tools/ical_parser.lua new file mode 100644 index 0000000..7490f99 --- /dev/null +++ b/awesome/src/tools/ical_parser.lua @@ -0,0 +1,332 @@ +local ical = {} +ical.VCALENDAR = {} +ical._private = {} +ical._private.parser = {} + +--[[ + The structure of ical looks like this: + ical = { + VCALENDAR = { + PRODID = "...", + VERSION = "...", + CALSCALE = "...", + METHOD = "...", + X-WR-CALNAME = "...", + X-WR-CALDESC = "...", + X-WR-TIMEZONE = "...", + VTIMEZONE = { + TZID = "...", + TZURL = "...", + X-LIC-LOCATION = "...", + STANDARD = { + TZOFFSETFROM = "...", + TZOFFSETTO = "...", + TZNAME = "...", + DTSTART = "...", + TZRDATE = "...", + TZRULE = "...", + TZUNTIL = "...", + TZPERIOD = "...", + }, + DAYLIGHT = { + TZOFFSETFROM = "...", + TZOFFSETTO = "...", + TZNAME = "...", + DTSTART = "...", + TZRDATE = "...", + TZRULE = "...", + TZUNTIL = "...", + TZPERIOD = "...", + }, + }, + VEVENT = { + UID = "...", + DTSTAMP = "...", + DTSTART = "...", + DTEND = "...", + SUMMARY = "...", + DESCRIPTION = "...", + LOCATION = "...", + RRULE = "...", + RDATE = "...", + EXDATE = "...", + CLASS = "...", + STATUS = "...", + TRANSP = "...", + SEQUENCE = "...", + ORGANIZER = "...", + ATTENDEE = "...", + CATEGORIES = "...", + PRIORITY = "...", + URL = "...", + UID = "...", + DTSTAMP = "...", +]] + +---Takes a path to an .ical file then parses it into a lua table and returns it +---@param path string Path to the .ical file +---@return table | nil calendar New calendar table or nil on error +function ical.new(path) + local handler = io.open(path, "r") + if not handler then return end + + -- Check if the line is a BEGIN:VCALENDAR + local v, k = handler:read("l"):match("([A-Z]+):([A-Z]+)") + + if v:match("BEGIN") and k:match("VCALENDAR") then + table.insert(ical.VCALENDAR, ical._private.parser.VCALENDAR(handler)) + return ical + end + return ical +end + +function ical._private.parser.VEVENT(handler) + local VEVENT = {} + + while true do + local line = handler:read("l") + + if not line or line:match("END:VEVENT") then + break + end + + local v, k = line:match("(.*):(.*)") + if v:match("CREATED") then + VEVENT.CREATED = ical._private.parser.to_datetime(k) + elseif v:match("LAST-MODIFIED") then + VEVENT.LAST_MODIFIED = ical._private.parser.to_datetime(k) + elseif v:match("DTSTAMP") then + VEVENT.DTSTAMP = ical._private.parser.to_datetime(k) + elseif v:match("UID") then + VEVENT.UID = k + elseif v:match("SUMMARY") then + VEVENT.SUMMARY = k + elseif v:match("RRULE") then + VEVENT.RRULE = { + FREQ = k:match("FREQ=([A-Z]+)"), + UNTIL = ical._private.parser.to_datetime(k:match("UNTIL=([TZ0-9]+)")), + WKST = k:match("WKST=([A-Z]+)"), + COUNT = k:match("COUNT=([0-9]+)"), + INTERVAL = k:match("INTERVAL=([0-9]+)") + } + elseif v:match("DTSTART") then + VEVENT.DTSTART = { + DTSTART = ical._private.parser.to_datetime(k), + TZID = v:match("TZID=([a-zA-Z-\\/]+)"), + VALUE = v:match("VALUE=([A-Z]+)") + } + elseif v:match("DTEND") then + VEVENT.DTEND = { + DTEND = ical._private.parser.to_datetime(k), + TZID = v:match("TZID=([a-zA-Z-\\/]+)"), + VALUE = v:match("VALUE=([A-Z]+)") + } + elseif v:match("TRANSP") then + VEVENT.TRANSP = k + elseif v:match("LOCATION") then + VEVENT.LOCATION = k + elseif v:match("DESCRIPTION") then + VEVENT.DESCRIPTION = k + elseif v:match("URL") then + VEVENT.URL = { + URL = k, + VALUE = v:match("VALUE=([A-Z]+)") + } + elseif v:match("BEGIN") then + if k:match("VALARM") then + VEVENT.VALARM = ical._private.parser.VALARM(handler) + end + end + end + + --VEVENT.duration = VEVENT.DTSTART.DTSTART - VEVENT.DTEND.DTEND + + return VEVENT +end + +function ical._private.parser.alarm_to_time(alarm) + if not alarm then return end + --Parse alarm into a time depending on its value. The value will be -PT15M where - mean before and with no leading - it means after. PT can be ignored and 15 is the time M then the unit where M is minute, H is out etc + local time = alarm:match("([-]?[0-9]+)[A-Z]") + local unit = alarm:match("[-]?[A-Z][A-Z][0-9]+([A-Z]*)") + + return time .. unit +end + +function ical._private.parser.VALARM(handler) + local VALARM = {} + + while true do + local line = handler:read("l") + + if not line or line:match("END:VALARM") then + break + end + + local v, k = line:match("(.*):(.*)") + if v:match("ACTION") then + VALARM.ACTION = k + elseif v:match("TRIGGER;VALUE=DURATION") then + VALARM.TRIGGER = { + VALUE = v:match("VALUE=(.*):"), + TRIGGER = ical._private.parser.alarm_to_time(k) + } + elseif v:match("DESCRIPTION") then + VALARM.DESCRIPTION = k + end + end + + return VALARM +end + +function ical._private.parser.VCALENDAR(handler) + local VCALENDAR = {} + VCALENDAR.VEVENT = {} + VCALENDAR.VTIMEZONE = {} + + while true do + local line = handler:read("l") + + if not line or line:match("END:VCALENDAR") then + break + end + + local v, k = line:match("(.*):(.*)") + if v and k then + if v:match("PRODID") then + VCALENDAR.PRODID = k + elseif v:match("VERSION") then + VCALENDAR.VERSION = k + elseif v:match("BEGIN") then + if k:match("VTIMEZONE") then + VCALENDAR.VTIMEZONE = ical._private.parser.VTIMEZONE(handler) + elseif k:match("VEVENT") then + table.insert(VCALENDAR.VEVENT, ical._private.parser.VEVENT(handler)) + end + end + end + end + + handler:close() + return VCALENDAR +end + +function ical._private.parser.VTIMEZONE(handler) + local VTIMEZONE = {} + + while true do + local line = handler:read("l") + + if not line or line:match("END:VTIMEZONE") then + break + end + + local v, k = line:match("(.*):(.*)") + if v:match("TZID") then + VTIMEZONE.TZID = k + end + if v:match("BEGIN") then + if k:match("DAYLIGHT") then + VTIMEZONE.DAYLIGHT = ical._private.parser.DAYLIGHT(handler) + elseif k:match("STANDARD") then + VTIMEZONE.STANDARD = ical._private.parser.STANDARD(handler) + end + end + end + + return VTIMEZONE +end + +function ical._private.parser.DAYLIGHT(handler) + local DAYLIGHT = {} + + while true do + local line = handler:read("l") + + if not line or line:match("END:DAYLIGHT") then + break + end + + local v, k = line:match("(.*):(.*)") + if v:match("TZOFFSETFROM") then + DAYLIGHT.TZOFFSETFROM = ical._private.parser.offset(k) + elseif v:match("TZOFFSETTO") then + DAYLIGHT.TZOFFSETTO = ical._private.parser.offset(k) + elseif v:match("TZNAME") then + DAYLIGHT.TZNAME = k + elseif v:match("DTSTART") then + DAYLIGHT.DTSTART = ical._private.parser.to_datetime(k) + elseif v:match("RRULE") then + DAYLIGHT.RRULE = { + FREQ = k:match("FREQ=([A-Z]+)"), + BYDAY = k:match("BYDAY=([%+%-0-9A-Z,]+)"), + BYMONTH = k:match("BYMONTH=([0-9]+)") + } + end + end + + return DAYLIGHT +end + +---Parses the STANDARD property into a table +---@param handler table +---@return table STANDARD The STANDARD property as a table +function ical._private.parser.STANDARD(handler) + local STANDARD = {} + + -- Read each line until END:STANDARD is read + while true do + local line = handler:read("l") + + if not line or line:match("END:STANDARD") then + break + end + + -- Break down each line into the property:value + local v, k = line:match("(.*):(.*)") + if v:match("TZOFFSETFROM") then + STANDARD.TZOFFSETFROM = ical._private.parser.offset(k) + elseif v:match("TZOFFSETTO") then + STANDARD.TZOFFSETTO = ical._private.parser.offset(k) + elseif v:match("TZNAME") then + STANDARD.TZNAME = k + elseif v:match("DTSTART") then + STANDARD.DTSTART = ical._private.parser.to_datetime(k) + elseif v:match("RRULE") then + STANDARD.RRULE = { + FREQ = k:match("FREQ=([A-Z]+)"), + BYDAY = k:match("BYDAY=([%+%-0-9A-Z,]+)"), + BYMONTH = k:match("BYMONTH=([0-9]+)") + } + end + end + + return STANDARD +end + +---Parse the ical date time format into an os.time integer and the utc +---@param datetime string The datetime from the ical +---@return date|nil time Parsed os.time() +---@return unknown|nil utc UTC identifier +function ical._private.parser.to_datetime(datetime) + if not datetime then return end + local dt, utc = {}, nil + + dt.year, dt.month, dt.day = datetime:match("^(%d%d%d%d)(%d%d)(%d%d)") + dt.hour, dt.min, dt.sec, utc = datetime:match("T(%d%d)(%d%d)(%d%d)(Z?)") + + if (dt.hour == nil) or (dt.min == nil) or (dt.sec == nil) then + dt.hour, dt.min, dt.sec, utc = 0, 0, 0, nil + end + + for k, v in pairs(dt) do dt[k] = tonumber(v) end + return dt, utc +end + +function ical._private.parser.offset(offset) + local s, h, m = offset:match("([+-])(%d%d)(%d%d)") + if s == "+" then s = 1 else s = -1 end + return s * (tonumber(h) * 3600 + tonumber(m) * 60) +end + +return ical diff --git a/awesome/src/widgets/date.lua b/awesome/src/widgets/date.lua index e631692..7b415fa 100644 --- a/awesome/src/widgets/date.lua +++ b/awesome/src/widgets/date.lua @@ -62,5 +62,13 @@ return function() -- Signals Hover_signal(date_widget) + date_widget:buttons { + gears.table.join( + awful.button({}, 1, function() + awesome.emit_signal("calendar::toggle", date_widget) + end) + ) + } + return date_widget end diff --git a/awesome/src/widgets/kblayout.lua b/awesome/src/widgets/kblayout.lua index b443c2d..6a9d2a4 100644 --- a/awesome/src/widgets/kblayout.lua +++ b/awesome/src/widgets/kblayout.lua @@ -214,6 +214,8 @@ return function(s) keymap = keymap } + Hover_signal(kb_layout_item) + awesome.connect_signal( "update::background:kblayout", function()