It is currently September 10th, 2024, 11:47 pm

Unfocusing skin when entering InputText field

Report bugs with the Rainmeter application and suggest features.
User avatar
Yincognito
Rainmeter Sage
Posts: 8071
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Unfocusing skin when entering InputText field

Post by Yincognito »

balala wrote: August 29th, 2024, 7:23 pm Done, I hope. Still testing, but it seems to be working. So, thanks for the suggestion (once again).
No problem, you gave me some excellent suggestions too on other occasions! :great:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Cariboudjan
Posts: 278
Joined: May 12th, 2019, 8:55 am

Re: Unfocusing skin when entering InputText field

Post by Cariboudjan »

HotInput works great - Maybe it should be the default way Rainmeter handles text input
Last edited by eclectic-tech on September 2nd, 2024, 1:11 pm, edited 1 time in total.
Reason: Added link to HotInput forum post.
User avatar
balala
Rainmeter Sage
Posts: 16517
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Unfocusing skin when entering InputText field

Post by balala »

Cariboudjan wrote: September 1st, 2024, 9:36 pm HotInput works great - Maybe it should be the default way Rainmeter handles text input
The plugin even if seems useful, has a great problem: it seems not too handle the not-english characters (for instance á, é, ö - this are Hungarian characters, or ă, ș, î - Romanian characters, but I assume the same applies for any character with diacritics, by any not-English language). For many this might seem a not too huge problem, but for one writing not-always in English, this practically makes the plugin not-usable.
Sorry, but this is a fatal problem for a this kind of plugin, in my opinion. InputText deals extremely well with these characters and I assume with any character which can be entered by a keyboard.
So NO, I HOPE THIS PLUGIN, IN THIS VERSION AT LEAST, never will be the default way Rainmeter handles text inputs.
User avatar
Yincognito
Rainmeter Sage
Posts: 8071
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Unfocusing skin when entering InputText field

Post by Yincognito »

balala wrote: September 4th, 2024, 6:25 pm The plugin even if seems useful, has a great problem: it seems not too handle the not-english characters (for instance á, é, ö - this are Hungarian characters, or ă, ș, î - Romanian characters, but I assume the same applies for any character with diacritics, by any not-English language).
Didn't test the plugin (though I should have had, given death.crafter's dedication of his Lua script to me, lol), but the plugin's GitHub page seems to indicate that such characters can be used literally:
https://github.com/deathcrafter/PluginHotInput#input-strings

Generally speaking, if Brian's HotKey had a multiple key whitelist / blacklist system similar to the UsageMonitor one, to avoid making thousands of measures for every little character, replicating natively in Rainmeter most of what InputText does would be easier. Another way of doing it, but again non natively, would be through the WebView plugin and passing the result to Rainmeter via RainmeterAPI. So far, InputText seems to check most boxes from all the possibilities, at least when it comes to making this as simple as possible for the regular user. Focus and transparency are about the only drawbacks of InputText.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
balala
Rainmeter Sage
Posts: 16517
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Unfocusing skin when entering InputText field

Post by balala »

Yincognito wrote: September 5th, 2024, 7:53 am Didn't test the plugin (though I should have had, given death.crafter's dedication of his Lua script to me, lol), but the plugin's GitHub page seems to indicate that such characters can be used literally:
https://github.com/deathcrafter/PluginHotInput#input-strings
No, unfortunately I still sustain they can't be. Just retried, doesn't work, no matter what the GitHub page indicates.
Yincognito wrote: September 5th, 2024, 7:53 am Generally speaking, if Brian's HotKey had a multiple key whitelist / blacklist system similar to the UsageMonitor one, to avoid making thousands of measures for every little character, replicating natively in Rainmeter most of what InputText does would be easier. Another way of doing it, but again non natively, would be through the WebView plugin and passing the result to Rainmeter via RainmeterAPI.
Doesn't worth to complicate things, at least not from my point of view. InputText is perfect for me, I refuse to uselessly complicate the code.
Yincognito wrote: September 5th, 2024, 7:53 am Focus and transparency are about the only drawbacks of InputText.
Since the issue is fixed (as previously said), this is not anymore a drawback. I'm happy with the solution proposed by you. Thanks for it.
User avatar
Yincognito
Rainmeter Sage
Posts: 8071
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Unfocusing skin when entering InputText field

