Skip to content

feat: Add sequential Hangul input with number row and custom layouts#1275

Open
codeg-old wants to merge 11 commits into
Julow:masterfrom
codeg-dev:feat/korean-custom-layout
Open

feat: Add sequential Hangul input with number row and custom layouts#1275
codeg-old wants to merge 11 commits into
Julow:masterfrom
codeg-dev:feat/korean-custom-layout

Conversation

@codeg-old

Copy link
Copy Markdown

Summary

This PR adds proper Korean (Hangul) support to Unexpected Keyboard with the following features:

Korean Input

  • Sequential Hangul input: Type "ㄱㅏㄴㅏㄷㅏ" and get "가나다" immediately (no composing text, no underlining)
  • Removed FLAG_LATCH from Hangul keys to enable immediate key_up processing
  • NFC precomposed syllables: Each keystroke builds the syllable in real-time

UI Improvements

  • Number row: Added a dedicated 1-0 row at the top of keyboard layouts
  • CandidatesView: Auto-hides when only 'No dictionary installed' message would be shown, freeing up vertical space
  • ISO-style punctuation: Punctuation keys arranged following ISO standards

New Layouts

  • 두벌식 Custom (hang_dubeolsik_custom.xml): Korean layout with number row and optimized swipe keys
  • QWERTY Custom (latn_qwerty_custom.xml): English layout matching the Korean structure

Changes

  • KeyEventHandler.java: Complete rewrite of Hangul handling logic
  • KeyValue.java: Hangul keys no longer use FLAG_LATCH
  • KeyModifier.java: Removed Hangul combine logic
  • CandidatesView.java: Added update_visibility() for auto-hiding
  • New layout files in srcs/layouts/

codeg-old added 3 commits May 5, 2026 12:35
- Replace composing text with immediate NFC syllable output

- Remove FLAG_LATCH from Hangul keys to enable direct key_up

- Add CandidatesView auto-hide when 'No dictionary installed'

- Add number row to Korean layout with ISO punctuation placement

- Add custom Korean (Dubeolsik+) and English (QWERTY Custom) layouts
@codeg-old codeg-old force-pushed the feat/korean-custom-layout branch from 72768af to d2d9f4e Compare May 5, 2026 03:37
@codeg-old

Copy link
Copy Markdown
Author

No issues found. Checked for bugs and AGENTS.md/PRINCIPLES.md compliance.\n\nVerification evidence: HangulInputTest and assembleDebug passed with OpenJDK 17; moto_g77 and gltab device input QA showed 적극적 and 하면 correctly.\n\n🤖 Generated with OpenCode

@arnekat

arnekat commented Jun 9, 2026

Copy link
Copy Markdown

I agree and would like to emphasize that currently (2.0.4), Korean typing is fundamentally unusable in practice. This looks like a more straightforward approach. So hopefully this could be quickly resolved in the next update.

A minor point for the UI Improvements: adding a dedicated number row can be found in the options settings so unsure if necessary.

(Also, I am not sure if this is part of your solution, but if not consider perhaps adding ㅐ->ㅒ and ㅔ->ㅖ for swiping O and P keys (and ㅃㅉㄸㄲㅆ for QWERT keys) in addition to the shift/capitalization method.

Thank you and I look forward to this being rolled out soon, so I can use this keyboard for Korean typing.

@Julow Julow mentioned this pull request Jul 3, 2026

@Julow Julow left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Sorry for the slow reply!
I think this makes the code too complex and I'm trying to come up with ideas. I don't have the time to do a full review for the next few weeks.

I don't like the complexity you added in KeyEventHandler. This class is modified often and with this PR, it becomes spaghetti.

I attempted this in the past in this PR: https://github.com/Julow/Unexpected-Keyboard/pull/594/changes, perhaps you can take inspiration from it ?
It doesn't actually implement Hangul composition (the call to Normalizer2 should be replaced by your code) but implements the composing text (the experience you want).
It's very old so it might require some work to be merged into the current code.

@chlwnsgud7

Copy link
Copy Markdown

I tested this PR and it works well overall. A few things I noticed:

  • res/values/layouts.xml (lines 137–138): The Dubeolsik layout entry appears twice, which would cause it to show up twice in the layout selection list.

  • srcs/layouts/hang_dubeolsik_custom.xml vs srcs/layouts/hang_dubeolsik_kr.xml: These two files appear to have identical contents. Is _custom intentional as a user-editable copy, or is it a duplicate that can be removed?

  • srcs/layouts/latn_qwerty_custom.xml: A custom Latin QWERTY layout seems out of scope for a Hangul input PR. Was this included intentionally?

@chlwnsgud7 chlwnsgud7 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One more thing I noticed: apply_hangul_shift() in KeyModifier.java appears to handle shifted jamo (e.g. ㅒ, ㅖ, double consonants) inline, but the existing codebase defines shift behaviour in srcs/compose/shift.json and compiles it into ComposeKeyData.java via compile.py. Would it make sense to follow that convention here as well?

Move the inline apply_hangul_shift() double-consonant and iotized-vowel
mappings into srcs/compose/shift.json, following the existing shift
convention (compiled into ComposeKeyData via compile.py). ComposeKey.apply
now routes Hangul_initial/Hangul_medial keys through their string form so
the shift compose data applies to them.

Refs: PR Julow#1275 review
@codeg-dev

Copy link
Copy Markdown

@chlwnsgud7 Good catch — you're right that shift behaviour belongs in srcs/compose/shift.json rather than inline in KeyModifier.java. Fixed in ae6a998.

Changes:

  • Removed apply_hangul_shift() from KeyModifier.java.
  • Added the double-consonant / iotized-vowel mappings to srcs/compose/shift.json:
    "ㄱ": ":ㄲ", "ㄷ": ":ㄸ", "ㅂ": ":ㅃ", "ㅅ": ":ㅆ", "ㅈ": ":ㅉ",
    "ㅐ": ":ㅒ", "ㅔ": ":ㅖ"
    (compiled into ComposeKeyData.java via compile.py, same as every other shift entry).
  • One small enabler in ComposeKey.java: the consonant keys are Hangul_initial kind (registered in KeyValue.getSpecialKeyByName), which ComposeKey.apply previously didn't handle, so the shift data would never reach them. I routed Hangul_initial/Hangul_medial through their string form so the compose lookup applies:
    case String:
    case Hangul_initial:
    case Hangul_medial:
      return apply(state, kv.getString());
    The : prefix on the results forces a string-final state so the lookup returns the properly-typed key via getKeyByName (preserving Hangul_initial kind for consonants).

Verification: ModmapTest.hangul_shift_mappings covers all 7 mappings and passes, full testDebugUnitTest + assembleDebug are green, the check-generated command reproduces ComposeKeyData.java byte-for-byte, and I confirmed shift+ㅂ→ㅃ and shift+ㄷ→ㄸ on a real device.

🤖 Generated with OpenCode

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.

6 participants