You are not logged in.

#1 2026-03-15 11:25:32

amolnaristvan
Member
Registered: 2025-02-10
Posts: 2

Modular Conky Widgets with Plasma Color Integration – Locale-Aware

Two modular Conky widgets built with Cairo and full Plasma color integration.
Both widgets follow the system locale (no hardcoded language), so month names and weekday labels automatically match the user’s environment.
The Clock and Calendar modules are finished and fully functional, using a shared common.lua framework for background rendering, theme updates, and Cairo helpers.

The goal is to build a complete, consistent Conky widget pack for Plasma, with additional modules (CPU, RAM, Disk, Network, GPU, Weather) currently in development.
Feedback, ideas, and suggestions from the community are very welcome.
3rOjnGk.png   lfhJ27U.png

common.lua

  -- PLASMA COLORS & AUTO-UPDATE LOGIC
PLASMA = PLASMA or {}
local last_color_update = 0
local color_update_interval = 5 -- 5 másodpercenként frissít

local function parse_rgb_to_table(str, alpha)
    local r, g, b = str:match("(%d+),(%d+),(%d+)")
    if not r then return { 1, 1, 1, alpha or 1 } end
    return { tonumber(r) / 255, tonumber(g) / 255, tonumber(b) / 255, alpha or 1 }
end

local function load_kdeglobals()
    local path = os.getenv("HOME") .. "/.config/kdeglobals"
    local f = io.open(path, "r")
    if not f then return end

    local section = nil
    -- Meglévő táblát frissítünk, nem töröljük le az egészet
    for line in f:lines() do
        local s = line:match("^%[(.+)%]$")
        if s then
            section = s
        else
            local key, val = line:match("^([^=]+)=([%d,]+)")
            if not key or not val then goto continue end

            -- WINDOW COLORS (Ablakkeret/Sávok)
            if section == "Colors:Window" then
                if key == "BackgroundNormal" then
                    PLASMA.bg_panel = parse_rgb_to_table(val, 1.0)
                elseif key == "ForegroundNormal" then
                    PLASMA.text_main = parse_rgb_to_table(val, 1.0)
                elseif key == "ForegroundInactive" then
                    PLASMA.text_dim = parse_rgb_to_table(val, 0.7)
                end
                -- VIEW COLORS (Dolphin háttér - ez a nyerő!)
            elseif section == "Colors:View" then
                if key == "BackgroundNormal" then
                    PLASMA.bg_widget = parse_rgb_to_table(val, 1.0)
                elseif key == "BackgroundAlternate" then
                    PLASMA.bg_alt = parse_rgb_to_table(val, 1.0)
                end
                -- SELECTION (Accent)
            elseif section == "Colors:Selection" then
                if key == "BackgroundNormal" then
                    PLASMA.accent = parse_rgb_to_table(val, 1.0)
                end
                -- BUTTON (Pozitív/Negatív)
            elseif section == "Colors:Button" then
                if key == "ForegroundPositive" then
                    PLASMA.positive = parse_rgb_to_table(val, 1.0)
                elseif key == "ForegroundNegative" then
                    PLASMA.negative = parse_rgb_to_table(val, 1.0)
                end
            end
            ::continue::
        end
    end
    f:close()

    PLASMA.bg_panel  = PLASMA.bg_panel or { 0.15, 0.17, 0.19, 1.0 }
    PLASMA.bg_widget = PLASMA.bg_widget or { 0.12, 0.14, 0.16, 1.0 }
    PLASMA.text_main = PLASMA.text_main or { 1.0, 1.0, 1.0, 1.0 }
    PLASMA.text_dim  = PLASMA.text_dim or { 0.7, 0.7, 0.7, 1.0 }
    PLASMA.accent    = PLASMA.accent or { 0.24, 0.6, 0.95, 1.0 }
end


function check_theme_update()
    local now = os.time()
    if now - last_color_update >= color_update_interval then
        load_kdeglobals()
        last_color_update = now
    end
end


