lc-soft / LCUI

C library for building user interfaces
https://lcui-dev.github.io
MIT License
4.13k stars 356 forks source link

Add clipboard support #216

Closed lc-soft closed 1 year ago

lc-soft commented 4 years ago

Issuehunt badges

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:

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.


IssueHunt Summary #### [whoatedacake whoatedacake](https://issuehunt.io/u/whoatedacake) has been rewarded. ### Backers (Total: $2.00) - [lc-soft lc-soft](https://issuehunt.io/u/lc-soft) ($2.00) ### Submitted pull Requests - [#271 feat(platform): add clipboard support](https://issuehunt.io/r/lc-soft/LCUI/pull/271) --- ### Tips - Checkout the [Issuehunt explorer](https://issuehunt.io/r/lc-soft/LCUI/) to discover more funded issues. - Need some help from other developers? [Add your repositories](https://issuehunt.io/r/new) on IssueHunt to raise funds.
issuehunt-oss[bot] commented 4 years ago

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


tangxg commented 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);
        }
    }
}
lc-soft commented 4 years ago

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

tangxg commented 4 years ago

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

WhoAteDaCake commented 2 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.

lc-soft commented 2 years ago

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

WhoAteDaCake commented 2 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

lc-soft commented 2 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:

WhoAteDaCake commented 2 years ago

@lc-soft

The problem is at the step:

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:

*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 :)

lc-soft commented 2 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.

WhoAteDaCake commented 2 years ago

@lc-soft Few questions

lc-soft commented 2 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:

WhoAteDaCake commented 2 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!

lc-soft commented 2 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.

issuehunt-oss[bot] commented 1 year ago

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