Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

app,io,widget: [android/macos] fix focus issues #138

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

inkeliz
Copy link
Contributor

@inkeliz inkeliz commented Jun 8, 2024

This patch is intended to fix two issues related to focus and
software-keyboard.

The focus is now reseted if it's lost due to external event,
such as clicking on non-Gio window/activity/fragment.

The software-keyboard will now re-open when clicked, except
if it's ReadOnly.

On macOS it will request focus again if the focus is lose
due to another View in the same Window. However, currently,
it's done by using ShowTextInput.

Fixes: https://todo.sr.ht/~eliasnaur/gio/591

@inkeliz inkeliz changed the title io,widget: [android] improve focus app,io,widget: [android/macos] fix focus issues Jun 9, 2024
This patch is intended to fix two issues related to focus and
software-keyboard.

The focus is now reseted if it's lost due to external event,
such as clicking on non-Gio window/activity/fragment.

The software-keyboard will now re-open when clicked, except
if it's ReadOnly.

On macOS it will request focus again if the focus is lose
due to another View in the same Window. However, currently,
it's done by using ShowTextInput.

Fixes: https://todo.sr.ht/~eliasnaur/gio/591

Signed-off-by: inkeliz <[email protected]>
Comment on lines 48 to +60
- (void)windowDidBecomeKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView;
gio_onFocus(view.handle, 1);
GioView *view = (GioView *)window.contentView;
if ([window firstResponder] == view) {
gio_onFocus(view.handle, 1);
}
}
- (void)windowDidResignKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView;
gio_onFocus(view.handle, 0);
GioView *view = (GioView *)window.contentView;
if ([window firstResponder] == view) {
gio_onFocus(view.handle, 0);
}
Copy link
Contributor Author

@inkeliz inkeliz Jun 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reason to change: This is relative to the window, not the view. If you have multiple views in the same window, it used to incorrectly report that Gio had the focus.

Comment on lines +292 to +294
if !e.ReadOnly {
gtx.Execute(key.SoftKeyboardCmd{Show: true})
}
Copy link
Contributor Author

@inkeliz inkeliz Jun 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't make sense to request SoftKeyboard when it's ReadOnly. Maybe I need to do it on a separated commit?

Comment on lines +522 to +526
func (w *window) ShowTextInput(show bool) {
if show && !w.config.Focused {
C.setFocus(w.view)
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is macOS, which doesn't have SoftwareKeyboard. However, I didn't find any specific function to request focus, and adding a new function will require changes on multiple OSes, even as no-op.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why you need this here and not on other desktop platforms. Why can't C.setFocus be called automatically in the cases where focus was lost?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to call it when the GioView is clicked, and doesn't have focus. I don't think Gio should immediate request for Focus, because it's possible to have multiple non-Gio View in the same Window.

whereswaldon pushed a commit that referenced this pull request Jun 16, 2024
Extracted from #138 by inkeliz.

Signed-off-by: Elias Naur <[email protected]>
whereswaldon pushed a commit that referenced this pull request Jun 16, 2024
Copy link
Contributor

@eliasnaur eliasnaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted the non-controversial changes, leaving only the two that confuse me. Please rebase.

e := &evts[i]
if fe, ok := e.event.(key.FocusEvent); ok {
if !fe.Focus {
state.keyState.focus = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me this should be done in Router.processEvent, near the lines

	case key.EditEvent, key.FocusEvent, key.SelectionEvent:
		var evts []taggedEvent
		if f := state.focus; f != nil {
			evts = append(evts, taggedEvent{tag: f, event: e})
		}
		q.changeState(e, state, evts)

But I'm not perfectly sure what use-case you're fixing. Can you add a test to io/input/key_test.go for my understanding and to guard against regressions?

Copy link
Contributor Author

@inkeliz inkeliz Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm fixing is reseting the focus when receiving key.FocusEvent from "the OS".

Consider that you have one FocusCmd{Tag: TextArea}. If Gio gets one FocusEvent from "the OS" (say: macos), it will not remove the focus from TextArea. That is the issue here.

So, this change changes the state (state.keyState.focus) to nil when the FocusEvent is false. Since it's messing with state I use the changeState function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changeState doesn't change state per se, it adds it to the list of states. A better name would be addStateChange.

See https://github.com/gioui/gio/pull/138/files/101cf1aa478601e9c823f86a86b652b09d768a56#diff-c04c544d7677b4f40b53717995fb8f2e3d67212768e651e229ef2c020fc0d357L434 where state is changed by processEvent, in response to a pointer.Event.

Comment on lines +522 to +526
func (w *window) ShowTextInput(show bool) {
if show && !w.config.Focused {
C.setFocus(w.view)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why you need this here and not on other desktop platforms. Why can't C.setFocus be called automatically in the cases where focus was lost?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants