2017-02-15 2 views
2

Lorsque l'utilisateur appuie sur Entrez clé dans wxStyledTextCtrl, il semble que le curseur va toujours jusqu'au début de la ligne (zéro indentation), ce qui est très probablement le comportement attendu. Je veux être capable d'écrire du code de script avec le format suivant, avec des retraits de ligne. J'utilise le code C++ suivant pour pouvoir contrôler l'indentation à un niveau très basique.Comment contrôler LineIndentation dans wxStyledTextCtrl lorsque l'utilisateur appuie sur Entrée

void Script::OnKeyUp(wxKeyEvent& evt) 
{ 
    if ((evt.GetKeyCode() == WXK_RETURN || evt.GetKeyCode() == WXK_NUMPAD_ENTER)) { 
     long int col, line; 
     PositionToXY(GetInsertionPoint(), &col, &line); 
     int PreviousIndentation = GetLineIndentation(line-1); 
     SetLineIndentation(line, PreviousIndentation); 
     GotoPos(GetCurrentPos() + PreviousIndentation); 
    } 
} 

Le code ci-dessus C++ préserve le niveau d'indentation, cependant, le curseur passe d'abord au début de la ligne, puis au niveau de retrait. Lorsque vous utilisez d'autres IDE, cela ne se passe pas de la sorte, comme aller au début de la ligne, puis au niveau de l'indentation. Plutôt, le curseur immédiatement va à/suit le niveau d'indentation. Y at-il un moyen que le curseur peut immédiatement aller au niveau d'indentation sans initialement aller au niveau d'indentation zéro.

D'ailleurs, j'ai essayé EVT_STC_CHARADDED, ce qui semble être la voie mis en œuvre ZeroBraneStudio, mais lorsque la touche Entrée est pressée evt.GetKeyCode() retourne un entier bizarre et evt.GetUnicodeKey retours \0 et de plus EVT_STC_CHARADDED événement est appelé deux fois (je suppose en raison de CR + LF).

Soit dit en passant, j'utilise wxWidgets-3.1.0 sur Windows 10.

Toutes les idées seraient appréciées.

Répondre

1

Nous devons intercepter un événement et ajouter une copie de l'indentation de la ligne précédente à la nouvelle ligne. La première question est de savoir quel événement utiliser. Lorsque la touche entrée est enfoncée, les événements suivants sont tirés:

  • wxEVT_CHAR_HOOK
  • wxEVT_KEY_DOWN
  • wxEVT_STC_MODIFIED - ModificationType: 0x00100000
  • wxEVT_STC_MODIFIED - ModificationType: 0x00000410
  • wxEVT_STC_MODIFIED - ModificationType: 0x00002011
  • wxEVT_STC_CHARADDED
  • wxEVT_STC_UPDATEUI
  • wxEVT_STC_PAINTED
  • wxEVT_KEY_UP

Avec le char_hook et les événements key_down, la clé n'a pas été envoyé au contrôle encore, donc il ne sera pas en mesure de donner les informations de position nécessaires. Le contrôle ne doit pas être modifié dans l'événement stc_modified, nous ne devrions donc pas utiliser ces événements. Au moment de l'événement stc_painted, le curseur a déjà été dessiné, donc il et l'événement key_up sont trop tard. Et j'ai appris dans l'autre réponse que l'événement stc_updateui ne fonctionnera pas. Donc, par processus d'élimination, la seule possibilité est l'événement wxEVT_STC_CHARADDED. La question suivante est ce qu'il faut faire dans ce gestionnaire d'événements. J'ai adapté le code de here pour travailler avec wxStyledTextCtrl.

void Script::OnCharAdded(wxStyledTextEvent& event) 
{ 
    int new_line_key=(GetEOLMode()==wxSTC_EOL_CR)?13:10; 

    if (event.GetKey() == new_line_key) 
    { 
     int cur_pos = GetCurrentPos(); 
     int cur_line = LineFromPosition(cur_pos); 

     if (cur_line > 0) 
     { 
      wxString prev_line = GetLine(cur_line-1); 
      size_t prev_line_indent_chars(0); 
      for (size_t i=0; i<prev_line.Length(); ++i) 
      { 
       wxUniChar cur_char=prev_line.GetChar(i); 

       if (cur_char==' ') 
       { 
        ++prev_line_indent_chars; 
       } 
       else if (cur_char=='\t') 
       { 
        ++prev_line_indent_chars; 
       } 
       else 
       { 
        break; 
       } 
      } 

      AddText(prev_line.Left(prev_line_indent_chars)); 
     } 
    } 
} 

