SkillAgentSearch skills...

DiUI

A basic UI framework for use in Oblivion Remaster modding.

Install / Use

/learn @dicene/DiUI
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

DiUI

A basic UI framework for use in Oblivion Remaster modding. I'm attempting to build things in an OOP Lua sort of way, so most things are generated with constructors and interacted with by directly calling their methods. I'm also attempting to use valid annotations for everything, which should make it easy to work with in VS Code or another IDE with Lua annotation support.

I can't make any promises about functionality or future support. I'll probably stick it on Nexus once I've gotten it a little further along. Currently, it's working for displaying information, but doesn't have a lot of input functionality. I've got a working button PoC, and I'm trying to find a easier setup for devs to be able to use it. I think I'll be able to get buttons, sliders, text input, and checkboxes working. I've also got a pretty ugly implementation of dragging a widget via a button, but it's a ways off from being ready to use. Hopefully I can find a way to implement it on non-button elements, but worst-case, I can probably just use an invisible button if I can't find a way to implement it directly on UserWidgets.

One thing to keep in mind, creating and destroying them repeatedly isn't a terribly efficient way to do things. If you're needing to use several widgets, it's much more efficient to pool them, hide them as they are no longer needed, and re-use them as new widgets are needed.

Devs are free to take whatever they want from this and implement it in their work, if folks want to contribute on here, PRs are welcome.

Examples

image

local DiUI = require("DiUI")

local printf = function(s,...) if not s:match("\n$") then s = "[DialogueWidget] " .. s .. "\n" end return print(s:format(...)) end

function FindByName(class, name)
    if name == nil then
        class, name = class:match("^(%w+) (.+)$")
    end

    if class == nil or name == nil then return CreateInvalidObject() end

    local objs = FindAllOf(class) or {}

    for i = 1, #objs, 1 do
        if objs[i]:GetFullName():match(name) then
            return objs[i]
        end
    end

    return CreateInvalidObject()
end

---@class DialogueWidget : DiWindow
---@field Name string
---
---@field NPC AVPairedPawn
---@field Pos FVector2D
---
---@field ExpandHint UTextBlock
---@field CollapsableBox UVerticalBox
---@field NameText UTextBlock
---@field ReRandomizeButton UWBP_ModernPrefab_Button_C
local DialogueWidget = {
    Name = "name123",
}
setmetatable(DialogueWidget, {__index = DiUI.DiWindow})

---@return DialogueWidget
function DialogueWidget:New(name, pos)

    local newDialogueWidget = DiUI.DiWindow:New(name, name, pos) ---@cast newDialogueWidget HealthWidget
    setmetatable(newDialogueWidget, {__index = DialogueWidget})

    local vBox = newDialogueWidget.InternalVBox

    local header = DiUI.CreateHorizontalBox(newDialogueWidget)
    vBox:AddChildToVerticalBox(header)

    newDialogueWidget.InternalBorder:SetBrushColor({A=1,R=0,G=0,B=0})

    -- local arrowTexture = FindByName("Texture2D /Game/Art/UI/Common/Buttons/T_CharGen_Arrow.T_CharGen_Arrow") or CreateInvalidObject() ---@cast arrowTexture UTexture2D
    -- local arrowTexture = FindByName("Texture2D /Game/Art/UI/Common/Buttons/T_CharGen_Arrow_Hovered.T_CharGen_Arrow_Hovered") or CreateInvalidObject() ---@cast arrowTexture UTexture2D
    -- local arrowTexture = FindByName("Texture2D /Game/Art/UI/Common/Buttons/T_Arrow_Hovered.T_Arrow_Hovered") or CreateInvalidObject() ---@cast arrowTexture UTexture2D
    -- local arrowTexture = FindByName("Texture2D /Game/Art/UI/Common/Buttons/T_ArrowHoverEffect.T_ArrowHoverEffect") or CreateInvalidObject() ---@cast arrowTexture UTexture2D
    local nameDecorationTexture = FindByName("Texture2D /Game/Art/UI/Common/Ornaments/T_UI_HelpPattern1.T_UI_HelpPattern1") or CreateInvalidObject() ---@cast arrowTexture UTexture2D

    if nameDecorationTexture:IsValid() then
        local image = DiUI.CreateImage(newDialogueWidget, nameDecorationTexture)
        image.Brush.ImageSize = {X=64,Y=32}
        header:AddChildToHorizontalBox(image)
    end

    newDialogueWidget.NameText = DiUI.CreateTextBlock(newDialogueWidget, "NameText", "Text")
    newDialogueWidget.NameText.Font.Size = 20
    newDialogueWidget.NameText:SetJustification(1)
    newDialogueWidget.NameText.ColorAndOpacity = {SpecifiedColor={A=1, R=1, G=1, B=1}, ColorUseRule=0}
    newDialogueWidget.NameText.ShadowColorAndOpacity = {A=1, R=0, G=0, B=0}
    header:AddChildToHorizontalBox(newDialogueWidget.NameText)

    if nameDecorationTexture:IsValid() then
        local image = DiUI.CreateImage(newDialogueWidget, nameDecorationTexture, {X=32, Y=32})
        image.Brush.ImageSize = {X=64,Y=32}
        image.RenderTransform.Angle = 180
        header:AddChildToHorizontalBox(image)
    end

    ReRandomizeButton = DiUI.CreateButton(newDialogueWidget, "Re-randomize")
    -- ReRandomizeButton:SetDesiredSizeInViewport({X=50,Y=50})
    newDialogueWidget.InternalVBox:AddChildToVerticalBox(ReRandomizeButton)

    return newDialogueWidget
