lc-soft/LCUI

Add clipboard support #216

lc-soft posted onGitHub

Is your feature request related to a problem? Please describe.

Sometimes we need to paste some content into the input box, but LCUI does not support it.

Describe the solution you'd like

Add clipboard support to meet the following requirements:

  • textedit: CTRL+V should be able to insert the content of the clipboard to the caret position.
  • Should work on Linux.
  • Should work on Windows.

Describe alternatives you've considered

None.

Additional context

You can refer to the source code of SDL:

Note: If you think you can do it, please request a bounty on issuehunt.io to tell everyone how much bounty you need. If you think this feature is necessary, you can also fund this issue on issuehunt.io.


@lc-soft has funded $2.00 to this issue.


posted by issuehunt-app[bot] over 4 years ago

@lc-soft win上 一些组合键输入乱码 例如:ctrl+v ctrl+n 等。 顺便附上 ctrl+v win32实现

static void OnKeyup(LCUI_Widget w, LCUI_WidgetEvent e, void *arg)
{
    if (e->key.code == 229 || e->key.code == 86)
    {
        if (LCUIKeyboard_IsHit(17))
        {
            OpenClipboard(NULL);
            HGLOBAL hGlobal = NULL;
            hGlobal = GetClipboardData(CF_UNICODETEXT);
            wchar_t *pGlobal = (wchar_t *)GlobalLock(hGlobal);
            CloseClipboard();
            TextEdit_InsertTextW(w, pGlobal);
        }
    }
}
posted by tangxg over 4 years ago

@tangxg 这方式获取到的是纯文本吗?如果剪切板里有图片或富文本内容,那 GlobalLock() 获取到的是什么内容?

posted by lc-soft over 4 years ago

@lc-soft 目前只是在win10上测试过 获取(word,网页,VS编辑器,富文本编辑器等等)是纯文本,其他未测试过。 我看了下WIN32API 复制文件时应该可以取到 路径 测试发现内存地址都没得 是个NULL。

posted by tangxg over 4 years ago

@lc-soft How would you expect the feature to be implemented? I assume the approach would be adding clipboard.c to platform (and equivalent linux_x11clipboard.c)

But then, how would the copy event be handled, It doesn't seem synchronous. The linked SDL approach seems to have a blocking while loop, however, I've seen another implementation, which can be found here. The explanation I've found was

As you ask another application to prepare the buffer, and that may take some time, the request is asynchronous: the owner prepares the buffer, saves it in a specified location (window property is used as a temporary storage) and notifies you with SelectionNotify event when it's done.

So I'm not certain you can just request XNextEvent and guarantee that next event will be SelectionNotify.

posted by WhoAteDaCake over 3 years ago

@WhoAteDaCake I expect the feature to be implemented as follows:

  • Add file src/clipboard.c.
  • For Linux x11, it should use LCUI_BindSysEvent() to bind the SelectionNotify event handler.
  • It should process the SelectionNotify event and store the clipboard content, only after calling the constructor function (for example: clipboard_create()).
posted by lc-soft over 3 years ago

Wouldn't it have to know which widget is currently in focus, so it knows where to send the data once SelectionNotify is retrieved. Since SelectionNotify is triggered after XConvertSelection is called, we should know where the event originated from. Also, from what I understand we need to track the variables passed down to XConvertSelection, so there would need to be some sort of state

My idea is to separate into something like:

Clipboard

Though I can image you'd want to avoid using things like LCUIMutex_Init within platform specific code, so not really sure what's the best approach here :thinking:

Any thoughts

posted by WhoAteDaCake over 3 years ago

Wouldn't it have to know which widget is currently in focus, so it knows where to send the data once SelectionNotify is retrieved. Since SelectionNotify is triggered after XConvertSelection is called, we should know where the event originated from. Also, from what I understand we need to track the variables passed down to XConvertSelection, so there would need to be some sort of state

My idea is to separate into something like:

Clipboard

Though I can image you'd want to avoid using things like LCUIMutex_Init within platform specific code, so not really sure what's the best approach here thinking

Any thoughts

@WhoAteDaCake Why call the new ReigsterPaste() function instead of the existing BindEvent() function?

I expected the clipboard event processing flow to be as follows:

  • Trigger LCUI_PASTE event
  • Call LCUIWIdget_OnPaste() event handler
  • Call LCUIWidget_GetFocus() to get focus widget (we assume TextEdit widget is the focus)
  • Trigger LCUI_WEVENT_PASTE event for focus widget
  • Call TextEdit_OnPaste() event handler
  • Call Clipboard_GetText()
  • Call TextEdit_InsertTextW()
posted by lc-soft over 3 years ago

@lc-soft

The problem is at the step:

  • Clipboard_GetText

Clipboard text can't be retrieved synchronously, we need to ask for it, by calling XConvertSelection with some variables (which we need to store and reference when copying the clipboard after SelectionNotify is received). Now we have to wait until SelectionNotify is received from the system and only then can we get the actual text.

I need to try and implement first I think, rather than guessing, but I think it we would wan't to let each platform handle the initial bit, so:

  • linux_x11keyboard.c
    • CTRL + V received, call (*SOME SORT OF INTERNAL EVENT)
  • linux_x11clipboard.c
    • Subscribe to (*SOME SORT OF INTERNAL EVENT)
      • Call XConvertSelection, save the needed variables to file state (via static)
      • return
    • Subscribe to SelectionNotify OnSelectionNotify
      • Function called, store the text locally
      • Trigger LCUI_PASTE_READY event ​
  • clipboard.c
    • Subscribe to LCUI_PASTE_READY
      • On trigger save text locally and call LCUI_PASTE

