1. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  2. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  3. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

UI: EditBox - Text Input

Discussion in 'JASS/AI Scripts Tutorials' started by Tasyen, Jun 12, 2019.

Tags:
  1. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,723
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37

    Introduction


    Editboxes are single line text frames beeing editable by players. There are 4 predefined mainframe Editboxes in the default fdfs, but 2 of them are basicly "equal" to some other one.
    • BattleNetEditBoxTemplate (same values as "StandardEditBoxTemplate")
    • StandardEditBoxTemplate
    • StandardDecoratedEditBoxTemplate (same values as "EscMenuEditBoxTemplate")
    • EscMenuEditBoxTemplate
    All of them are not loaded on default. In this tutorial I use "EscMenuEditBoxTemplate" from "UI\FrameDef\UI\escmenutemplates.fdf".
    [​IMG]

    An Editbox is used to let the player type in text. There are 2 events for editboxes handling text input:
    Code (Text):

    FRAMEEVENT_EDITBOX_TEXT_CHANGED
    FRAMEEVENT_EDITBOX_ENTER
     
    In both events one uses
    BlzGetTriggerFrameText
    to get the text
    GetTriggerPlayer
    has inside its box during the event this text is synced.

    BlzGetTriggerFrameText
    length won't exceed 255. Text after the 255. position is not contained inside
    BlzGetTriggerFrameText
    .

    FRAMEEVENT_EDITBOX_ENTER
    , when the local player gave the editbox focus and presses enter/return. The currently local text in the editbox will be
    BlzGetTriggerFrameText
    .

    FRAMEEVENT_EDITBOX_TEXT_CHANGED
    , when for the local player the text of the editbox changed. Happens on adding/Removing Text by player or by code (Setting the text synced will evoke one event for each player). This event will happen quite often.

    Outside of this events one would need
    BlzFrameGetText
    to get the text, but
    BlzFrameGetText
    returns for each player the text he currently has in his editbox -> is not synced in multiplayer. Therefore one has to sync it using the frameevents.​

    Example


    Thats our Lua code for the demo. It Loads the custom tocFile, creates a frame of name "EscMenuEditBoxTemplate" and registeres 2 events to that frame. Also when the local player has its keyboard focus on the editbox and presses enter/return, its current insert text will be shown in the message frame and that message is saved in the gui variable udg_UserInput[playerIndex].
    Code (Lua):

    function LoadToc()
       BlzLoadTOCFile("war3mapimported\\so.toc")
    end

    function EditBoxEnter()
       print("EditBoxEnter:")
       print(BlzGetTriggerFrameText())
       print(GetPlayerName(GetTriggerPlayer()))
      udg_UserInput[GetConvertedPlayerId(GetTriggerPlayer())] = BlzGetTriggerFrameText() --save the text of the local player in a synced manner.
    end

    function TEXT_CHANGED()
       --print("TEXT_CHANGED")
       --print(BlzGetTriggerFrameText())
       --print(GetPlayerName(GetTriggerPlayer()))
    end

    function CreateBox()
       local editbox = BlzCreateFrame("EscMenuEditBoxTemplate", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0),0,0) --create the box
       local eventHandler
       BlzFrameSetAbsPoint(editbox, FRAMEPOINT_CENTER, 0.4, 0.3) -- pos the box
       BlzFrameSetSize(editbox, 0.2, 0.03) --set the boxs size
       eventHandler = CreateTrigger() --Create the FRAMEEVENT_EDITBOX_ENTER trigger
       TriggerAddAction(eventHandler, EditBoxEnter)
       BlzTriggerRegisterFrameEvent(eventHandler, editbox, FRAMEEVENT_EDITBOX_ENTER)
       eventHandler = CreateTrigger() --Create the FRAMEEVENT_EDITBOX_TEXT_CHANGED trigger
       TriggerAddAction(eventHandler, TEXT_CHANGED)
       BlzTriggerRegisterFrameEvent(eventHandler, editbox, FRAMEEVENT_EDITBOX_TEXT_CHANGED)
    end
     
    This Trigger executes the loading and creation functions.
    • Init
      • Events
        • Map initialization
      • Conditions
      • Actions
        • Custom script: LoadToc()
        • Custom script: CreateBox()


    This Trigger will print the text the local player has currently shown in his editbox. The currently Shown text is probably not synced and can desync the game, if it is used in a synced manner.
    • Press Esc
      • Events
        • Player - Player 1 (Red) skips a cinematic sequence
        • Player - Player 2 (Blue) skips a cinematic sequence
      • Conditions
      • Actions
        • Custom script: print(BlzFrameGetText(BlzGetFrameByName("EscMenuEditBoxTemplate",0)))


    [​IMG]

    Text Limits


    One might want to limit the amount of Text the player can put into the editbox, one can set such a limit by code quite simple. The native one uses is
    BlzFrameSetTextSizeLimit takes framehandle frame, integer size
    .
    After using
    BlzFrameSetTextSizeLimit(editbox, 10)
    the editbox can only contain 10 chars.
    As said above it does not make much sense to allow a limit above 255 when the text has to be used inside Events in a synced manner.
    An editbox can containt 4096 chars, if that amount is exceeded, it becomes invisible.(KeepVary)
    On default an editbox uses an TextSizeLimit of -256 which allows any amount of input, but regardless of allowed input only 255 are useable inside the events.

    One can read the current limit with
    BlzFrameGetTextSizeLimit(editbox)
    .

    The Editbox inputtext will be in one line even with a bigger height.

    Inside FDF one can add an EditTextFrame to an Editbox to change the font of the input text.
    Code (Text):

    EditTextFrame "SaveGameFileEditBoxText",
    Frame "TEXT" "SaveGameFileEditBoxText" INHERITS "EscMenuEditBoxTextTemplate" {
    }
     



    Other UI-Frame Tutorials


     

    Attached Files:

    Last edited by a moderator: Oct 4, 2020
  2. _Guhun_

    _Guhun_

    Joined:
    Jun 12, 2010
    Messages:
    395
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    Thanks! This is exactly what I needed to know about player input through frames.
     
  3. KeepVary

    KeepVary

    Joined:
    May 12, 2019
    Messages:
    12
    Resources:
    0
    Resources:
    0
    I've put in over a million chars to the EditBox, but
    BlzFrameGetText
    still can get all of them (although gamerun become extremely slow).
    It didn't seem has a limit.

    Here's the code I used to test:
    Code
    Code (Lua):

    function FrameTest()
        print("toc loaded:", BlzLoadTOCFile("war3mapImported/CustomToc.toc"))
        -- Needs a custom toc that include "UI\FrameDef\Glue\BattleNetTemplates.fdf"
       
        local null = BlzGetFrameByName("", 0)
        local gui = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)
        local point = {
            c = FRAMEPOINT_CENTER
        }
        local eventList = {
            editbox_text = FRAMEEVENT_EDITBOX_TEXT_CHANGED,
            editbox_enter = FRAMEEVENT_EDITBOX_ENTER
        }
       
        local function createTemplate(template, x, y, w, h, parent, id)
            x = x or 0; y = y or 0; parent = parent or gui; id = id or 0
            local frame = BlzCreateFrame(template, parent, 0, id)
            if frame ~= null then
                BlzFrameSetAbsPoint(frame, point.c, x, y)
                if w and h then
                    BlzFrameSetSize(frame, w, h)
                end
            else
                print('Failed on create "' .. template .. '"')
            end
            return frame
        end
       
        local function addEvent(frame, event, func)
            local trg = CreateTrigger()
            BlzTriggerRegisterFrameEvent(trg, frame, eventList[event])
            TriggerAddAction(trg, func)
        end
       
        local function editboxTest()
            local ebox = createTemplate("BattleNetEditBoxTemplate", 0.3, 0.5, 0.2, 0.04)
            local tarea = createTemplate("BattleNetTextAreaTemplate", 0.3, 0.3, 0.2, 0.3)
            addEvent(ebox, "editbox_text", function()
                ClearTextMessages()
                print("EditBox length:", #BlzFrameGetText(BlzGetTriggerFrame()))
            end)
            -- When press enter/return in the EditBox, a callback will get it's text and set into the TextArea.
            addEvent(ebox, "editbox_enter", function()
                local txt = BlzFrameGetText(BlzGetTriggerFrame())
                BlzFrameSetText(tarea, txt)
                print("TextArea length:", #txt)
            end)
            local testText = ""
            for i = 1, 5000 do -- This will make a string that has nearly 120000 chars, then set it into the EditBox.
                local line = "this is test text " .. i
                testText = testText .. line .. "|n"
            end
            BlzFrameSetText(ebox, testText)
        end
        editboxTest()
    end
     

    Btw, if text in the EditBox got a length that over 4096, they'll become invisible.
     
  4. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,723
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    Intressting. I hardcoded that text with in one line in world editor. Might also be that 1082 is the allowed amount of text per BlzFrameSetText.
     
  5. KeepVary

    KeepVary

    Joined:
    May 12, 2019
    Messages:
    12
    Resources:
    0
    Resources:
    0
    I tested the code (in my post above) in Jass version, found that a string variable in Jass got a 4096 length limit.

    Then I tried hardcoding an one line string as you do, found that if the string's length exceeded 1100, jasshelper will show a syntax error that said "String literal size limit exceeded /unclosed string", else when less than or equal to 1100, the string will limited in 1023 chars.
    I guess this is why you found a number of 1082, and perhaps it's because of the difference in charset (WE language) that I got a 1023.
    I've also tested hardcoding in Lua version, it doesn't have the problem.

    Anyway, there's nothing about those natives, it's a Jass problem with WE.
     
  6. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,430
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    @KeepVary Thanks for those tests, that is really useful to know. The ~1024 char limit is definitely a JASS issue (and iirc it can get close to 2048 with concatenation), it's really cool that Lua doesn't have that constraint!

    @Tasyen Do you mind updating the first post with that information? According to post #3, it seems there is no practical limit to how much text you can put/retrieve from a text input frame, but beyond 4096 characters, it becomes invisible. (maybe it is worth setting 4096 as the limit then?) After that it should be good for approval.

    P.S. Great tutorial btw, I really like that you included that tip about retrieving frame text in a synchronous manner. :)
     
  7. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,723
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    I made a practical misstake when recommenting to create the EditBox Templates. I mean it is not wrong but their TextStyling is bad. It is better to do it like shown in "SaveGameFileEditBox" from "escmenusavegamepanel.fdf". One inherits one of the templates, defines a functional child Frame: EditTextFrame is managing the Text style.
    Code (Text):

    IncludeFile "UI\FrameDef\UI\EscMenuTemplates.fdf",

    Frame "EDITBOX" "YourEditBox" INHERITS WITHCHILDREN "EscMenuEditBoxTemplate" {
        Width 0.362,
        Height 0.037,

        EditTextFrame "YourEditBoxText",
        Frame "TEXT" "YourEditBoxText" INHERITS "EscMenuEditBoxTextTemplate" {
        }
    }
     
    Edit: The cursor has an own FrameFont, which has to be setuped inside the EDITBOX directly, it is not taken from EditTextFrame.
     
    Last edited: Jan 27, 2020
  8. Sabe

    Sabe

    Joined:
    Jul 30, 2018
    Messages:
    434
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Here, I made a JASS version in case someone has no idea how to work with code. :)

    Code (vJASS):

    function LoadToc takes nothing returns nothing
        call BlzLoadTOCFile("war3mapimported\\so.toc")
    endfunction

    function EditBoxEnter takes nothing returns nothing
        call DisplayTextToForce(GetPlayersAll(), "EditBoxEnter:")
        call DisplayTextToForce(GetPlayersAll(), BlzGetTriggerFrameText())
        call DisplayTextToForce(GetPlayersAll(), GetPlayerName(GetTriggerPlayer()))
        set udg_UserInput[GetConvertedPlayerId(GetTriggerPlayer())] = BlzGetTriggerFrameText() // save the text of the local player in a synced manner.
    endfunction

    function TEXT_CHANGED takes nothing returns nothing
    endfunction

    function CreateBox takes nothing returns nothing
        local framehandle editbox = BlzCreateFrame("EscMenuEditBoxTemplate", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0),0,0) // create the box
        local trigger eventHandler
       
        call BlzFrameSetAbsPoint(editbox, FRAMEPOINT_CENTER, 0.4, 0.3) // Position of the box
        call BlzFrameSetSize(editbox, 0.2, 0.03) // Boxes size
       
        set eventHandler = CreateTrigger() // Create the FRAMEEVENT_EDITBOX_ENTER trigger
        call TriggerAddAction(eventHandler, function EditBoxEnter)
        call BlzTriggerRegisterFrameEvent(eventHandler, editbox, FRAMEEVENT_EDITBOX_ENTER)
       
        set eventHandler = CreateTrigger() // Create the FRAMEEVENT_EDITBOX_TEXT_CHANGED trigger
        call TriggerAddAction(eventHandler, function TEXT_CHANGED)
        call BlzTriggerRegisterFrameEvent(eventHandler, editbox, FRAMEEVENT_EDITBOX_TEXT_CHANGED)
    endfunction
     
     

    Attached Files:

  9. Ice

    Ice

    Joined:
    Sep 4, 2004
    Messages:
    164
    Resources:
    14
    Models:
    2
    Icons:
    12
    Resources:
    14
    how would one remove the textbox after hitting enter and or atleast clear the entered text from box?
     
  10. Sabe

    Sabe

    Joined:
    Jul 30, 2018
    Messages:
    434
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I made you an example already. Check out the private conversation.
     
  11. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,723
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    One sets the Frames text only for the Triggering Player to "", that is done inside the FRAMEEVENT_EDITBOX_ENTER Action.
    Code (Lua):
    if GetLocalPlayer() == GetTriggerPlayer() then
        BlzFrameSetText(BlzGetTriggerFrame(), "")
    end
    That will also trigger a FRAMEEVENT_EDITBOX_TEXT_CHANGED with that player as Triggering Player.
     
  12. Chaosy

    Chaosy

    Tutorial Reviewer

    Joined:
    Jun 9, 2011
    Messages:
    11,102
    Resources:
    18
    Icons:
    1
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    18
    Took the liberty to make some changes as the author happens to have a bunch of tutorials I figure it would be a pain to update all of them.

    Changes made:
    1. Changed one title to h2 from h3 to match the other titles
    2. Changed the title color to orange (from default) to match the last title
    3. Applied an indent after each title
    4. Swapped the image links to direct img tags since they are small enough to be viewed in full size directly
    5. Added introduction title at the top

    Sending this to the JASS/AI section as that is where the other tutorials in the series are even if I would possibly argue that this is not a "jass" heavy tutorial.
    If @Tasyen wants it in another section, poke me. And preferably mark which sub-forum you are "applying" for by setting a prefix to the thread.