end

DiUI.DestroyWidgetsByName("DialogueWidget")
DialogueMenu = DialogueWidget:New("DialogueWidget", {X = -1000, Y = -1000})
ReRandomizeButton = DialogueMenu.ReRandomizeButton
DialogueMenu:Show()

image

local DiUI = require("DiUI")

local printf = function(s,...) if not s:match("\n$") then s = "[HealthWidget] " .. s .. "\n" end return print(s:format(...)) end

function FindByName(class, name)
    if name == nil then
        class, name = class:match("^(%w+) (.+)$")
    end

    if class == nil or name == nil then return CreateInvalidObject() end

    local objs = FindAllOf(class) or {}

    for i = 1, #objs, 1 do
        if objs[i]:GetFullName():match(name) then
            return objs[i]
        end
    end

    return CreateInvalidObject()
end

---@class HealthWidget : DiWindow
---@field Name string
---@field CurrentHealth float
---@field MaxHealth float
---@field CurrentMagicka float
---@field MaxMagicka float
---@field CurrentFatigue float
---@field MaxFatigue float
---@field Aggression float
---
---@field NPC AVPairedPawn
---@field Pos FVector2D
---
---@field Header UHorizontalBox
---@field NameText UTextBlock
---@field HostileIcon UImage
---@field HealthBar UProgressBar
---@field MagickaBar UProgressBar
---@field FatigueBar UProgressBar
---
local HealthWidget = {
    Name = "name123",
    CurrentHealth = 100,
    MaxHealth = 100,
    CurrentFatigue = 100,
    MaxFatigue = 100,
    CurrentMagicka = 100,
    MaxMagicka = 100,
    Aggression = 0,
}
setmetatable(HealthWidget, {__index = DiUI.DiWindow})

---@return HealthWidget
function HealthWidget:New(name, pos)
    printf("Creating new HealthWidget: %s, (X=%0.0f,Y=%0.0f)\n", name, pos.X, pos.Y)
    ---@type HealthWidget
    local newHealthWidget = DiUI.DiWindow:New(name, name, pos) ---@cast newHealthWidget HealthWidget
    setmetatable(newHealthWidget, {__index = HealthWidget})

    local vBox = newHealthWidget.InternalVBox

    local header = DiUI.CreateHorizontalBox(newHealthWidget)
    vBox:AddChildToVerticalBox(header)

    newHealthWidget.NameText = DiUI.CreateTextBlock(newHealthWidget, "NameText", "EnemyHealthWidget")
    newHealthWidget.NameText.Font.Size = 20
    newHealthWidget.NameText:SetJustification(1)
    newHealthWidget.NameText.ColorAndOpacity = {SpecifiedColor={A=1, R=1, G=1, B=1}, ColorUseRule=0}
    newHealthWidget.NameText.ShadowColorAndOpacity = {A=1, R=0, G=0, B=0}
    header:AddChildToHorizontalBox(newHealthWidget.NameText)

    newHealthWidget.HealthBar = DiUI.CreateProgressBar(newHealthWidget)
    newHealthWidget.HealthBar:SetFillColorAndOpacity({A=1,R=1,G=0.3,B=0.3})
    vBox:AddChildToVerticalBox(newHealthWidget.HealthBar)

    newHealthWidget.MagickaBar = DiUI.CreateProgressBar(newHealthWidget)
    newHealthWidget.MagickaBar:SetFillColorAndOpacity({A=1,R=0.3,G=0.3,B=1})
    vBox:AddChildToVerticalBox(newHealthWidget.MagickaBar)

    newHealthWidget.FatigueBar = DiUI.CreateProgressBar(newHealthWidget)
    newHealthWidget.FatigueBar:SetFillColorAndOpacity({A=1,R=0.3,G=1,B=0.3})
    vBox:AddChildToVerticalBox(newHealthWidget.FatigueBar)

    local gameplaySettingsTexture = FindByName("Texture2D", "/Game/Art/UI/Modern/MenuLayer/Settings/T_Settings_Gameplay.T_Settings_Gameplay") or CreateInvalidObject() ---@cast gameplaySettingsTexture UTexture2D
    if gameplaySettingsTexture:IsValid() then
        printf("GameplaySettingsTexture: %s", gameplaySettingsTexture:GetFullName())
        newHealthWidget.HostileIcon = DiUI.CreateImage(newHealthWidget, gameplaySettingsTexture, {X=32, Y=32})
        header:AddChildToHorizontalBox(newHealthWidget.HostileIcon)
    else
        printf("Couldn't find GameplaySettingsTexture")
    end
    return newHealthWidget
end

local newHealthWidget = HealthWidget:New("NPCHealthWidget", {X=100,Y=100})
newHealthWidget.NPC = pawn
newHealthWidget:UpdateWidget()
newHealthWidget:Show()
View on GitHub
GitHub Stars6
CategoryDevelopment
Updated1mo ago
Forks2

Languages

Lua

Security Score

85/100

Audited on Feb 28, 2026

No findings