Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Realtime Fullscreen GLSL Editor

edited October 10 in Code Sharing Posts: 22

UPDATE: Please go to LiveCodeEditor

(this post is too messy)

Hi everyone!

I just wanted to share my LIVE code editor with all of you.

Features:
- Custom cursor shape and color
- Cursor navigation using hardware keyboard
- Cursor navigation using finger
- Multiple lines

To do:
- Fix misaligned cursor / cursor spacing in certain font sizes
- Text scrolling action
- Syntax highlighting
- Custom text wrapper
- Save document changes
- Load document
- Create new document
- Multiple document in tabs

I hope this is useful!

(edit: screenshot added)

Comments

  • edited October 7 Posts: 22
    -- RealtimeEditor by @marcelliino @invinixity
    
    function setup()
        displayMode(STANDARD)
        editor = TextEditor()
        editor:input(fragment())
        editor.monkey = false
        editor.fontSize = 16
        editor.l_m = 8
        editor.t_m = 8
        parameter.text("OUTPUT")
        parameter.watch("MONITOR")
        parameter.watch("CURSOR_NAVIGATION")
        CURSOR_NAVIGATION =
        "STEP"..
        "\n(option + Y)    \u{2191}    UP"..
        "\n(option + H)    \u{2193}  DOWN"..
        "\n(option + G)   \u{2190}  LEFT"..
        "\n(option + J)   \u{2192} RIGHT"..
        "\n\nTELEPORT"..
        "\n(shift + option + Y)   \u{2912}  START"..
        "\n(shift + option + H)   \u{2913}    END"..
        "\n(shift + option + G)   \u{21e4}   HEAD"..
        "\n(shift + option + J)   \u{21e5}   TAIL"
    end
    
    function draw()
        background(255)
    
        D = vec2(WIDTH, HEIGHT)
        T = CurrentTouch.pos
    
        myMesh = mesh()
        myShader = shader()
    
        myShader.vertexProgram = vertex()
        myShader.fragmentProgram = TextEditor:output()
        myShader.resolution = D
        myShader.touch = T
        myShader.time = ElapsedTime
    
        myMesh.shader = myShader
    
        myMesh.vertices = {
            vec2(0*D.x, 0*D.y),
            vec2(0*D.x, 1*D.y),
            vec2(1*D.x, 1*D.y),
            vec2(1*D.x, 1*D.y),
            vec2(1*D.x, 0*D.y),
            vec2(0*D.x, 0*D.y)
        }
    
        myMesh.texCoords = {
            vec2(0, 0),
            vec2(0, 1),
            vec2(1, 1),
            vec2(1, 1),
            vec2(1, 0),
            vec2(0, 0)
        }
    
        myMesh:setColors(255,255,255,255)
        myMesh:draw()
    
        editor:draw()
        OUTPUT = editor:output()
        MONITOR = editor.details
    end
    
    function touched(touch)
        editor:touched(touch)
    end
    
    function keyboard(key)
        editor:keyboard(key)
    end
    
  • ---------- SHADER CODES ----------
    
    function vertex()
        return
    [[
    uniform mat4 modelViewProjection;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    
    void main(){
        vColor = color;
        vTexCoord = texCoord;
    
        gl_Position = modelViewProjection * position;
    }
    ]]
    end
    
    function fragment()
        return
    [[
    precision highp float;
    
    uniform vec2 resolution;
    uniform vec2 touch;
    uniform float time;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    
    #define nsin(n) sin(n)*0.5+0.5
    #define ncos(n) cos(n)*0.5+0.5
    #define ntan(n) tan(n)*0.5+0.5
    
    vec3 liquid(vec2 position, float mult, vec2 time){
        float len;
        for(float i = 0.0; i < mult; i++){
            len = length(position);
            position.x = position.x + sin(position.y + cos(len-pow(ncos(len), mult*0.5))) + cos(time.x);
            position.y = position.y - cos(position.x + sin(len-pow(nsin(len), mult*0.5))) + sin(time.y);
        }
        return vec3(position, len);
    }
    
    void main(){
        vec2 uv = vTexCoord * 2.0 - 1.0;
        #define res resolution
        vec2 mouse = (touch/res) * 2.0 - 1.0;
        float ratio = max(res.x, res.y)/min(res.x, res.y);
    
        if(res.x > res.y){
            uv.x *= ratio;
            mouse.x *= ratio;
        }else{
            uv.y *= ratio;
            mouse.y *= ratio;
        }
    
        vec4 col = vec4(1.0);
    
        vec3 l = liquid(uv*2.0, 3.0, vec2(time*0.9, time*1.2));
        col.rgb = ntan(l);
    
        gl_FragColor = vec4(col.rgb, 1.0) * vColor;
    }
    ]]
    end
    
  • edited October 7 Posts: 22
    ---------- TEXT EDITOR CLASS ----------
    
    TextEditor = class()
    
    local editor = {""}
    local lines, cursorPos, nW = 1, 1, 1
    local inline, tempCursorPos = 0, 0
    local head, tail = "", ""
    
    function TextEditor:init()
    
        font("Inconsolata")
        textMode(CORNER)
    
        self.fontSize = 20
        self.t_m = 10
        self.b_m = 10
        self.l_m = 10
        self.r_m = 20
    
        self.monkey = false
    
        self:updateinfo()
    
        showKeyboard()
    end
    
    function TextEditor:updateinfo()
        self.details = "current line: "..lines+inline.." / "..lines..
        "\ncursor position: "..cursorPos.." / "..#editor[lines+inline]..
        "\npreserved position: "..tempCursorPos
    end
    
  • edited October 7 Posts: 22
    function TextEditor:draw()
    
        fontSize(self.fontSize)
    
        nW = textSize(lines)
        for n, t in pairs(editor) do
            local tW, tH = textSize(t)
            local h = self.t_m + tH * n
    
            fill(50, 205)
            pushStyle()
            textMode(CENTER)
            text(n, self.l_m+nW/2+1.25, HEIGHT-h+tH/2-1.25)
            fill(255, 180)
            text(n, self.l_m+nW/2, HEIGHT-h+tH/2)
            popStyle()
    
            if n ~= lines+inline then
                rect(self.l_m+nW+5, HEIGHT-h-0.505, tW+10, tH+1.05)
                fill(25, 130)
                text(t, self.l_m+nW+10+1.25, HEIGHT-h-1.25)
                fill(255)
                text(t, self.l_m+nW+10, HEIGHT-h)
            end
    
        end
    
        local currentLine = editor[lines+inline]
    
        local blink = math.sin(ElapsedTime/0.125)*0.5+1.0
        local cursor = "" --string.rep("\u{25ae}", math.floor(blink))..string.rep("\u{25af}", 1-math.floor(blink))
        if self.monkey then cursor = string.rep(" ", math.floor(blink))..string.rep(" ", 1-math.floor(blink)) end
    
        head = string.sub(currentLine, 1, cursorPos)
        tail = string.sub(currentLine, cursorPos+1, #currentLine)
    
        local editorW, editorH = textSize(currentLine)
        local h = self.t_m + editorH * (lines+inline)
        fill(50 , 140+60*blink)
        rect(-5, HEIGHT-h, self.l_m+2.5, editorH)
        rect(self.l_m+nW+5, HEIGHT-h, WIDTH+10, editorH)
        fill(0)
        text(head..cursor..tail, self.l_m+nW+10+1.25, HEIGHT-h-1.25)
        fill(255)
        text(head..cursor..tail, self.l_m+nW+10, HEIGHT-h)
    
        local sW, sH = textSize(" ")
        local cP = vec2(self.l_m+nW+10+sW*cursorPos, HEIGHT-self.t_m-sH*(lines+inline))
        pushStyle()
        stroke(255)
        strokeWidth(3.75*blink)
        lineCapMode(PROJECT)
        if self.monkey == false then line(cP.x, cP.y+2.5, cP.x, cP.y+self.fontSize-2.5) end
        popStyle()
    
    end
    
    function TextEditor:input(s)
        lines = 0
        local t = {}
        for l in string.gmatch(s, "([^".."\n".."]+)") do
            table.insert(t, l)
            lines = lines + 1
        end
        editor = t
        cursorPos = #editor[lines]
        tempCursorPos = 0
        self:updateinfo()
    end
    
    function TextEditor:output()
        return table.concat(editor, "\n")
    end
    
  • function TextEditor:touched(touch)
        local cL = editor[lines+inline]
        lW, lH = textSize(cL)
    
        inline = 1-lines+math.floor((HEIGHT-self.t_m-touch.y)/lH)
        if inline >= 0 then inline = 0
        elseif 1-inline >= lines then inline = 1-lines
        end
    
        cursorPos = math.floor((touch.x-self.l_m-nW-5)/lW*#cL)
        if #cL == 0 then cursorPos = 0
        elseif cursorPos <= 0 then cursorPos = 0
        elseif cursorPos >= #cL then cursorPos = #cL
        end
        tempCursorPos = cursorPos
    
        self:updateinfo()
    end
    
    function TextEditor:keyboard(key)
    
        -- NEW LINE --
        if key == RETURN then
            editor[lines+inline] = head
            lines = lines + 1
            cursorPos = 0
            tempCursorPos = cursorPos
            table.insert(editor, lines+inline, tail)
        -- DELETE --
        elseif key == BACKSPACE then
            if #head > 0 then
                editor[lines+inline] = string.sub(head, 1, -2)..tail
                cursorPos = cursorPos - 1
            elseif 1-inline < lines then
                lines = lines - 1
                cursorPos = #editor[lines+inline]
                editor[lines+inline] = editor[lines+inline]..tail
                table.remove(editor, lines+inline+1)
            end
            tempCursorPos = cursorPos
        -- STEP UP --
        elseif key == '¥' then
            if inline == 0
            and cursorPos == #editor[lines]
            and tempCursorPos ~= #editor[lines]
            then
                if #editor[lines+inline] < tempCursorPos then
                    inline = inline-1
                end
                cursorPos = tempCursorPos
            elseif 1-inline < lines then
                if #editor[lines+inline-1] >= tempCursorPos then
                    cursorPos = tempCursorPos
                else
                    cursorPos = #editor[lines+inline-1]
                end
                inline = inline - 1
            else
                cursorPos = 0
            end
        -- STEP DOWN --
        elseif key == '˙' then
            if 1-inline == lines
            and cursorPos == 0
            and tempCursorPos ~= 0
            then
                if #editor[lines+inline] > tempCursorPos then
                    cursorPos = tempCursorPos
                else
                    cursorPos = #editor[lines+inline]
                end
            elseif inline < 0 then
                if #editor[lines+inline+1] >= tempCursorPos then
                    cursorPos = tempCursorPos
                else
                    cursorPos = #editor[lines+inline+1]
                end
                inline = inline + 1
            else
                cursorPos = #editor[lines+inline]
            end
        -- STEP LEFT --
        elseif key == '©' then
            if cursorPos > 0 then
                cursorPos = cursorPos - 1
            elseif 1-inline < lines then
                inline = inline - 1
                cursorPos = #editor[lines+inline]
            end
            tempCursorPos = cursorPos
        -- STEP RIGHT --
        elseif key == '∆' then
            if cursorPos < #editor[lines+inline] then
                cursorPos = cursorPos + 1
            elseif inline < 0 then
                inline = inline + 1
                cursorPos = 0
            end
            tempCursorPos = cursorPos
        -- TELEPORT TO START --
        elseif key == 'Á' then
            inline = 1-lines  
            cursorPos = 0
        -- TELEPORT TO END --
        elseif key == 'Ó' then
            inline = 0
            cursorPos = #editor[lines]
        -- JUMP LEFT --
        elseif key == '˝' then
            cursorPos = 0
            tempCursorPos = cursorPos
        -- JUMP RIGHT --
        elseif key == 'Ô' then
            cursorPos = #editor[lines+inline]
            tempCursorPos = cursorPos
        -- TAB REPLACEMENT --
        elseif key == string.match(key, "%c") then
            editor[lines+inline] = head.."  "..tail
            cursorPos = cursorPos + 2
            tempCursorPos = cursorPos
        -- OTHER KEYS --
        elseif key == string.match(key, ".") then
            editor[lines+inline] = head..key..tail
            cursorPos = cursorPos + 1
            tempCursorPos = cursorPos
        end
        self:updateinfo()
    
    end
    
  • dave1707dave1707 Mod
    Posts: 7,865

    @marcelliino If you’re done adding the code above, then you’re missing code at the end in the section function TextEditor:draw(). I loaded the zip file and that works OK. Haven’t tried anything other than just running it.

  • edited October 7 Posts: 22

    @dave1707 yes, I’ve edited it :wink:
    It was because the monkey emoji :/

  • dave1707dave1707 Mod
    Posts: 7,865

    @marcelliino Emojis don’t show very well when displayed in the code in the forum. You’re missing a lot of code in the TextEditor:draw() function in the code you show above. Apparently when an emoji is encountered, none of the code after it is shown. An if statement is started, but not finished.

  • @dave1707 yep, sorry for that. Thanks for the advice :smiley:

Sign In or Register to comment.