@@ -351,11 +351,10 @@ void FcitxCskkContext::updateUI() {
351351 }
352352
353353 // Preedit
354- auto preedit = skk_context_get_preedit (context_);
355- Text preeditText;
356- // FIXME: Pretty text format someday.
357- preeditText.append (std::string (preedit));
358- preeditText.setCursor (static_cast <int >(strlen (preedit)));
354+ uint32_t stateStackLen;
355+ auto preeditDetail = skk_context_get_preedit_detail (context_, &stateStackLen);
356+ auto [mainPreedit, supplementPreedit] =
357+ FcitxCskkContext::formatPreedit (preeditDetail, stateStackLen);
359358
360359 // CandidateList
361360 int currentCursorPosition =
@@ -383,10 +382,11 @@ void FcitxCskkContext::updateUI() {
383382 }
384383
385384 if (ic_->capabilityFlags ().test (CapabilityFlag::Preedit)) {
386- inputPanel.setClientPreedit (preeditText);
385+ inputPanel.setClientPreedit (mainPreedit);
386+ inputPanel.setPreedit (supplementPreedit);
387387 ic_->updatePreedit ();
388388 } else {
389- inputPanel.setPreedit (preeditText );
389+ inputPanel.setPreedit (mainPreedit );
390390 }
391391
392392 // StatusArea for status icon
@@ -432,6 +432,132 @@ int FcitxCskkContext::getInputMode() {
432432 }
433433 return skk_context_get_input_mode (context_);
434434}
435+ /* *
436+ * format preedit state into Text.
437+ * Returns tuple of <Main content Text, Supplement content Text>
438+ * Main content is something you always want to show, supplement content is
439+ * something you may show when you have space.
440+ */
441+ std::tuple<Text, Text>
442+ FcitxCskkContext::formatPreedit (CskkStateInfoFfi *cskkStateInfoArray,
443+ uint32_t stateLen) {
444+ std::string precomposition_marker = " ▽" ;
445+ std::string selection_marker = " ▼" ;
446+ Text mainContent = Text (" " ), supplementContent = Text (" " );
447+ size_t mainCursorIdx = 0 ;
448+ for (uint32_t i = 0 ; i < stateLen; i++) {
449+ auto cskkStateInfo = cskkStateInfoArray[i];
450+ switch (cskkStateInfo.tag ) {
451+ case DirectStateInfo: {
452+ auto directStateInfo = cskkStateInfo.direct_state_info ;
453+ if (directStateInfo.confirmed ) {
454+ mainCursorIdx += strlen (directStateInfo.confirmed );
455+ mainContent.append (directStateInfo.confirmed , TextFormatFlag::NoFlag);
456+ }
457+ if (directStateInfo.unconverted ) {
458+ mainCursorIdx += strlen (directStateInfo.unconverted );
459+ mainContent.append (directStateInfo.unconverted ,
460+ TextFormatFlag::Underline);
461+ }
462+ } break ;
463+ case PreCompositionStateInfo: {
464+ auto precompositionStateInfo = cskkStateInfo.pre_composition_state_info ;
465+ mainCursorIdx += precomposition_marker.length ();
466+ mainContent.append (precomposition_marker, TextFormatFlag::DontCommit);
467+ if (precompositionStateInfo.confirmed ) {
468+ mainCursorIdx += strlen (precompositionStateInfo.confirmed );
469+ mainContent.append (precompositionStateInfo.confirmed ,
470+ TextFormatFlag::NoFlag);
471+ }
472+ if (precompositionStateInfo.kana_to_composite ) {
473+ mainCursorIdx += strlen (precompositionStateInfo.kana_to_composite );
474+ mainContent.append (precompositionStateInfo.kana_to_composite ,
475+ TextFormatFlag::Underline);
476+ }
477+ if (precompositionStateInfo.unconverted ) {
478+ mainCursorIdx += strlen (precompositionStateInfo.unconverted );
479+ mainContent.append (precompositionStateInfo.unconverted ,
480+ TextFormatFlag::Underline);
481+ }
482+ } break ;
483+ case PreCompositionOkuriganaStateInfo: {
484+ auto precompositionOkuriganaStateInfo =
485+ cskkStateInfo.pre_composition_okurigana_state_info ;
486+ mainContent.append (precomposition_marker, TextFormatFlag::DontCommit);
487+ mainCursorIdx += precomposition_marker.length ();
488+ if (precompositionOkuriganaStateInfo.confirmed ) {
489+ mainCursorIdx += strlen (precompositionOkuriganaStateInfo.confirmed );
490+ mainContent.append (precompositionOkuriganaStateInfo.confirmed ,
491+ TextFormatFlag::NoFlag);
492+ }
493+ if (precompositionOkuriganaStateInfo.kana_to_composite ) {
494+ mainCursorIdx +=
495+ strlen (precompositionOkuriganaStateInfo.kana_to_composite );
496+ mainContent.append (precompositionOkuriganaStateInfo.kana_to_composite ,
497+ TextFormatFlag::Underline);
498+ }
499+ if (precompositionOkuriganaStateInfo.unconverted ) {
500+ mainContent.append (" *" , TextFormatFlags{TextFormatFlag::Underline,
501+ TextFormatFlag::DontCommit});
502+ mainCursorIdx +=
503+ strlen (precompositionOkuriganaStateInfo.unconverted ) + 1 ;
504+ mainContent.append (precompositionOkuriganaStateInfo.unconverted ,
505+ TextFormatFlag::Underline);
506+ }
507+ } break ;
508+ case CompositionSelectionStateInfo: {
509+ auto compositionSelectionStateInfo =
510+ cskkStateInfo.composition_selection_state_info ;
511+ mainContent.append (selection_marker, TextFormatFlag::DontCommit);
512+ mainCursorIdx += selection_marker.length ();
513+ if (compositionSelectionStateInfo.composited ) {
514+ mainCursorIdx += strlen (compositionSelectionStateInfo.composited );
515+ mainContent.append (compositionSelectionStateInfo.composited ,
516+ TextFormatFlag::Underline);
517+ }
518+ if (compositionSelectionStateInfo.okuri ) {
519+ mainCursorIdx += strlen (compositionSelectionStateInfo.okuri );
520+ mainContent.append (compositionSelectionStateInfo.okuri ,
521+ TextFormatFlag::Underline);
522+ }
523+ if (compositionSelectionStateInfo.annotation ) {
524+ supplementContent.clear ();
525+ supplementContent.append (compositionSelectionStateInfo.annotation ,
526+ TextFormatFlag::DontCommit);
527+ }
528+ } break ;
529+ case RegisterStateInfo: {
530+ auto registerStateInfo = cskkStateInfo.register_state_info ;
531+ mainContent.append (selection_marker, TextFormatFlag::DontCommit);
532+ mainCursorIdx += selection_marker.length ();
533+ if (registerStateInfo.confirmed ) {
534+ mainCursorIdx += strlen (registerStateInfo.confirmed );
535+ mainContent.append (registerStateInfo.confirmed ,
536+ TextFormatFlag::DontCommit);
537+ }
538+ if (registerStateInfo.kana_to_composite ) {
539+ mainCursorIdx += strlen (registerStateInfo.kana_to_composite );
540+ mainContent.append (registerStateInfo.kana_to_composite ,
541+ TextFormatFlag::DontCommit);
542+ }
543+ if (registerStateInfo.okuri ) {
544+ mainCursorIdx += strlen (registerStateInfo.okuri );
545+ mainContent.append (registerStateInfo.okuri , TextFormatFlag::DontCommit);
546+ }
547+ mainCursorIdx += strlen (" 【" );
548+ mainContent.append (" 【" , TextFormatFlag::DontCommit);
549+ } break ;
550+ }
551+ }
552+ // FIXME: Silently assuming length is less than int_max here. May fail when
553+ // length is over UINT_MAX. very unlikely, not high priority.
554+ mainContent.setCursor ((int )mainCursorIdx);
555+ for (uint32_t i = 1 ; i < stateLen; i++) {
556+ mainContent.append (" 】" , TextFormatFlag::DontCommit);
557+ }
558+
559+ return {mainContent, supplementContent};
560+ }
435561
436562/* ******************************************************************************
437563 * FcitxCskkFactory
0 commit comments