load_kdeglobals()
-- COMMON TOOLS
--{{{
require 'cairo'
local ok, cairo_xlib = pcall(require, 'cairo_xlib')
if not ok then
    cairo_xlib = setmetatable({}, {
        __index = function(_, k) return _G[k] end
    })
end
function conky_ready()
    local u = tonumber(conky_parse("${updates}")) or 0
    if u < 5 then return false end
    if conky_window == nil then return false end
    if conky_window.width == 0 or conky_window.height == 0 then return false end
    return true
end

-- CACHE
cache = cache or {}
function cached(key, interval, func)
    local now = os.time()
    local entry = cache[key]
    if entry and (now - entry.time < interval) then
        return entry.value
    end
    local value = func()
    cache[key] = { time = now, value = value }
    return value
end

function pread(cmd)
    local f = io.popen(cmd)
    if not f then
        return ""
    end
    local out = f:read("*a") or ""
    f:close()
    return out:gsub("%s+$", "")
end

function read_file(path)
    local f = io.open(path, "r")
    if not f then
        return ""
    end
    local out = f:read("*a") or ""
    f:close()
    return out:gsub("%s+$", "")
end

function has_cmd(cmd)
    local ok = os.execute("command -v " .. cmd .. " >/dev/null 2>&1")
    return ok ~= nil and ok ~= false
end

function normalize_number(v)
    if not v then return 0 end
    v = tostring(v):gsub(",", ".")
    v = v:match("([%d%.]+)")
    return tonumber(v) or 0
end

function normalize_with_suffix(raw)
    if not raw then return 0 end
    raw = tostring(raw):lower():gsub("%s+", "")
    raw = raw:gsub("b$", "")
    local num = raw:match("([%d%.]+)")
    if not num then return 0 end
    local base = normalize_number(num)
    local suf = raw:match("([kmg])$")
    if suf == "k" then return base * 1024 end
    if suf == "m" then return base * 1024 * 1024 end
    if suf == "g" then return base * 1024 * 1024 * 1024 end
    return base
end

function draw_allowed(flag)
    if flag == nil or flag == true then return true end
    return conky_parse(tostring(flag)) == "1"
end

local function parse_rgb(str)
    local r, g, b = str:match("(%d+),(%d+),(%d+)")
    return tonumber(r) / 255, tonumber(g) / 255, tonumber(b) / 255
end


--}}}
-- BACKGROUND

backgrounds = backgrounds or {}

-- Alap háttér preset (minden widget ezt használja, ha nem adsz mást)
backgrounds.default = {
    draw_me      = true, -- rajzoljuk-e
    x            = 0,    -- bal felső sarok X
    y            = 0,    -- bal felső sarok Y
    w            = 0,    -- 0 = automatikus (conky_window.width)
    h            = 0,    -- 0 = automatikus (conky_window.height)
    radius       = 10,   -- lekerekítés sugara
    border_width = 1,    -- keret vastagsága

    -- Színek:
    -- nil → automatikusan PLASMA.bg_widget / PLASMA.text_dim
    bg_color     = nil,
    border_color = nil,
}

-- Ha valaha több stílust akarsz:
-- backgrounds.rounded = { ... }
-- backgrounds.flat    = { ... }
-- backgrounds.panel   = { ... }


BACKGROUND_DEFAULT = {
    x            = 0,
    y            = 0,
    radius       = 10,
    border_width = 1,
}

function draw_background(cr, cfg)
    -- 1. Alapértelmezett értékek betöltése (DE a színeket nem mentjük el a cfg-be, ha nil-ek!)
    local x = cfg.x or BACKGROUND_DEFAULT.x
    local y = cfg.y or BACKGROUND_DEFAULT.y
    local r = cfg.radius or BACKGROUND_DEFAULT.radius
    local border_w = cfg.border_width or BACKGROUND_DEFAULT.border_width

    if not draw_allowed(cfg.draw_me) then return end


    local bg = cfg.bg_color or PLASMA.bg_widget
    local bc = cfg.border_color or PLASMA.text_dim

    local w = (cfg.w == 0 or cfg.w == nil) and conky_window.width or cfg.w
    local h = (cfg.h == 0 or cfg.h == nil) and conky_window.height or cfg.h


    cairo_new_sub_path(cr)
    cairo_arc(cr, x + w - r, y + r, r, -math.pi / 2, 0)
    cairo_arc(cr, x + w - r, y + h - r, r, 0, math.pi / 2)
    cairo_arc(cr, x + r, y + h - r, r, math.pi / 2, math.pi)
    cairo_arc(cr, x + r, y + r, r, math.pi, 3 * math.pi / 2)
    cairo_close_path(cr)


    cairo_set_source_rgba(cr, bg[1], bg[2], bg[3], bg[4] or 1)
    cairo_fill_preserve(cr)


    cairo_set_line_width(cr, border_w)
    cairo_set_source_rgba(cr, bc[1], bc[2], bc[3], bc[4] or 1)
    cairo_stroke(cr)