Post by Yincognito »

balala wrote: September 5th, 2024, 5:15 pm No, unfortunately I still sustain they can't be. Just retried, doesn't work, no matter what the GitHub page indicates.
Doesn't worth to complicate things, at least not from my point of view. InputText is perfect for me, I refuse to uselessly complicate the code.
Since the issue is fixed (as previously said), this is not anymore a drawback. I'm happy with the solution proposed by you. Thanks for it.
:thumbup:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Cariboudjan
Posts: 278
Joined: May 12th, 2019, 8:55 am

Re: Unfocusing skin when entering InputText field

Post by Cariboudjan »

balala wrote: September 4th, 2024, 6:25 pm The plugin even if seems useful, has a great problem: it seems not too handle the not-english characters (for instance á, é, ö - this are Hungarian characters, or ă, ș, î - Romanian characters, but I assume the same applies for any character with diacritics, by any not-English language). For many this might seem a not too huge problem, but for one writing not-always in English, this practically makes the plugin not-usable.
Sorry, but this is a fatal problem for a this kind of plugin, in my opinion. InputText deals extremely well with these characters and I assume with any character which can be entered by a keyboard.
So NO, I HOPE THIS PLUGIN, IN THIS VERSION AT LEAST, never will be the default way Rainmeter handles text inputs.
This was actually a bug in an older version of HotInput and was fixed last month. All non-English characters now work fine, as well as copy paste.
User avatar
balala
Rainmeter Sage
Posts: 16517
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Unfocusing skin when entering InputText field

Post by balala »

Cariboudjan wrote: September 6th, 2024, 12:58 am This was actually a bug in an older version of HotInput and was fixed last month. All non-English characters now work fine, as well as copy paste.
And where is available the new version? Because the plugin downloadable from the posted link is the one having the issue. Thanks.
User avatar
Cariboudjan
Posts: 278
Joined: May 12th, 2019, 8:55 am

Re: Unfocusing skin when entering InputText field

Post by Cariboudjan »

Code: Select all

Running = false

function Initiate()
    -- if Running then Terminate() end
    Running = true
    Caret = ''

    local valid = false

    MainMeter = SELF:GetOption('MainMeterName')
    TargetVariable = SELF:GetOption('TargetVariableName')
    CaretMeter = SELF:GetOption('CaretMeterName')
    UpdateGroup = SELF:GetOption('UpdateGroup', '*')
    ContainingFile = SELF:GetOption('ContainingFilePath')
    Multiline = SELF:GetOption('Multiline') == '1'

    if SKIN:GetMeter(MainMeter) and SKIN:GetMeter(CaretMeter) then valid = true end

    if not valid then
        error("Measure options invalid!")
    end

    if ContainingFile then 
        local file = assert(io.open(ContainingFile), 'File not valid: ' .. ContainingFile)
        file:close()
    end

    local tabWidth = tonumber(SELF:GetOption('TabWidth', 4))
    Tab = (function() local x = ''; for i = 0, tabWidth do x = x .. ' ' end ; return x; end)()
    CaretChar = SELF:GetOption('CaretCharacter')

    UserInputList = {}
    for _, v in ipairs(SplitString(SKIN:GetMeter(MainMeter):GetOption('Text', ''), '\n')) do
        table.insert(UserInputList, v)
    end

    if not UserInputList[1] then UserInputList[1] = '' end

    CurrentLine = #UserInputList or 1
    CurrentColumn = UserInputList[CurrentLine] and #UserInputList[CurrentLine] or 0

    Caret = UserInput() .. CaretChar
	InputTest = UserInput()

    SKIN:Bang('!ShowMeter', CaretMeter)
    UnD()
    
    SKIN:Bang('!Log "Starting input..." Debug')
    SKIN:Bang('!CommandMeasure', 'HotInput', 'Start')
    SKIN:Bang(SELF:GetOption('OnStartAction'))
end

