Undo/Redo in MultiLineEditBox

For help with general CEGUI usage:
- Questions about the usage of CEGUI and its features, if not explained in the documentation.
- Problems with the CMAKE configuration or problems occuring during the build process/compilation.
- Errors or unexpected behaviour.

Moderators: CEGUI MVP, CEGUI Team

User avatar
sjcomp
Not too shy to talk
Not too shy to talk
Posts: 30
Joined: Wed Apr 27, 2005 14:59
Contact:

Undo/Redo in MultiLineEditBox

Postby sjcomp » Sun Oct 19, 2008 13:35

Hello,

I'm curious if there is a widget that extends functionality of MultiLineEditBox to have undo/redo and maybe copy/paste? And if not, what is the best way to approach this?

Thanks.

Pompei2
Home away from home
Home away from home
Posts: 489
Joined: Tue May 23, 2006 16:31

Postby Pompei2 » Sun Oct 19, 2008 15:04

I have implemented an OS-independent copy/paste mechanism using SCRAP

Please note that there is no way to get the currently "Active" widget from CEGUI, thus you need to implement "this->getActiveWidget()" yourself in some way.

Code: Select all

/// Copies the currently selected text.
/** This function copies the currently selected text (maybe more to come ?)
 *  into the clipboard. It is system-independent !
 *
 * \param in_bDelete Whether to delete the copied object (cut) or not (copy)
 *
 * \return If successfull: ERR_OK
 * \return If failed:      An error code <0
 *
 * \author Pompei2
 */
int CFTSUI::copy(bool in_bDelete)
{
    CEGUI::Window *activeWin = this->getActiveWidget();
    CEGUI::String s;
    const char *pszText = NULL;

    if(activeWin == NULL)
        return ERR_OK;

    // Cancel, if the active widget is not an editable.
    if(activeWin->getType() != "ArkanaLook/Editbox" &&
       activeWin->getType() != "ArkanaLook/MultiLineEditbox")
        return ERR_OK;

    // Get the currently selected text from the widget.

    // First, for normal editboxes:
    if(activeWin->getType() == "ArkanaLook/Editbox") {
        CEGUI::Editbox *e = (CEGUI::Editbox *)activeWin;

        // No need to copy an empty selection.
        if(e->getSelectionLength() == 0)
            return ERR_OK;

        s = CEGUI::String(e->getText(), e->getSelectionStartIndex(), e->getSelectionLength());
    // Then, for normal MultiLineEditbox:
    } else if(activeWin->getType() == "ArkanaLook/MultiLineEditbox") {
        CEGUI::MultiLineEditbox *e = (CEGUI::MultiLineEditbox *)activeWin;

        // No need to copy an empty selection.
        if(e->getSelectionLength() == 0)
            return ERR_OK;

        s = CEGUI::String(e->getText(), e->getSelectionStartIndex(), e->getSelectionLength());
    }

    // To cut the text out, we use a little trick: simulate a keypress.
    if(in_bDelete) {
        CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Backspace);
    }


    pszText = s.c_str();
    FTSMSG("%s", FTS_NOMSG, pszText);

    put_scrap(T('T','E','X','T'), strlen(pszText), pszText);

    return ERR_OK;
}

/// Copies the currently copied text into the current widget.
/** This function pastes the content of the clipboard into the currently selected
 *  object. Currently this works only for texts.
 *
 * \return If successfull: ERR_OK
 * \return If failed:      An error code <0
 *
 * \author Pompei2
 */
int CFTSUI::paste(void)
{
    CEGUI::Window *activeWin = this->getActiveWidget();
    char cNewline = ' ';

    if(activeWin == NULL)
        return ERR_OK;

    // Cancel, if the active widget is not an editable.
    if(activeWin->getType() == "ArkanaLook/Editbox")
        cNewline = ' ';
    else if(activeWin->getType() == "ArkanaLook/MultiLineEditbox")
        cNewline = '\n';
    else
        return ERR_OK;

    char *pszScrap = NULL;
    int iScraplen = 0;

    get_scrap(T('T','E','X','T'), &iScraplen, &pszScrap);
    if(iScraplen == 0) {
        FTSMSG("Text scrap is empty\n", FTS_NOMSG);
        return ERR_OK;
    }

    // Convert mac newlines to unix newlines or spaces, whatever is needed.
    for(int i = 0 ; i < iScraplen ; ++i) {
        if(pszScrap[i] == '\r')
            pszScrap[i] = cNewline;
    }

    // Build an CEGUI string based on this, to keep the accents and stuff.
    CEGUI::String s((const CEGUI::utf8 *)pszScrap);

    // And now inject each charachter, so it get placed into the editbox.
    for(size_t i = 0 ; i < s.size() ; i++) {
        if(s[i] == '\n') {
            CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Return);
        } else
            CEGUI::System::getSingleton().injectChar(s[i]);
    }

    free(pszScrap);

    return ERR_OK;
}


Regarding undo/redo I have nothing for you.

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Postby Jamarr » Mon Oct 20, 2008 15:46

Pompei2 wrote:Please note that there is no way to get the currently "Active" widget from CEGUI, thus you need to implement "this->getActiveWidget()" yourself in some way.


Technically speaking that is incorrect; cegui does have this functionality built-in: CEGUI::System::getSingleton().getGUISheet()->getActiveChild().

Regarding copy/paste, it depends on how widly available you want to copied/cut data to be. If it's only going to be process-specific, I'm sure you won't have any trouble figuring it out. If you want the data to be system-wide, there are many examples on the web for accessing the system clipboard.

Regarding undo/redo, you might try looking into some design patterns like: Command Pattern or Memento Pattern.

Pompei2
Home away from home
Home away from home
Posts: 489
Joined: Tue May 23, 2006 16:31

Postby Pompei2 » Mon Oct 20, 2008 21:58

Jamarr wrote:Technically speaking that is incorrect; cegui does have this functionality built-in: CEGUI::System::getSingleton().getGUISheet()->getActiveChild().

I don't remember what exactly, but there was a problem with it, that "Active" was not the same as "Focused" or so. don't remember it.

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Postby Jamarr » Mon Oct 20, 2008 23:52

That is odd. It must have been a bug in a previous version, as I'm using it to find the active/focused widget and works fine for me in v0.6.1.

The catch is that it will return child/auto widgets, so say if you where using a Combobox and are typing into the child Editbox it would return the Editbox - so you would have to traverse the parent hierarchy to get to the Combobox; that could be misperceived as not working if you had different expectations.

User avatar
sjcomp
Not too shy to talk
Not too shy to talk
Posts: 30
Joined: Wed Apr 27, 2005 14:59
Contact:

Postby sjcomp » Tue Oct 21, 2008 23:05

Thanks Pompei2, I'll have a look at it. I want all these operations to work for a single instance of the editbox at the moment, so getting active widget is not a problem for me. When a person types some text in or selects and deletes text I'd like to be able to undo/redo these changes. How can I keep track of changes in the edit box in real time?

Thanks.

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Postby Jamarr » Wed Oct 22, 2008 17:39

sjcomp wrote:When a person types some text in or selects and deletes text I'd like to be able to undo/redo these changes. How can I keep track of changes in the edit box in real time?


Jamarr wrote:Regarding undo/redo, you might try looking into some design patterns like: Command Pattern or Memento Pattern.


Return to “Help”

Who is online

Users browsing this forum: No registered users and 25 guests