end

-- BARS
--{{{
local BAR_DEFAULT = {
    draw_me = true,
    x = 0,
    y = 0,
    w = 200,
    h = 10,
    max = 100,


    fg_color = nil, -- ha nil → PLASMA.accent


    bg_color = nil, -- ha nil → PLASMA.bg_widget
}

function draw_flat_bar(cr, m)
    -- Merge defaults
    for k, v in pairs(BAR_DEFAULT) do
        if m[k] == nil then m[k] = v end
    end
    if not draw_allowed(m.draw_me) then return end

    local fg = m.fg_color or PLASMA.accent
    local bg = m.bg_color or PLASMA.bg_widget


    local raw
    if type(m.name) == "number" then
        raw = m.name
    else
        raw = normalize_with_suffix(conky_parse(string.format("${%s %s}", m.name, m.arg or "")))
    end

    local pct = math.max(0, math.min(1, raw / m.max))


    cairo_set_source_rgba(cr, bg[1], bg[2], bg[3], bg[4] or 1)
    cairo_rectangle(cr, m.x, m.y, m.w, m.h)
    cairo_fill(cr)

    if pct > 0 then
        cairo_set_source_rgba(cr, fg[1], fg[2], fg[3], fg[4] or 1)
        cairo_rectangle(cr, m.x, m.y, m.w * pct, m.h)
        cairo_fill(cr)
    end
end

--}}}
-- GRAPS
--{{{
local GRAPH_DEFAULT = {
    draw_me      = true,
    x            = 0,
    y            = 0,
    w            = 200,
    h            = 50,

    overlay      = false,

    background   = true,
    bg_color     = nil, -- ha nil → PLASMA.bg_widget

    border       = true,
    border_color = nil, -- ha nil → PLASMA.text_dim
    border_width = 1,

    line         = true,
    line_color   = nil, -- ha nil → PLASMA.accent

    grid         = true,
    grid_color   = nil, -- ha nil → PLASMA.text_main (0.10 alpha)

    autoscale    = true,
    max          = 100,
}

graph_history = graph_history or {}
function draw_plasma_graph(cr, g)
    -- Defaults
    for k, v in pairs(GRAPH_DEFAULT) do
        if g[k] == nil then g[k] = v end
    end
    if not draw_allowed(g.draw_me) then return end


    local bg = g.bg_color or PLASMA.bg_panel
    local bc = g.border_color or PLASMA.text_dim
    local lc = g.line_color or PLASMA.accent

    local gc = g.grid_color or { PLASMA.text_main[1], PLASMA.text_main[2], PLASMA.text_main[3], 0.08 }

    if g.overlay then
        g.background = false
        g.border     = false
        g.grid       = false 
    end
    ------------------------------------------------------------


    local key = tostring(g.name) .. (g.arg or "")
    if not graph_history[key] then
        graph_history[key] = {}
        for i = 1, g.w do graph_history[key][i] = 0 end
    end


    local raw
    if type(g.name) == "number" then
        raw = g.name
    else
        raw = normalize_with_suffix(conky_parse(string.format("${%s %s}", g.name, g.arg or "")))
    end

    table.remove(graph_history[key], 1)
    table.insert(graph_history[key], raw)

    -- Autoscale
    local max_val = g.max
    if g.autoscale then
        max_val = 0
        for _, v in ipairs(graph_history[key]) do
            if v > max_val then max_val = v end
        end
        max_val = max_val * 1.1
    end
    if max_val <= 0 then max_val = 1 end


    if g.background then
        cairo_set_source_rgba(cr, bg[1], bg[2], bg[3], bg[4] or 1)
        cairo_rectangle(cr, g.x, g.y, g.w, g.h)
        cairo_fill(cr)
    end


    if g.grid then
        cairo_set_source_rgba(cr, gc[1], gc[2], gc[3], gc[4] or 1)
        cairo_set_line_width(cr, 1)

        local steps = 4
        for i = 1, steps - 1 do
            local gy = g.y + (g.h / steps) * i
            cairo_move_to(cr, g.x, gy)
            cairo_line_to(cr, g.x + g.w, gy)
            cairo_stroke(cr)
        end
    end


    if g.border then
        cairo_set_source_rgba(cr, bc[1], bc[2], bc[3], bc[4] or 1)
        cairo_set_line_width(cr, g.border_width)
        cairo_rectangle(cr, g.x, g.y, g.w, g.h)
        cairo_stroke(cr)
    end


    if g.line then
        cairo_set_source_rgba(cr, lc[1], lc[2], lc[3], lc[4] or 1)
        cairo_set_line_width(cr, 1)

        for i = 2, g.w do
            local v1 = graph_history[key][i - 1] / max_val
            local v2 = graph_history[key][i] / max_val
            cairo_move_to(cr, g.x + i - 2, g.y + g.h - (v1 * g.h))
            cairo_line_to(cr, g.x + i - 1, g.y + g.h - (v2 * g.h))
            cairo_stroke(cr)
        end
    end
end

--}}}
-- TEXT
--{{{
TEXT_DEFAULT = {
    text    = "",
    draw_me = true,
    x       = 0,
    y       = 0,
    font    = "Ubuntu",
    size    = 11,
    slant   = "normal",
    weight  = "normal",
    align   = "left",


    color   = nil, -- ha nil → PLASMA.text_main
}

local function normalize_text(opts)
    if not opts or not opts.text then return nil end
    local txt = tostring(opts.text)
    return (opts.conky ~= false) and conky_parse(txt) or txt
end

function draw_text(cr, opts)
    if not opts or not draw_allowed(opts.draw_me) then return end

    -- defaults merge
    local cfg = {}
    for k, v in pairs(TEXT_DEFAULT) do cfg[k] = v end
    for k, v in pairs(opts) do cfg[k] = v end

    -- Plasma fallback
    local col = cfg.color or PLASMA.text_main

    local txt = normalize_text(cfg)
    if not txt or txt == "" then return end

    local slant  = (cfg.slant == "italic") and CAIRO_FONT_SLANT_ITALIC or CAIRO_FONT_SLANT_NORMAL
    local weight = (cfg.weight == "bold") and CAIRO_FONT_WEIGHT_BOLD or CAIRO_FONT_WEIGHT_NORMAL

    cairo_select_font_face(cr, cfg.font, slant, weight)
    cairo_set_font_size(cr, cfg.size)

    local ext = cairo_text_extents_t:create()
    cairo_text_extents(cr, txt, ext)

    local x, y = cfg.x, cfg.y

    -- center handling
    if x == "center" then x = conky_window.width / 2 end
    if y == "center" then y = conky_window.height / 2 end

    -- alignment
    if cfg.align == "center" then
        x = x - ext.width / 2
    elseif cfg.align == "right" then
        x = x - ext.width
    end

    -- baseline correction
    y = y - ext.y_bearing

    -- Plasma RGBA
    cairo_set_source_rgba(cr, col[1], col[2], col[3], col[4] or 1)

    cairo_move_to(cr, x, y)
    cairo_show_text(cr, txt)
    cairo_stroke(cr)
end

--}}}
-- LINES
--{{{
local LINE_DEFAULT = {
    draw_me   = true,
    x1        = 0,
    y1        = 0,
    x2        = 100,
    y2        = 0,
    style     = "solid", -- solid | dashed | dotted
    cell_size = 6,
    dash_on   = 0.6,
    thickness = 2,

    -- új: RGBA, nem hex
    color     = nil, -- ha nil → PLASMA.text_main
}

function draw_line(cr, opts)
    -- defaults merge
    local cfg = {}
    for k, v in pairs(LINE_DEFAULT) do cfg[k] = v end
    for k, v in pairs(opts) do cfg[k] = v end
    if not draw_allowed(cfg.draw_me) then return end

    -- Plasma fallback
    local col = cfg.color or PLASMA.text_main

    local x1, y1, x2, y2 = cfg.x1, cfg.y1, cfg.x2, cfg.y2
    local dx, dy = x2 - x1, y2 - y1
    local L = math.sqrt(dx * dx + dy * dy)
    if L < 1 then return end

    local cs = cfg.cell_size
    local count = math.floor(L / cs)
    if count < 1 then return end

    local gap = (L - (count * cs)) / (count - 1)
    local nx, ny = dx / L, dy / L

    cairo_set_source_rgba(cr, col[1], col[2], col[3], col[4] or 1)
    cairo_set_line_width(cr, cfg.thickness)

    for i = 0, count - 1 do
        local base_dist = i * (cs + gap)
        local cx1 = x1 + nx * base_dist
        local cy1 = y1 + ny * base_dist

        if cfg.style == "solid" then
            local cx2 = x1 + nx * (base_dist + cs)
            local cy2 = y1 + ny * (base_dist + cs)
            cairo_move_to(cr, cx1, cy1)
            cairo_line_to(cr, cx2, cy2)
            cairo_stroke(cr)
        elseif cfg.style == "dashed" then
            local on_len = cs * cfg.dash_on
            local ex = x1 + nx * (base_dist + on_len)
            local ey = y1 + ny * (base_dist + on_len)
            cairo_move_to(cr, cx1, cy1)
            cairo_line_to(cr, ex, ey)
            cairo_stroke(cr)
        elseif cfg.style == "dotted" then
            local mid = base_dist + cs / 2
            local px = x1 + nx * mid
            local py = y1 + ny * mid
            cairo_arc(cr, px, py, cfg.thickness / 2, 0, 2 * math.pi)
            cairo_fill(cr)
        end
    end
end

--}}}
-- IMAGES
--{{{
local PNG_DEFAULT = {
    draw_me = true,
    path    = nil,
    x       = 0,
    y       = 0,
    w       = nil,
    h       = nil,
}
function draw_png(cr, opts)
    -- Merge defaults
    local cfg = {}
    for k, v in pairs(PNG_DEFAULT) do cfg[k] = v end
    for k, v in pairs(opts) do cfg[k] = v end
    if not draw_allowed(cfg.draw_me) or not cfg.path then return end
    -- File existence check
    local f = io.open(cfg.path, "rb")
    if not f then return end
    f:close()
    -- Create surface from PNG
    local img = cairo_image_surface_create_from_png(cfg.path)
    if cairo_surface_status(img) ~= 0 then
        cairo_surface_destroy(img)
        return
    end
    local iw = cairo_image_surface_get_width(img)
    local ih = cairo_image_surface_get_height(img)
    -- Scaling logic
    local sx = cfg.w and (cfg.w / iw) or 1
    local sy = cfg.h and (cfg.h / ih) or 1
    -- If only one dimension is provided, preserve aspect ratio
    if cfg.w and not cfg.h then sy = sx end
    if cfg.h and not cfg.w then sx = sy end
    cairo_save(cr)
    cairo_translate(cr, cfg.x, cfg.y)
    cairo_scale(cr, sx, sy)
    cairo_set_source_surface(cr, img, 0, 0)
    cairo_paint(cr)
    cairo_restore(cr)
    cairo_surface_destroy(img)
end

--}}}
-- CLOCK
--{{{
function draw_clock(cr, cfg)
    if not draw_allowed(cfg.draw_me) then return end
    if conky_window == nil then return end

    local x          = cfg.x
    local y          = cfg.y
    local r          = cfg.radius

    -- Plasma színek
    local bg         = PLASMA.bg_widget
    local border     = PLASMA.text_dim
    local tick       = PLASMA.text_main
    local number     = PLASMA.text_main
    local hour_c     = PLASMA.text_main
    local min_c      = PLASMA.text_main
    local sec_c      = PLASMA.accent
    local center     = PLASMA.text_main


    local hours      = tonumber(os.date("%I"))
    local minutes    = tonumber(os.date("%M"))
    local seconds    = tonumber(os.date("%S"))

    local sec_angle  = (seconds / 60) * 2 * math.pi
    local min_angle  = (minutes / 60) * 2 * math.pi
    local hour_angle = ((hours % 12) / 12 + minutes / 720) * 2 * math.pi


    cairo_set_source_rgba(cr, bg[1], bg[2], bg[3], bg[4])
    cairo_arc(cr, x, y, r, 0, 2 * math.pi)
    cairo_fill(cr)


    cairo_set_source_rgba(cr, border[1], border[2], border[3], border[4])
    cairo_set_line_width(cr, 2)
    cairo_arc(cr, x, y, r, 0, 2 * math.pi)
    cairo_stroke(cr)

    --------------------------------------------------------
    -- TICKS
    --------------------------------------------------------
    if cfg.show_ticks then
        for i = 0, 59 do
            local angle    = (i / 60) * 2 * math.pi
            local sin_a    = math.sin(angle)
            local cos_a    = math.cos(angle)

            local is_hour  = (i % 5 == 0)
            local tick_len = is_hour and 10 or 5
            local tick_w   = is_hour and cfg.tick_width_hour or cfg.tick_width_minute

            cairo_set_source_rgba(cr, tick[1], tick[2], tick[3], tick[4])
            cairo_set_line_width(cr, tick_w)

            cairo_move_to(cr, x + sin_a * (r - tick_len), y - cos_a * (r - tick_len))
            cairo_line_to(cr, x + sin_a * r, y - cos_a * r)
            cairo_stroke(cr)
        end
    end


    if cfg.show_numbers then
        cairo_select_font_face(cr, "Ubuntu", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD)
        cairo_set_font_size(cr, cfg.number_size)

        for i = 1, 12 do
            local angle = (i / 12) * 2 * math.pi
            local sin_a = math.sin(angle)
            local cos_a = math.cos(angle)

            local nx = x + sin_a * (r * cfg.number_radius)
            local ny = y - cos_a * (r * cfg.number_radius)

            cairo_set_source_rgba(cr, number[1], number[2], number[3], number[4])

            local text = tostring(i)
            local ext = cairo_text_extents_t:create()
            cairo_text_extents(cr, text, ext)

            cairo_move_to(cr, nx - ext.width / 2, ny + ext.height / 2)
            cairo_show_text(cr, text)
        end
    end


    local function draw_hand(angle, length, width, col)
        cairo_set_source_rgba(cr, col[1], col[2], col[3], col[4])
        cairo_set_line_width(cr, width)

        cairo_move_to(cr, x, y)
        cairo_line_to(cr, x + math.sin(angle) * length, y - math.cos(angle) * length)
        cairo_stroke(cr)
    end

    draw_hand(hour_angle, r * 0.5, cfg.hour_hand_width, hour_c)
    draw_hand(min_angle, r * 0.75, cfg.minute_hand_width, min_c)

    if cfg.show_seconds then
        draw_hand(sec_angle, r * 0.9, cfg.second_hand_width, sec_c)
    end


    cairo_set_source_rgba(cr, center[1], center[2], center[3], center[4])
    cairo_arc(cr, x, y, cfg.center_radius, 0, 2 * math.pi)
    cairo_fill(cr)
end

--}}}
-- CALENDAR
--{{{

function get_cal_monthname()
    return pread("date +%B' '%Y")
end

function get_cal_weekdays()
    -- A cal -m (hétfővel kezdődő) kimenetének 2. sora a napok nevei
    local raw = pread("cal -m | head -n2 | tail -n1")
    local days = {}
    for day in raw:gmatch("%S+") do
        table.insert(days, day)
    end
    return days
end

function draw_calendar(cr, cfg)
    if not draw_allowed(cfg.draw_me) then return end
    if conky_window == nil then return end

    local x, y, cw, rh       = cfg.x, cfg.y, cfg.cell_w, cfg.row_h

    -- Plasma színek
    local col_month          = PLASMA.text_main
    local col_weekdays       = PLASMA.text_dim
    local col_days           = PLASMA.text_main
    local col_today          = PLASMA.accent
    local col_outside        = PLASMA.text_dim
    local col_weeknums       = PLASMA.text_dim
    local col_sep            = PLASMA.text_dim

    local now                = os.date("*t")
    local year, month, today = now.year, now.month, now.day
    local first_t            = os.time { year = year, month = month, day = 1 }
    local wday_num           = tonumber(os.date("%u", first_t))
    local start_col          = wday_num - 1

    local days_in_month      = os.date("*t", os.time { year = year, month = month + 1, day = 0 }).day
    local prev_days_max      = os.date("*t", os.time { year = year, month = month, day = 0 }).day


    local month_raw          = get_cal_monthname()
    local fmt                = cfg.month_format or "year_month"
    local month_display      = ""

    if fmt == "month_year" then
        month_display = month_raw
    else
        local ystr = tostring(year)
        local m_only = month_raw:gsub("%s*" .. ystr .. "%s*", "")
        month_display = ystr .. ". " .. m_only
    end

    draw_text(cr, {
        text   = month_display,
        x      = 160,      -- A 320 fele, így ha align="center", pont középen lesz
        y      = y + 3,
        align  = "center", -- Ez a kulcs!
        font   = cfg.font,
        size   = cfg.size + 8,
        weight = "bold",
        color  = col_month,
    })

    draw_line(cr, {
        x1        = x,
        y1        = y + rh * 1,
        x2        = x + cw * (cfg.show_weeknums and 8 or 7),
        y2        = y + rh * 1,
        style     = "solid",
        thickness = 1,
        color     = col_sep
    })

    --------------------------------------------------------
    -- WEEKDAY LABELS
    --------------------------------------------------------
    local WEEKDAYS = get_cal_weekdays()
    for i = 1, 7 do
        if WEEKDAYS[i] then
            draw_text(cr, {
                text  = WEEKDAYS[i],
                x     = x + (cfg.show_weeknums and cw or 0) + (i - 1) * cw + cw / 2,
                y     = y + rh * 2.2,
                align = "center",
                font  = cfg.font,
                size  = cfg.size ,
                color = col_weekdays,
            })
        end
    end

    --------------------------------------------------------
    -- DAYS GRID
    --------------------------------------------------------
    local row, col = 0, start_col


    for i = start_col - 1, 0, -1 do
        draw_text(cr, {
            text  = tostring(prev_days_max - i),
            x     = x + (cfg.show_weeknums and cw or 0) + (start_col - 1 - i) * cw + cw / 2,
            y     = y + rh * 3.5,
            align = "center",
            font  = cfg.font,
            size  = cfg.size,
            color = col_outside,
        })
    end

    for d = 1, days_in_month do
        if col == 7 then
            col = 0; row = row + 1
        end

        if cfg.show_weeknums and col == 0 then
            local t = os.time { year = year, month = month, day = d }
            draw_text(cr, {
                text  = os.date("%V", t),
                x     = x + cw / 2,
                y     = y + rh * (row + 3.5),
                align = "center",
                font  = cfg.font,
                size  = cfg.size - 4,
                color = col_weeknums,
            })
        end

        draw_text(cr, {
            text   = tostring(d),
            x      = x + (cfg.show_weeknums and cw or 0) + col * cw + cw / 2,
            y      = y + rh * (row + 3.5),
            align  = "center",
            font   = cfg.font,
            size   = cfg.size,
            color  = (d == today) and col_today or col_days,
            weight = (d == today) and "bold" or "normal",
        })
        col = col + 1
    end

    local next_d = 1
    while (row * 7 + col) < 42 do
        if col == 7 then
            col = 0; row = row + 1
        end
        if row > 5 then break end

        draw_text(cr, {
            text  = tostring(next_d),
            x     = x + (cfg.show_weeknums and cw or 0) + col * cw + cw / 2,
            y     = y + rh * (row + 3.5),
            align = "center",
            font  = cfg.font,
            size  = cfg.size,
            color = col_outside,
        })
        next_d = next_d + 1
        col = col + 1
    end
end

--}}}