*SOME SORT OF INTERNAL EVENT - Not sure what's the best one to use here

Now we can continue with the steps you've mentioned.

I guess the biggest concern is how to communicate for the Widget -> System flow and Widget -> System -> Platform, since for Copy event, I think we will need to store the copied text on the platform level (linux_x11clipboard.c) due to the SelectionRequest event being called.

If we choose to do that, we could skip the last step I've mentioned and have clipboard.c call platform specific implementation to retrieve the pasted text.

I hope I'm making sense, let me know if any of my assumptions are wrong, I'm still trying to wrap my head around the codebase :)

posted by WhoAteDaCake over 3 years ago

Clipboard text can't be retrieved synchronously, we need to ask for it

Yes, I know. My idea is to asynchronously process SelectionNotify event and store clipboard data, and when the data is ready, trigger the LCUI_PASTE event to tell TextEdit widget, This makes the call to 'Clipboard_GetText()' synchronous for TextEdit.

I need to try and implement first I think, rather than guessing, but I think it we would wan't to let each platform handle the initial bit, so: ...

I think LCUI_PASTE_READY and LCUI_PASTE should be merged.

I guess the biggest concern is how to communicate for the Widget -> System flow and Widget -> System -> Platform, since for Copy event, I think we will need to store the copied text on the platform level (linux_x11clipboard.c) due to the SelectionRequest event being called.

I agree.

posted by lc-soft over 3 years ago

@lc-soft Few questions

  • would you expect the LCUI_RegisterPaste event to be called directly from OnKeyboardMessage or should it trigger a custom event?
  • If it is a direct call, how would I go about including it using the headers (I'm not a C expert)? Would I include linux_x11clipboard.h or go about it some other way? Thanks for your help!
posted by WhoAteDaCake over 3 years ago

@WhoAteDaCake

Sorry, there are some problems with my comments above, the idea described in my comment is only suitable for read clipboard content when CTRL+V is pressed, not for read clipboard content at any time.

I redesigned the usage of the Clipboard API. The example pseudo code is as follows:

typedef struct LCUI_TextEditRec_ {
    // ...

    LCUI_Clipboard clipboard;
};

void TextEdit_OnInit(LCUI_Widget w)
{
    // ...

    // Create a clipboard object for storing clipboard data
    textedit->clipboard = LCUI_CreateClipboard();

    // ...
}

void TextEdit_OnDestroy(LCUI_Widget w)
{
    // ...

    LCUI_DestroyClipboard(textedit->clipboard);
    textedit->clipboard = NULL;

    // ...
}

void TextEdit_OnPaste(LCUI_Widget w, LCUI_WidgetEvent e, void *arg)
{
    const char *text;
    const wchar_t *wtext;
    LCUI_Clipboard cb = arg;

    if (Clipboard_HasText(cb)) {
        text = Clipboard_GetText(cb);

        // Convert to unicode string
        // ...

        TextEdit_InsertTextW(w, wtext);
    }
}


// In Linux x11, this function will be called when SelectionNotify event is received
void TextEdit_OnClipboardReady(LCUI_Clipboard clipboard, void *arg)
{
    LCUI_Widget w = arg;
    LCUI_WidgetEventRec e = { 0 };

    LCUI_InitWidgetEvent(&e, "paste");
    Widget_TriggerEvent(w, &e, clipboard);
}

void TextEdit_OnKeyDown(LCUI_Widget w, LCUI_WidgetEvent e, void *arg)
{
    LCUI_Clipboard clipboard;
    LCUI_TextEdit textedit;

    // ...

    if (is pressed Ctrl+V) {
        // Asynchronously preparing clipboard data
        // In linux x11, it will call XConvertSelection()
        LCUI_UseClipboard(textedit->clipboard, TextEdit_OnClipboardReady, w);
    }
}

Note:

  • No LCUI_PASTE and LCUI_PASTE_READY events
  • The clipboard does not need to care about keyboard events
posted by lc-soft over 3 years ago

@lc-soft

Wouldn't storing clipboard at widget, rather than system level cause problems? If we wan't to support CTRL+C feature, we'd need to support SelectionRequest event (It would come if some other window asks for the the text we copied), meaning we need access to clipboard selected text from within linux_x11clipboard.c.

I'd propose, we keep the clipboard data as part of each system file linux_x11clipboard.c, windows_clipboard.c etc. Other than that, I think your suggestion is the right approach!

posted by WhoAteDaCake over 3 years ago

@WhoAteDaCake

Well, storing the clipboard in widgets would increase the cost and implementation complexity of the clipboard API, so I think we can choose to store the clipboard at the system level.

posted by lc-soft over 3 years ago

@lc-soft has rewarded $1.80 to @whoatedacake. See it on IssueHunt

  • :moneybag: Total deposit: $2.00
  • :tada: Repository reward(0%): $0.00
  • :wrench: Service fee(10%): $0.20
posted by issuehunt-app[bot] almost 2 years ago

Fund this Issue

$2.00
Rewarded

Rewarded pull request

Recent activities

whoatedacake was rewarded by lc-soft for lc-soft/LCUI# 216
almost 2 years ago
lc-soft submitted an output to  lc-soft/ LCUI# 216
almost 2 years ago