From c39d3c5d11e336b8ac3249f4f944de3f37ac00b4 Mon Sep 17 00:00:00 2001 From: wangqr Date: Sat, 3 Apr 2021 18:56:06 -0400 Subject: [PATCH] Allow using wxTextCtrl as subtitle edit box Fix wangqr/Aegisub#81 --- src/command/edit.cpp | 1 + src/subs_edit_box.cpp | 125 +++++++++++++++++++++++++----- src/subs_edit_box.h | 5 +- src/text_selection_controller.cpp | 91 ++++++++++++++++++---- src/text_selection_controller.h | 4 +- 5 files changed, 187 insertions(+), 39 deletions(-) diff --git a/src/command/edit.cpp b/src/command/edit.cpp index dce2772e96..20ba8670b1 100644 --- a/src/command/edit.cpp +++ b/src/command/edit.cpp @@ -1261,6 +1261,7 @@ struct edit_insert_original final : public Command { line->Text = line->Text.get().substr(0, sel_start) + c->initialLineState->GetInitialText() + line->Text.get().substr(sel_end); c->ass->Commit(_("insert original"), AssFile::COMMIT_DIAG_TEXT, -1, line); + c->textSelectionController->SetSelection(sel_start, sel_start + c->initialLineState->GetInitialText().length()); } }; diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp index 1e127cebe9..8ccbcc2a99 100644 --- a/src/subs_edit_box.cpp +++ b/src/subs_edit_box.cpp @@ -213,18 +213,41 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) main_sizer->Add(middle_right_sizer, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 3)); // Text editor - edit_ctrl_stc = new SubsTextEditCtrl(this, wxDefaultSize, wxBORDER_SUNKEN, c); - edit_ctrl_stc->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this); +#ifdef WITH_WXSTC + if (use_stc) { + edit_ctrl_stc = new SubsTextEditCtrl(this, wxDefaultSize, wxBORDER_SUNKEN, c); + edit_ctrl_stc->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this); + } + else { +#endif + edit_ctrl_tc = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN | wxTE_MULTILINE); + edit_ctrl_tc->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this); +#ifdef WITH_WXSTC + } +#endif secondary_editor = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN | wxTE_MULTILINE | wxTE_READONLY); - // Here we use the height of secondary_editor as the initial size of edit_ctrl_stc, - // which is more reasonable than the default given by wxWidgets. - // See: https://trac.wxwidgets.org/ticket/18471#ticket - // https://github.com/wangqr/Aegisub/issues/4 - edit_ctrl_stc->SetInitialSize(secondary_editor->GetSize()); +#ifdef WITH_WXSTC + if (use_stc) { + // Here we use the height of secondary_editor as the initial size of edit_ctrl_stc, + // which is more reasonable than the default given by wxWidgets. + // See: https://trac.wxwidgets.org/ticket/18471#ticket + // https://github.com/wangqr/Aegisub/issues/4 + edit_ctrl_stc->SetInitialSize(secondary_editor->GetSize()); + } +#endif main_sizer->Add(secondary_editor, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 3)); - main_sizer->Add(edit_ctrl_stc, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 3)); +#ifdef WITH_WXSTC + if (use_stc) { + main_sizer->Add(edit_ctrl_stc, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 3)); + } + else { +#endif + main_sizer->Add(edit_ctrl_tc, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 3)); +#ifdef WITH_WXSTC + } +#endif main_sizer->Hide(secondary_editor); bottom_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -237,8 +260,17 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) SetSizerAndFit(main_sizer); - edit_ctrl_stc->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChange, this); - edit_ctrl_stc->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION); +#ifdef WITH_WXSTC + if (use_stc) { + edit_ctrl_stc->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChangeStc, this); + edit_ctrl_stc->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION); + } + else { +#endif + edit_ctrl_tc->Bind(wxEVT_TEXT, &SubsEditBox::OnChangeTc, this); +#ifdef WITH_WXSTC + } +#endif Bind(wxEVT_TEXT, &SubsEditBox::OnLayerEnter, this, layer->GetId()); Bind(wxEVT_SPINCTRL, &SubsEditBox::OnLayerEnter, this, layer->GetId()); @@ -259,8 +291,18 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) context->initialLineState->AddChangeListener(&SubsEditBox::OnLineInitialTextChanged, this), }); - context->textSelectionController->SetControl(edit_ctrl_stc); - edit_ctrl_stc->SetFocus(); +#ifdef WITH_WXSTC + if (use_stc) { + context->textSelectionController->SetControl(edit_ctrl_stc); + edit_ctrl_stc->SetFocus(); + } + else { +#endif + context->textSelectionController->SetControl(edit_ctrl_tc); + edit_ctrl_tc->SetFocus(); +#ifdef WITH_WXSTC + } +#endif bool show_original = OPT_GET("Subtitle/Show Original")->GetBool(); if (show_original) { @@ -270,7 +312,7 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) } SubsEditBox::~SubsEditBox() { - c->textSelectionController->SetControl(nullptr); + c->textSelectionController->SetControl((wxTextCtrl*)nullptr); } wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) { @@ -384,7 +426,16 @@ void SubsEditBox::UpdateFields(int type, bool repopulate_lists) { } if (type & AssFile::COMMIT_DIAG_TEXT) { - edit_ctrl_stc->SetTextTo(line->Text); +#ifdef WITH_WXSTC + if (use_stc) { + edit_ctrl_stc->SetTextTo(line->Text); + } + else { +#endif + edit_ctrl_tc->SetValue(to_wx(line->Text)); +#ifdef WITH_WXSTC + } +#endif UpdateCharacterCount(line->Text); } @@ -470,7 +521,8 @@ void SubsEditBox::OnKeyDown(wxKeyEvent &event) { hotkey::check("Subtitle Edit Box", c, event); } -void SubsEditBox::OnChange(wxStyledTextEvent &event) { +#ifdef WITH_WXSTC +void SubsEditBox::OnChangeStc(wxStyledTextEvent &event) { if (line && edit_ctrl_stc->GetTextRaw().data() != line->Text.get()) { if (event.GetModificationType() & wxSTC_STARTACTION) commit_id = -1; @@ -478,6 +530,14 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) { UpdateCharacterCount(line->Text); } } +#endif + +void SubsEditBox::OnChangeTc(wxCommandEvent& event) { + if (line && edit_ctrl_tc->GetValue().utf8_str() != line->Text.get()) { + CommitText(_("modify text")); + UpdateCharacterCount(line->Text); + } +} void SubsEditBox::Commit(wxString const& desc, int type, bool amend, AssDialogue *line) { file_changed_slot.Block(); @@ -508,8 +568,17 @@ void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, wxString const& val } void SubsEditBox::CommitText(wxString const& desc) { - auto data = edit_ctrl_stc->GetTextRaw(); - SetSelectedRows(&AssDialogue::Text, boost::flyweight(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true); +#ifdef WITH_WXSTC + if (use_stc) { + auto data = edit_ctrl_stc->GetTextRaw(); + SetSelectedRows(&AssDialogue::Text, boost::flyweight(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true); + } + else { +#endif + SetSelectedRows(&AssDialogue::Text, boost::flyweight(edit_ctrl_tc->GetValue().utf8_str()), desc, AssFile::COMMIT_DIAG_TEXT, true); +#ifdef WITH_WXSTC + } +#endif } void SubsEditBox::CommitTimes(TimeField field) { @@ -608,7 +677,16 @@ void SubsEditBox::SetControlsState(bool state) { Enable(state); if (!state) { wxEventBlocker blocker(this); - edit_ctrl_stc->SetTextTo(""); +#ifdef WITH_WXSTC + if (use_stc) { + edit_ctrl_stc->SetTextTo(""); + } + else { +#endif + edit_ctrl_tc->Clear(); +#ifdef WITH_WXSTC + } +#endif } } @@ -659,7 +737,16 @@ void SubsEditBox::OnCommentChange(wxCommandEvent &evt) { void SubsEditBox::CallCommand(const char *cmd_name) { cmd::call(cmd_name, c); - edit_ctrl_stc->SetFocus(); +#ifdef WITH_WXSTC + if (use_stc) { + edit_ctrl_stc->SetFocus(); + } + else { +#endif + edit_ctrl_tc->SetFocus(); +#ifdef WITH_WXSTC + } +#endif } void SubsEditBox::UpdateCharacterCount(std::string const& text) { diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h index d9bcaa2734..cdc8eecfc7 100644 --- a/src/subs_edit_box.h +++ b/src/subs_edit_box.h @@ -137,7 +137,10 @@ class SubsEditBox final : public wxPanel { wxComboBox *MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip); wxRadioButton *MakeRadio(wxString const& text, bool start, wxString const& tooltip); - void OnChange(wxStyledTextEvent &event); +#ifdef WITH_WXSTC + void OnChangeStc(wxStyledTextEvent &event); +#endif + void OnChangeTc(wxCommandEvent& event); void OnKeyDown(wxKeyEvent &event); void OnActiveLineChanged(AssDialogue *new_line); diff --git a/src/text_selection_controller.cpp b/src/text_selection_controller.cpp index 54298f14c3..231da6a1c0 100644 --- a/src/text_selection_controller.cpp +++ b/src/text_selection_controller.cpp @@ -32,8 +32,8 @@ void TextSelectionController::SetControl(wxTextCtrl* ctrl) { this->ctrl_te = ctrl; this->ctrl_ctl = ctrl; if (ctrl) { - ctrl->Bind(wxEVT_KEY_DOWN, &TextSelectionController::UpdateUI, this); - ctrl->Bind(wxEVT_LEFT_DOWN, &TextSelectionController::UpdateUI, this); + ctrl->Bind(wxEVT_KEY_UP, &TextSelectionController::UpdateUI, this); + ctrl->Bind(wxEVT_LEFT_UP, &TextSelectionController::UpdateUI, this); } #ifdef WITH_WXSTC use_stc = false; @@ -56,22 +56,27 @@ TextSelectionController::~TextSelectionController() { #endif } -#define GET(var, new_value) do { \ - int tmp = new_value; \ - if (tmp != var) { \ - var = tmp; \ - changed = true; \ - } \ -} while(false) - void TextSelectionController::UpdateUI(wxEvent& evt) { + evt.Skip(); if (changing) return; bool changed = false; - GET(insertion_point, ctrl_te->GetInsertionPoint()); - long tmp_start, tmp_end; + long tmp_insertion, tmp_start, tmp_end; + tmp_insertion = ctrl_te->GetInsertionPoint(); ctrl_te->GetSelection(&tmp_start, &tmp_end); - if (tmp_start != selection_start || tmp_end != selection_end) { +#ifdef WITH_WXSTC + if (!use_stc) { +#endif + // GetSelection returned by wxTextCtrl is the index of Unicode codepoint position + // We need to convert it to UTF-8 location + tmp_insertion = ctrl_te->GetRange(0, tmp_insertion).utf8_str().length(); + tmp_start = ctrl_te->GetRange(0, tmp_start).utf8_str().length(); + tmp_end = ctrl_te->GetRange(0, tmp_end).utf8_str().length(); +#ifdef WITH_WXSTC + } +#endif + if (tmp_insertion != insertion_point || tmp_start != selection_start || tmp_end != selection_end) { + insertion_point = tmp_insertion; selection_start = tmp_start; selection_end = tmp_end; changed = true; @@ -79,22 +84,74 @@ void TextSelectionController::UpdateUI(wxEvent& evt) { if (changed) AnnounceSelectionChanged(); } -void TextSelectionController::SetInsertionPoint(int position) { +void TextSelectionController::SetInsertionPoint(long position) { changing = true; if (insertion_point != position) { insertion_point = position; - if (ctrl_te) ctrl_te->SetInsertionPoint(position); + if (ctrl_te) { + long tmp_position = 0; +#ifdef WITH_WXSTC + if (use_stc) { + tmp_position = position; + } + else { +#endif + // Convert UTF-8 position to wxTextEdit position + long last_position = ctrl_te->GetLastPosition(); + for (; tmp_position < last_position; ++tmp_position) { + if (ctrl_te->GetRange(0, tmp_position).utf8_str().length() >= position) { + break; + } + } +#ifdef WITH_WXSTC + } +#endif + ctrl_te->SetInsertionPoint(tmp_position); + } } changing = false; AnnounceSelectionChanged(); } -void TextSelectionController::SetSelection(int start, int end) { +void TextSelectionController::SetSelection(long start, long end) { changing = true; if (selection_start != start || selection_end != end) { selection_start = start; selection_end = end; - if (ctrl_te) ctrl_te->SetSelection(start, end); + if (ctrl_te) { + long tmp_start = -1, tmp_end = -1; +#ifdef WITH_WXSTC + if (use_stc) { + tmp_start = start; + tmp_end = end; + } + else { +#endif + // Convert UTF-8 position to wxTextEdit position + long last_position = ctrl_te->GetLastPosition(); + for (long pos = 0; pos < last_position; ++pos) { + size_t pos_utf8 = ctrl_te->GetRange(0, pos).utf8_str().length(); + if (tmp_start == -1 && pos_utf8 >= start) { + tmp_start = pos; + } + if (tmp_end == -1 && pos_utf8 >= end) { + tmp_end = pos; + } + if (tmp_start != -1 && tmp_end != -1) { + break; + } + } + if (tmp_start == -1) { + tmp_start = last_position; + } + if (tmp_end == -1) { + tmp_end = last_position; + } +#ifdef WITH_WXSTC + } +#endif + ctrl_te->SetSelection(tmp_start, tmp_end); + } } changing = false; AnnounceSelectionChanged(); diff --git a/src/text_selection_controller.h b/src/text_selection_controller.h index 0b5e1b8996..7cf0d8ac75 100644 --- a/src/text_selection_controller.h +++ b/src/text_selection_controller.h @@ -36,8 +36,8 @@ class TextSelectionController { agi::signal::Signal<> AnnounceSelectionChanged; public: - void SetSelection(int start, int end); - void SetInsertionPoint(int point); + void SetSelection(long start, long end); + void SetInsertionPoint(long point); long GetSelectionStart() const { return selection_start; } long GetSelectionEnd() const { return selection_end; }