calendar.lua

local script_path = debug.getinfo(1).source:match("@(.*/)")
package.path = package.path .. ";" .. script_path .. "../common/?.lua"

require "common"

calendar_cfg = {
    draw_me       = true,
    month_format  = "year_month",
    x             = 0,
    y             = 5,
    cell_w        = 40,
    row_h         = 32,
    font          = "Ubuntu",
    size          = 16,
    show_weeknums = true,
}


function conky_calendar_main()
    if not conky_ready() then return end
    check_theme_update()

    local cs = cairo_xlib_surface_create(
        conky_window.display,
        conky_window.drawable,
        conky_window.visual,
        conky_window.width,
        conky_window.height
    )
    local cr = cairo_create(cs)


 draw_background(cr, backgrounds.default)
    draw_calendar(cr, calendar_cfg)

    cairo_destroy(cr)
    cairo_surface_destroy(cs)
end

colck.lua

local script_path = debug.getinfo(1).source:match("@(.*/)")
package.path = package.path .. ";" .. script_path .. "../common/?.lua"
require 'common'


local my_clock_cfg = {
    draw_me = true,
    x = 150,
    y = 150,
    radius = 150,
    show_ticks = true,
    tick_width_hour = 3,
    tick_width_minute = 1,
    hour_hand_width = 4,
    minute_hand_width = 2,
    second_hand_width = 1,
    show_seconds = true,
    show_numbers = true,
    number_size = 16,
    number_radius = 0.8,
    center_radius = 5
}


