mikke89 / RmlUi

RmlUi - The HTML/CSS User Interface library evolved
https://mikke89.github.io/RmlUiDoc/
MIT License
2.75k stars 302 forks source link

Detect if RmlUi need use my inputs #124

Closed rafadsm closed 3 years ago

rafadsm commented 3 years ago

Would it be possible to detect if RmlUi needs to use my entries? Because I would like to prevent other components from being affected by the inputs if RmlUi uses them. Thank you for your attention.

mikke89 commented 3 years ago

Yes, this is already possible. The input functions Context::Process...() return a boolean to indicate whether or not it was used by RmlUi.

rafadsm commented 3 years ago

Cursor events are not boolean, just keyboard

rokups commented 3 years ago

Funny, i had same dilemma recently. It would be very useful. Events returning bools are ok, but they fall short of delivering. This would be extremely useful when combining multiple UI solutions, as well as handling game input. For example if we have a character jump function bound to a character, we would like character to not jump when we are typing text into UI.

Also this would serve as a good hint for managing input focus between multiple Rml::Context instances. Say i have multiple UIs rendered on to multiple 3D objects in the game, each of them is their own Rml::Context. Rml::Context::WantsKeyboardInput() would be a useful indicator to help decide which of these contexts should unfocus their focused elements so they do not capture input. Likewise Rml::Context::WantsMouseInput() (returns true when dragging something for example) would also be helpful.

mikke89 commented 3 years ago

I think a lot of the functionality can already be made in the application logic.

Some things that work well for me:

See also Context::GetHoverElement() and Context::GetFocusElement() which will be be useful for implementing similar functionality.

I'm a bit worried that making some kind of built-in feature for such behavior would not be general enough to actually be useful. There will always be special cases for most applications. I don't think the library can do anything special in this regard, except perhaps make some common patterns easier to do.

What do you think?

rokups commented 3 years ago

Right, you are correct, and this is pretty much what i do now. My general beef with this approach is that there is no easy way to tell if a particular Element wants to take input. Now i do this:

bool RmlUI::IsInputCapturedInternal() const
{
    if (Rml::Element* element = rmlContext_->GetFocusElement())
    {
        const ea::string& tag = element->GetTagName();
        return tag == "input" || tag == "textarea" || tag == "select";
    }
    return false;
}

Which is not a very good approach. If i added a new element that takes keyboard focus i would need to update a completely unrelated spot, which will most likely be forgotten and result in a bug. If Element could tell us whether it wants keyboard input (like textarea/input) or it wants mouse input (like slider knob being dragged) then we could implement rest of input management on our own.

mikke89 commented 3 years ago

@rokups I feel like it's tricky for the library to make the decision for which elements should have exclusive focus. For example, input.range can respond to keyboard input, however, you may want to submit unused keys elsewhere even if it has focus. I think only the application can make this decision. Which elements do you suggest this function should return true for?

I don't actually think your solution is that bad, there are quite a limited number of relevant elements. And this approach is much more flexible than any library solution.

@rafadsm What would be the expected behavior for a boolean for mouse input in your case? Perhaps we could eg. return true if it is hovering over a document in the context, or while the mouse is dragging? I think this could be helpful for some common cases.

rokups commented 3 years ago

Each element which reacts to keyboard input when focused should return true for WantCaptureKeyboard. Likewise each element that is currently active and reacts to mouse input should return true for WantCaptureMouse. Note that this would be completely opt-in, as elements would only provide a hint they want exclusive input, but it would be user's responsibility to act on it or not. In case of range element, say it is at the start of range and user presses left arrow. There is no place to move to the left and user may pass this input to other game systems for processing, and yet range element should return true as it does want keyboard input and is focused. User may do additional checking and pass input or not, depending on range element value and pressed key. Basically these hints would not introduce any limitations, only enable us to better manage input, or do nothing and keep current behavior, of we chose to.

mikke89 commented 3 years ago

Okay I just had a look. The current behavior is that input.text always returns false for key and text input. This should actually already give it "exclusive" access, if the user acts on the return value.

On the other hand input.range always returns true. I'd consider this a bug, it should return false if the arrow keys are pressed at the very least (regardless of whether or not it's at the edge). So let's say we correct that bug, then I think the return value is what users generally would expect.

Such a function also raises many questions. Should it return true for a button? What about a checker-box? Anything that can be tabbed? Anything that can be clicked? I don't know, I feel like a WantCaptureKeyboard function is too abstract, and sounds more confusing than what it could be helpful for. Considering that this behavior can already be emulated, I'm not convinced.

For mouse input on the other hand, I think we can come up with better solutions. But here as well I think a boolean return value should suffice.

rokups commented 3 years ago

What you say makes sense. My only problem is that it does not work quite well with event-driven input systems (like mine). Main issue is that in such cases we need a strict order of event processing. Something like imgui -> [some other UI solution] -> [any number of Rml::Context] -> game input. If order is not guaranteed then bool result after event is processed is not very useful, especially if that order can change when some Rml::Context instance gets removed. This would happen if event subscribers are stored in a hashmap without order guarantee (like in my case). Basically bool result alone creates some non-obvious requirements for user's systems.

With that said, i also think such ordered event processing is a good idea in general, and lack of it is a shortcoming of input processing system which i should correct on my end. I suppose my particular case where i have three different UI systems at play, with multiple contexts of one of them, is a very special case.. But we could certainly use a bool result from mouse processing events, including mouse movement. For example if we drag a slider knob and mouse goes out of window bounds, we still would like Rml::Context to capture mouse movement events as long as mouse button is not released.

mikke89 commented 3 years ago

I understand your problem better now. Generally, I would say a well-defined, strict ordering for input handling is absolutely necessary.

In terms of mouse input, I agree. What do you think about the following. Context functions MouseMove, MouseUp, and MouseDown should return true if any of the following conditions are met:

Elements with pointer-events: none should be ignored, I think this is already the case if we use the context's active and hover elements as indicators.

Anything we are missing then, or edge cases? I guess we'll have to just try and test it.

rokups commented 3 years ago

That should be good. Cant think of anything why any of this would cause any issues.

mikke89 commented 3 years ago

Alright, I made a stab at it. Please let me know if any of this can be improved :)