function Terminate()
    SKIN:Bang('!SetVariableGroup', TargetVariable, UserInput():gsub('\n', '#*CRLF*#'), 'DroptopSuite')
    SKIN:Bang('!UpdateGroup', 'DroptopSuite')
    SKIN:Bang('!WriteKeyValue', 'Variables', TargetVariable, UserInput():gsub('\n', '#*CRLF*#'), ContainingFile)
    local finalValue = UserInput()
    local finishAction = SELF:GetOption('OnFinishAction'):gsub(
        '%$[Uu][Ss][Ee][Rr][Ii][Nn][Pp][Uu][Tt]%$',
        finalValue
    ):gsub(
        '%$[Uu][Ss][Ee][Rr][Ii][Nn][Pp][Uu][Tt]%:[Cc][Rr][Ll][Ff]%$',
        finalValue:gsub('\n', '#*CRLF*#')
    )
    print(finishAction)
    SKIN:Bang(finishAction)
    SKIN:Bang('!HideMeter', CaretMeter)
    SKIN:Bang('!CommandMeasure', 'HotInput', 'Stop')
    UnD()
    SKIN:Bang('!Log "Stopped input!" Debug')
end

function HandleInput(key)
    --#region TERMINATE
    if (key == 'RETURN') then
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
    elseif (key == 'ESC') then
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
    --#endregion

    --#region WHITESPACE
    elseif (key == 'LINEFEED') then
        if (not Multiline) then
            return
        end
        AddToInput('\n')
    elseif (key == 'TAB') then
        -- AddToInput(Tab)
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
	elseif (key == 'PASTE') then
		SKIN:Bang('!EnableMeasure', 'SendClipboard')
		SKIN:Bang('!UpdateMeasure', 'SendClipboard')
    elseif (key == 'SPACE') then
        AddToInput(' ')
    --#endregion

    --#region REMOVAL
    elseif (key == 'BACKSPACE') then
        SetCaret()
        local curr = UserInputList[CurrentLine]
        if CurrentColumn <= 0 then
            if CurrentLine <= 1 then return end
            CurrentLine = CurrentLine - 1
            CurrentColumn = #UserInputList[CurrentLine]
            UserInputList[CurrentLine] = UserInputList[CurrentLine] .. curr
            table.remove(UserInputList, CurrentLine + 1)
        else
            UserInputList[CurrentLine] = curr:sub(1, CurrentColumn - 1) .. curr:sub(CurrentColumn + 1, #curr)
            CurrentColumn = CurrentColumn - 1
			if (CurrentColumn <= 0) then
				SKIN:Bang('!SetOption', MainMeter, 'FontColor', '0,0,0,0')
				-- SKIN:Bang('!UpdateMeter', MainMeter)
				-- SKIN:Bang('!Redraw')
			end
        end
        SetCaret()
    elseif (key == 'DELETE') then
        SetCaret()
        local curr = UserInputList[CurrentLine]
        if CurrentColumn >= #curr then
            if CurrentLine >= #UserInputList then return end
            UserInputList[CurrentLine] = UserInputList[CurrentLine] .. UserInputList[CurrentLine + 1]
            table.remove(UserInputList, CurrentLine + 1)
        else
            UserInputList[CurrentLine] = curr:sub(1, CurrentColumn) .. curr:sub(CurrentColumn + 2, #curr)
        end
        SetCaret()
    --#endregion

    --#region CHARACTER
    elseif (key) then
        AddToInput(key)
		SKIN:Bang('!SetOption', MainMeter, 'FontColor', '0,0,0,255')
		SKIN:Bang('!UpdateMeter', MainMeter)
		SKIN:Bang('!Redraw')
    end
    --#endregion
end

function HandleNavigation(key)
    if key == 'LEFT' then
        if CurrentColumn > 0 then
            CurrentColumn = CurrentColumn - 1
            SetCaret()
        elseif CurrentColumn <= 0 then
            if CurrentLine <= 1 then return end
            CurrentLine = CurrentLine - 1
            CurrentColumn = #UserInputList[CurrentLine]
            SetCaret()
        end
    elseif key == 'RIGHT' then
        if CurrentColumn < #UserInputList[CurrentLine] then
            CurrentColumn = CurrentColumn + 1
            SetCaret()
        elseif CurrentColumn >= #UserInputList[CurrentLine] then
            if CurrentLine >= #UserInputList then return end
            CurrentLine = CurrentLine + 1
            CurrentColumn = 0
            SetCaret()
        end
    elseif key == 'UP' then
        if CurrentLine > 1 then
            CurrentLine = CurrentLine - 1
            ShowCaret()
        end
    elseif key == 'DOWN' then
        if CurrentLine < #UserInputList then
            CurrentLine = CurrentLine + 1
            ShowCaret()
        end
    end
end

function AddToInput(char)
    SetCaret()
    -- if #char > 1 then return end
    local curr = UserInputList[CurrentLine]

    if char == '\n' then
        local next = curr:sub(CurrentColumn + 1, #curr)
        UserInputList[CurrentLine] = curr:sub(1, CurrentColumn)
        CurrentLine = CurrentLine + 1
        table.insert(UserInputList, CurrentLine, next)
        CurrentColumn = 0
    else
        UserInputList[CurrentLine] = curr:sub(1, CurrentColumn) .. char .. curr:sub(CurrentColumn + 1, #curr)
        CurrentColumn = CurrentColumn + #char
    end

    CaretOffset = #UserInputList[CurrentLine] - CurrentColumn
    SetCaret()
end

function UserInput()
    return table.concat(UserInputList, '\n')
end

function ShowCaret() -- Shows the caret, but doesn't change the position permamnently
    local curr = UserInputList[CurrentLine]
    local currentColumn = CurrentColumn

    if CurrentColumn < 0 then currentColumn = 0 end
    if CurrentColumn > #curr then currentColumn = #curr end

    Caret = (function()
        local n = ''
        for i = 1, CurrentLine - 1 do
            n = n .. '\n'
        end
        return n
    end)() .. curr:sub(1, currentColumn) .. CaretChar

    UnD()
end

function SetCaret(update) -- Changes the caret position permamnently
    local curr = UserInputList[CurrentLine] or ''

    if CurrentColumn < 0 then CurrentColumn = 0 end
    if CurrentColumn > #curr then CurrentColumn = #curr end

    Caret = (function()
        local n = ''
        for i = 1, CurrentLine - 1 do
            n = n .. '\n'
        end
        return n
    end)() .. curr:sub(1, CurrentColumn) .. CaretChar

    if not update then UnD() end
end

function UnD()
    SKIN:Bang('!SetOption', MainMeter, 'Text', UserInput())
    SKIN:Bang('!SetOption', CaretMeter, 'Text', Caret)
    if (UpdateGroup ~= '*') then SKIN:Bang('!UpdateMeterGroup', UpdateGroup)
    else SKIN:Bang('!UpdateMeter', '*') end
    SKIN:Bang('!Redraw')
end

-- ==========================================================

function SplitString(inString, inDelimiter)
	assert(inDelimiter:len() == 1, 'SplitString: Delimiter may only be a single character')

	local outTable = {}

	for matchedString in inString:gmatch('[^%' .. inDelimiter .. ']+') do
		table.insert(outTable, matchedString)
	end

	return outTable

end
Untitle123d.png
You do not have the required permissions to view the files attached to this post.
User avatar
balala
Rainmeter Sage
Posts: 16517
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Unfocusing skin when entering InputText field

Post by balala »

Cariboudjan wrote: September 6th, 2024, 6:35 pm

Code: Select all

Running = false

function Initiate()
    -- if Running then Terminate() end
    Running = true
    Caret = ''

    local valid = false

    MainMeter = SELF:GetOption('MainMeterName')
    TargetVariable = SELF:GetOption('TargetVariableName')
    CaretMeter = SELF:GetOption('CaretMeterName')
    UpdateGroup = SELF:GetOption('UpdateGroup', '*')
    ContainingFile = SELF:GetOption('ContainingFilePath')
    Multiline = SELF:GetOption('Multiline') == '1'

    if SKIN:GetMeter(MainMeter) and SKIN:GetMeter(CaretMeter) then valid = true end

    if not valid then
        error("Measure options invalid!")
    end

    if ContainingFile then 
        local file = assert(io.open(ContainingFile), 'File not valid: ' .. ContainingFile)
        file:close()
    end

    local tabWidth = tonumber(SELF:GetOption('TabWidth', 4))
    Tab = (function() local x = ''; for i = 0, tabWidth do x = x .. ' ' end ; return x; end)()
    CaretChar = SELF:GetOption('CaretCharacter')

    UserInputList = {}
    for _, v in ipairs(SplitString(SKIN:GetMeter(MainMeter):GetOption('Text', ''), '\n')) do
        table.insert(UserInputList, v)
    end

    if not UserInputList[1] then UserInputList[1] = '' end

    CurrentLine = #UserInputList or 1
    CurrentColumn = UserInputList[CurrentLine] and #UserInputList[CurrentLine] or 0

    Caret = UserInput() .. CaretChar
	InputTest = UserInput()

    SKIN:Bang('!ShowMeter', CaretMeter)
    UnD()
    
    SKIN:Bang('!Log "Starting input..." Debug')
    SKIN:Bang('!CommandMeasure', 'HotInput', 'Start')
    SKIN:Bang(SELF:GetOption('OnStartAction'))
end

function Terminate()
    SKIN:Bang('!SetVariableGroup', TargetVariable, UserInput():gsub('\n', '#*CRLF*#'), 'DroptopSuite')
    SKIN:Bang('!UpdateGroup', 'DroptopSuite')
    SKIN:Bang('!WriteKeyValue', 'Variables', TargetVariable, UserInput():gsub('\n', '#*CRLF*#'), ContainingFile)
    local finalValue = UserInput()
    local finishAction = SELF:GetOption('OnFinishAction'):gsub(
        '%$[Uu][Ss][Ee][Rr][Ii][Nn][Pp][Uu][Tt]%$',
        finalValue
    ):gsub(
        '%$[Uu][Ss][Ee][Rr][Ii][Nn][Pp][Uu][Tt]%:[Cc][Rr][Ll][Ff]%$',
        finalValue:gsub('\n', '#*CRLF*#')
    )
    print(finishAction)
    SKIN:Bang(finishAction)
    SKIN:Bang('!HideMeter', CaretMeter)
    SKIN:Bang('!CommandMeasure', 'HotInput', 'Stop')
    UnD()
    SKIN:Bang('!Log "Stopped input!" Debug')
end

function HandleInput(key)
    --#region TERMINATE
    if (key == 'RETURN') then
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
    elseif (key == 'ESC') then
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
    --#endregion

    --#region WHITESPACE
    elseif (key == 'LINEFEED') then
        if (not Multiline) then
            return
        end
        AddToInput('\n')
    elseif (key == 'TAB') then
        -- AddToInput(Tab)
		SKIN:Bang('!SetOptionGroup', 'InputBox', 'FillColor', 'Fill Color 255,255,255,205 | Stroke Color 0,0,0,55')
		SKIN:Bang('!UpdateMeterGroup', 'InputBox')
		SKIN:Bang('!Redraw')
		SKIN:Bang('!EnableMouseAction', '*', '*')
        Terminate()
	elseif (key == 'PASTE') then
		SKIN:Bang('!EnableMeasure', 'SendClipboard')
		SKIN:Bang('!UpdateMeasure', 'SendClipboard')
    elseif (key == 'SPACE') then
        AddToInput(' ')
    --#endregion

    --#region REMOVAL
    elseif (key == 'BACKSPACE') then
        SetCaret()
        local curr = UserInputList[CurrentLine]
        if CurrentColumn <= 0 then
            if CurrentLine <= 1 then return end
            CurrentLine = CurrentLine - 1
            CurrentColumn = #UserInputList[CurrentLine]
            UserInputList[CurrentLine] = UserInputList[CurrentLine] .. curr
            table.remove(UserInputList, CurrentLine + 1)
        else
            UserInputList[CurrentLine] = curr:sub(1, CurrentColumn - 1) .. curr:sub(CurrentColumn + 1, #curr)
            CurrentColumn = CurrentColumn - 1
			if (CurrentColumn <= 0) then
				SKIN:Bang('!SetOption', MainMeter, 'FontColor', '0,0,0,0')
				-- SKIN:Bang('!UpdateMeter', MainMeter)
				-- SKIN:Bang('!Redraw')
			end
        end
        SetCaret()
    elseif (key == 'DELETE') then
        SetCaret()
        local curr = UserInputList[CurrentLine]
        if CurrentColumn >= #curr then
            if CurrentLine >= #UserInputList then return end
            UserInputList[CurrentLine] = UserInputList[CurrentLine] .. UserInputList[CurrentLine + 1]
            table.remove(UserInputList, CurrentLine + 1)
        else
            UserInputList[CurrentLine] = curr:sub(1, CurrentColumn) .. curr:sub(CurrentColumn + 2, #curr)
        end
        SetCaret()
    --#endregion

    --#region CHARACTER
    elseif (key) then
        AddToInput(key)
		SKIN:Bang('!SetOption', MainMeter, 'FontColor', '0,0,0,255')
		SKIN:Bang('!UpdateMeter', MainMeter)
		SKIN:Bang('!Redraw')
    end
    --#endregion
end

function HandleNavigation(key)
    if key == 'LEFT' then
        if CurrentColumn > 0 then
            CurrentColumn = CurrentColumn - 1
            SetCaret()
        elseif CurrentColumn <= 0 then
            if CurrentLine <= 1 then return end
            CurrentLine = CurrentLine - 1
            CurrentColumn = #UserInputList[CurrentLine]
            SetCaret()
        end
    elseif key == 'RIGHT' then
        if CurrentColumn < #UserInputList[CurrentLine] then
            CurrentColumn = CurrentColumn + 1
            SetCaret()
        elseif CurrentColumn >= #UserInputList[CurrentLine] then
            if CurrentLine >= #UserInputList then return end
            CurrentLine = CurrentLine + 1
            CurrentColumn = 0
            SetCaret()
        end
    elseif key == 'UP' then
        if CurrentLine > 1 then
            CurrentLine = CurrentLine - 1
            ShowCaret()
        end
    elseif key == 'DOWN' then
        if CurrentLine < #UserInputList then
            CurrentLine = CurrentLine + 1
            ShowCaret()
        end
    end
end

function AddToInput(char)
    SetCaret()
    -- if #char > 1 then return end
    local curr = UserInputList[CurrentLine]

    if char == '\n' then
        local next = curr:sub(CurrentColumn + 1, #curr)
        UserInputList[CurrentLine] = curr:sub(1, CurrentColumn)
        CurrentLine = CurrentLine + 1
        table.insert(UserInputList, CurrentLine, next)
        CurrentColumn = 0
    else
        UserInputList[CurrentLine] = curr:sub(1, CurrentColumn) .. char .. curr:sub(CurrentColumn + 1, #curr)
        CurrentColumn = CurrentColumn + #char
    end

    CaretOffset = #UserInputList[CurrentLine] - CurrentColumn
    SetCaret()
end

function UserInput()
    return table.concat(UserInputList, '\n')
end

function ShowCaret() -- Shows the caret, but doesn't change the position permamnently
    local curr = UserInputList[CurrentLine]
    local currentColumn = CurrentColumn

    if CurrentColumn < 0 then currentColumn = 0 end
    if CurrentColumn > #curr then currentColumn = #curr end

    Caret = (function()
        local n = ''
        for i = 1, CurrentLine - 1 do
            n = n .. '\n'
        end
        return n
    end)() .. curr:sub(1, currentColumn) .. CaretChar

    UnD()
end

function SetCaret(update) -- Changes the caret position permamnently
    local curr = UserInputList[CurrentLine] or ''

    if CurrentColumn < 0 then CurrentColumn = 0 end
    if CurrentColumn > #curr then CurrentColumn = #curr end

    Caret = (function()
        local n = ''
        for i = 1, CurrentLine - 1 do
            n = n .. '\n'
        end
        return n
    end)() .. curr:sub(1, CurrentColumn) .. CaretChar

    if not update then UnD() end
end

function UnD()
    SKIN:Bang('!SetOption', MainMeter, 'Text', UserInput())
    SKIN:Bang('!SetOption', CaretMeter, 'Text', Caret)
    if (UpdateGroup ~= '*') then SKIN:Bang('!UpdateMeterGroup', UpdateGroup)
    else SKIN:Bang('!UpdateMeter', '*') end
    SKIN:Bang('!Redraw')
end

-- ==========================================================

function SplitString(inString, inDelimiter)
	assert(inDelimiter:len() == 1, 'SplitString: Delimiter may only be a single character')

	local outTable = {}

	for matchedString in inString:gmatch('[^%' .. inDelimiter .. ']+') do
		table.insert(outTable, matchedString)
	end

	return outTable

end
Is there a compiled .dll file? A code is not too useful for me. Thanks.