function conky_main_clock()
    if not conky_ready() then return end
    check_theme_update()

    local cs = cairo_xlib_surface_create(conky_window.display, conky_window.drawable,
        conky_window.visual, conky_window.width, conky_window.height)
    local cr = cairo_create(cs)

    draw_clock(cr, my_clock_cfg)

    cairo_destroy(cr)
    cairo_surface_destroy(cs)
end

clock.conf

conky.config = {
-- -- Belső működés
    update_interval = 1.0,          
    cpu_avg_samples = 2,
    net_avg_samples = 2,
    double_buffer = true,
    out_to_console = false,
    out_to_stderr = false,
    extra_newline = false,


    own_window = true,
    own_window_class = 'Conky',
    own_window_type = 'normal',    
    own_window_transparent = true,
    own_window_argb_visual = true,
    own_window_argb_value = 255,   
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',


    alignment = 'top_right',        
    gap_x = 10,
    gap_y = 10,
    minimum_width = 300,
    minimum_height = 300,
    fixed_size = true,

    draw_shades = false,
    draw_outline = false,
    draw_borders = false,
    draw_graph_borders = false,
    use_xft = true,
    font = 'Ubuntu:size=10',
    text_buffer_size = 2048,
    border_inner_margin = 0,
    border_outer_margin = 0,
    border_width = 0,

-- -- Lua Script betöltése
    lua_load = 'clock.lua',
    lua_draw_hook_pre = 'main_clock',
};