Cela pourrait être mieux car il compte physiquement les espaces et les onglets.

+0

Merci beaucoup! Hier, j'ai passé quelques heures à régler le problème de saut de ligne, mais en vain. Maintenant, cela fonctionne comment cela devrait fonctionner. – macroland

+0

Désolé pour l'autre réponse. Une fois que j'ai réalisé le problème, j'ai passé environ 3 heures à essayer de le contourner avant de réaliser que c'était probablement sans espoir. –

+0

Avec vos améliorations récentes sur la réponse, c'est maintenant en effet une très bonne! – macroland

2

Remarque: Les commentaires ci-dessous soulignent une erreur fatale dans le code de cette réponse. Ajuster la position du curseur dans un gestionnaire d'événements UpdateUI comme j'ai essayé de le faire ici est une mauvaise idée. J'ai posté une autre réponse qui, je l'espère, fonctionne mieux. Je ne peux pas garantir que c'est la meilleure façon, mais voici un moyen. Tout d'abord, cela nécessite d'ajouter un membre entier à votre classe de script pour servir d'indicateur indiquant que l'indentation doit être ajoutée. Dans la suite, je l'ai appelé 'm_indentToAdd'.

Pour détecter qu'une ligne a été ajoutée, vous pouvez utiliser l'événement wxEVT_STC_MODIFIED. Si le type de modification indique qu'il s'agit d'une action de l'utilisateur, du texte a été inséré et qu'une ligne a été ajoutée, la ligne suivante doit être indentée. En plus de la touche Entrée appuyée, elle sera interceptée lorsqu'une seule ligne incluant les fins de ligne a été collée.

void Script::OnModified(wxStyledTextEvent& event) 
{ 
    int mt = event.GetModificationType(); 

    if(mt&wxSTC_MOD_INSERTTEXT && mt&wxSTC_PERFORMED_USER && event.GetLinesAdded()==1) 
    { 
     int cur_line = m_stc->LineFromPosition(event.GetPosition()); 
     int cur_indent = m_stc->GetLineIndentation(cur_line); 
     m_indentToAdd=cur_indent; 
    } 
} 

Pour éviter d'avoir le début du curseur au début de la ligne, puis passer à l'indentation, vous pouvez gérer l'événement wxEVT_STC_UPDATEUI et rétablir la position là:

void Script::OnUpdateUI(wxStyledTextEvent& event) 
{ 
    if(m_indentToAdd) 
    { 
     int cur_pos = m_stc->GetCurrentPos(); 
     int cur_line = m_stc->LineFromPosition(cur_pos); 
     m_stc->SetLineIndentation(cur_line, m_indentToAdd); 
     m_stc->GotoPos(cur_pos+m_indentToAdd); 

     m_indentToAdd=0; 
    } 
} 

L'événement UpdateUI n » t fournir la position ou la ligne actuelle, de sorte qu'ils doivent être recalculés avant que l'indentation ne puisse être définie. Je suppose que cela pourrait être optimisé en stockant ces valeurs dans le gestionnaire d'événements OnModified et en les utilisant ensuite dans le gestionnaire d'événements UpdateUI.

+0

Merci pour votre bien et au point réponse! Cela fonctionne bien sauf que s'il y a une ligne vide en dessous de la ligne que l'utilisateur appuie sur Entrée, par exemple l'utilisateur est à la 5ème ligne et la 6ème ligne est vide (disons qu'il y a 6ème ligne dans l'éditeur), alors l'indentation plutôt que 6ème. S'il n'y avait pas de 6ème ligne, appuyer sur Entrée fait ce qu'il devrait faire. Je devrais trouver un moyen de régler ça. Juste une question: je vois que vous utilisez 'LineFromIndentation' plutôt que' GetLineIndentation'; Y a-t-il une raison spécifique à cela? – macroland

+0

La solution que j'ai donnée a eu très peu de tests, mais je ne vois pas de problème à cause des lignes vides suivantes. Pour moi, il semble fonctionner dans tous les cas suivants: aucune ligne suivante, ligne suivante vide, et la ligne suivante est non vide mais avec un niveau d'indentation différent. Si vous ne trouvez pas de solution, pouvez-vous donner plus de détails? –

+0

Aussi pour votre dernière question, demandiez-vous pourquoi j'utilise LineFromPosition au lieu de PositionToXY? Il n'y a pas de vraie raison. PositionToXY est implémenté en utilisant LineFromPosition, donc ils font tous les deux la même chose. –