conky.text = [[

]];

calendar.conf

conky.config = {
use_xft = true,
font = 'DejaVu Sans:size=10',
xftalpha = 1,
background = true,
double_buffer = true,
no_buffers = true,
text_buffer_size = 2048,

update_interval = 1,
cpu_avg_samples = 2,
net_avg_samples = 2,

alignment = 'top_right',
gap_x = 10,
gap_y = 320,

minimum_width = 320,
maximum_width = 320,
minimum_height = 310,

own_window = true,
own_window_type = 'normal',
own_window_title = 'Conky',
own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',

own_window_transparent = true,
own_window_argb_visual = true,

border_inner_margin = 0,
border_outer_margin = 0,
border_width = 0,

default_color = '#ededed',
pad_percents = 0,
extra_newline = false,

-- -- Lua
    lua_load = 'calendar.lua',
    lua_draw_hook_pre = 'calendar_main',
};

conky.text = [[]];

File structure

    ~/conky  pwd                                                                               ✔ 
/home/molnar/conky
    ~/conky  tree .                                                                            ✔ 
.
├── calendar
│   ├── calendar.conf
│   └── calendar.lua
├── clock
│   ├── clock.conf
│   └── clock.lua
└── common
    └── common.lua

4 directories, 5 files
    ~/conky                                                                                    ✔ 

This is just the beginning — more Plasma‑integrated widgets are on the way   — feedback and contributions are welcome.

Offline

#2 2026-03-15 19:43:27

amolnaristvan
Member
Registered: 2025-02-10
Posts: 2

Re: Modular Conky Widgets with Plasma Color Integration – Locale-Aware

Update: I've finished the modular rewrite.

Features:

    Automatic KDE Plasma color integration (via kdeglobals parsing).

    Full i18n support
   
    Clean Disk I/O graphs with autoscale.
    fVH2ANk.gif
   
    I've posted all the source files (Gists) on Reddit here:
   
    https://www.reddit.com/r/Conkyporn/comm … with_full/

Offline

Board footer

Powered by FluxBB