diff --git a/src/findbar.cpp b/src/findbar.cpp index 26ab6e8..9a09a7e 100644 --- a/src/findbar.cpp +++ b/src/findbar.cpp @@ -59,8 +59,8 @@ FindBar::FindBar(Konsole::TerminalView* terminal) setName("findBar"); setVisible(false); + m_field->setName("findField"); m_field->setFrameVisible(false); - m_field->setPlaceholder(tr("Find")); m_field->setTextMargins(0, 0); m_previous->setGlyph("chevron-up", 16); m_next->setGlyph("chevron-down", 16); @@ -76,6 +76,7 @@ FindBar::FindBar(Konsole::TerminalView* terminal) m_notFound = !found; updateColors(); }); + retranslate(); updateColors(); } @@ -126,6 +127,11 @@ void FindBar::close() onClosed(); } +void FindBar::retranslate() +{ + m_field->setPlaceholder(tr("Find")); +} + void FindBar::search(bool forwards, bool fromSelection) { const std::string text = m_field->text(); diff --git a/src/findbar.h b/src/findbar.h index 7ab6f3e..5ccbe62 100644 --- a/src/findbar.h +++ b/src/findbar.h @@ -34,6 +34,7 @@ public: void open(const std::string& text); void close(); + void retranslate(); std::function onClosed; diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 4ad63c4..7ce3121 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -269,6 +269,7 @@ public: : m_window(window) , preview(addChild(window)) { + setName("settingsContent"); TerminalSettings& settings = window->settings(); Card* appearance = addCard(); @@ -454,6 +455,15 @@ void SettingsDialog::show() m_dialog->window()->activate(); } +void SettingsDialog::retranslate() +{ + if (!m_dialog || !m_content) + return; + m_dialog->setTitle(tr("Settings")); + m_content = m_dialog->setContent(m_window); + update(); +} + void SettingsDialog::close() { if (m_dialog) diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 876bf33..bc98556 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -29,6 +29,8 @@ public: void show(); void close(); + // An open dialog takes the new language in place, keeping its window. + void retranslate(); private: void update(); diff --git a/src/terminalpage.cpp b/src/terminalpage.cpp index 0a75ab3..f49d6d6 100644 --- a/src/terminalpage.cpp +++ b/src/terminalpage.cpp @@ -126,6 +126,15 @@ void TerminalPage::applySettings() m_window->schemeColor(1, accent::red), m_window->schemeColor(4, Theme::current().highlight)); } +void TerminalPage::retranslate() +{ + if (m_menu) + m_menu->close(); + m_menu.reset(); + if (m_findBar) + m_findBar->retranslate(); +} + void TerminalPage::startShell() { m_session->startShellProgram(); diff --git a/src/terminalpage.h b/src/terminalpage.h index 3503bc5..1225aac 100644 --- a/src/terminalpage.h +++ b/src/terminalpage.h @@ -47,6 +47,9 @@ public: // The settings and the window's zoom and colours, applied again. void applySettings(); + // The menu closes and the find bar takes the new language, keeping its + // search; the shell and its output stay as they are. + void retranslate(); void startShell(); void openFind(); diff --git a/src/terminalwindow.cpp b/src/terminalwindow.cpp index 573f90e..bc717ba 100644 --- a/src/terminalwindow.cpp +++ b/src/terminalwindow.cpp @@ -10,6 +10,7 @@ #include "base/i18n.h" #include "base/strings.h" #include "ui/animation.h" +#include "ui/application.h" #include "ui/dialog_window.h" #include "ui/drag.h" #include "ui/header_button.h" @@ -345,6 +346,7 @@ TerminalWindow::TerminalWindow(Windows& windows, TerminalSettings& settings, Opt m_window->onStatesChanged = [this] { applySettings(); }; m_connections.push_back(m_settings.changed.connect([this] { applySettings(); })); + m_connections.push_back(Application::instance()->languageChanged.connect([this] { retranslate(); })); applySettings(); if (options.detachedPage) { @@ -417,6 +419,32 @@ void TerminalWindow::applySettings() updateTitle(); } +// The shells, their output and the tabs stay; menus close, and the dialogs are +// made again, an open one shown again with what it was asking about. +void TerminalWindow::retranslate() +{ + if (m_tabMenu) + m_tabMenu->close(); + m_tabMenu.reset(); + for (TerminalPage* page : m_pages) + page->retranslate(); + if (m_settingsDialog) + m_settingsDialog->retranslate(); + + const bool exitShown = m_exitPrompt && m_exitPrompt->window(); + const bool linkShown = m_linkConfirm && m_linkConfirm->window(); + const bool pasteShown = m_pasteConfirm && m_pasteConfirm->window(); + m_exitPrompt.reset(); + m_linkConfirm.reset(); + m_pasteConfirm.reset(); + if (exitShown) + showExitPrompt(); + if (linkShown) + showLinkConfirm(); + if (pasteShown) + showPasteConfirm(); +} + void TerminalWindow::updateBackground() { m_window->setBackgroundColor(m_terminalBackground.withAlpha(m_settings.opacity())); @@ -637,6 +665,12 @@ void TerminalWindow::confirmClose(const std::vector& pages, std:: return; } + m_pendingAction = std::move(action); + showExitPrompt(); +} + +void TerminalWindow::showExitPrompt() +{ if (!m_exitPrompt) { m_exitPrompt = std::make_unique(m_window.get(), tr("Process is running, are you sure you want to quit?")); m_exitPrompt->setCancelText(tr("Cancel")); @@ -648,7 +682,6 @@ void TerminalWindow::confirmClose(const std::vector& pages, std:: }; m_exitPrompt->onRejected = [this] { m_pendingAction = nullptr; }; } - m_pendingAction = std::move(action); m_exitPrompt->show(); } @@ -785,14 +818,20 @@ void TerminalWindow::openLink(const std::string& url) return; } + m_pendingLink = url; + showLinkConfirm(); +} + +void TerminalWindow::showLinkConfirm() +{ if (!m_linkConfirm) { m_linkConfirm = std::make_unique(m_window.get(), tr("Open this link?")); m_linkConfirm->setConfirmText(tr("Open")); m_linkConfirm->onAccepted = [this] { ProcessHelper::openUrl(m_pendingLink); }; } - m_pendingLink = url; - const std::u16string utf16 = strings::utf8ToUtf16(url); - m_linkConfirm->setText(utf16.size() > 300 ? strings::utf16ToUtf8(utf16.substr(0, 299)) + "\xe2\x80\xa6" : url); + const std::u16string utf16 = strings::utf8ToUtf16(m_pendingLink); + m_linkConfirm->setText(utf16.size() > 300 ? strings::utf16ToUtf8(utf16.substr(0, 299)) + "\xe2\x80\xa6" + : m_pendingLink); m_linkConfirm->show(); } @@ -812,6 +851,14 @@ void TerminalWindow::confirmPaste(const std::string& text, std::function if (lines.size() > 4) shown.push_back("\xe2\x80\xa6"); + m_pasteLineCount = lines.size(); + m_pastePreview = strings::join(shown, "\n"); + m_pendingPaste = std::move(action); + showPasteConfirm(); +} + +void TerminalWindow::showPasteConfirm() +{ if (!m_pasteConfirm) { m_pasteConfirm = std::make_unique(m_window.get(), std::string()); m_pasteConfirm->setConfirmText(tr("Paste")); @@ -822,10 +869,8 @@ void TerminalWindow::confirmPaste(const std::string& text, std::function m_pasteConfirm->onRejected = [this] { m_pendingPaste = nullptr; }; } - const size_t count = lines.size(); - m_pasteConfirm->setTitle(count > 1 ? trn("Paste {n} line?", "Paste {n} lines?", count) - : std::string(tr("Paste this line and press Enter?"))); - m_pasteConfirm->setText(strings::join(shown, "\n")); - m_pendingPaste = std::move(action); + m_pasteConfirm->setTitle(m_pasteLineCount > 1 ? trn("Paste {n} line?", "Paste {n} lines?", m_pasteLineCount) + : std::string(tr("Paste this line and press Enter?"))); + m_pasteConfirm->setText(m_pastePreview); m_pasteConfirm->show(); } diff --git a/src/terminalwindow.h b/src/terminalwindow.h index 49b3527..cd14952 100644 --- a/src/terminalwindow.h +++ b/src/terminalwindow.h @@ -100,9 +100,13 @@ private: class Overlay; void applySettings(); + void retranslate(); void updateBackground(); void updateTitle(); void confirmClose(const std::vector& pages, std::function action); + void showExitPrompt(); + void showLinkConfirm(); + void showPasteConfirm(); void closeProtection(int index); void detachTab(int index, cutefish::ui::PointF position); void openTabMenu(int index, cutefish::ui::PointF position); @@ -137,6 +141,8 @@ private: std::function m_pendingAction; std::function m_pendingPaste; std::string m_pendingLink; + size_t m_pasteLineCount = 0; + std::string m_pastePreview; // Set once the close has been confirmed, so closing does not ask again. bool m_quitting = false; diff --git a/tests/window_test.cpp b/tests/window_test.cpp index b567139..537fc39 100644 --- a/tests/window_test.cpp +++ b/tests/window_test.cpp @@ -16,6 +16,7 @@ #include "ui/keys.h" #include "ui/painter.h" #include "ui/test_input.h" +#include "ui/text_field.h" #include "ui/view.h" #include "ui/window.h" @@ -155,6 +156,61 @@ TEST_F(WindowTest, CloseOtherTabsAfterTheKeptTabEnded) EXPECT_TRUE(busy->isVisible()); } +static ui::View* findView(ui::View* view, const std::string& name) +{ + if (!view || view->name() == name) + return view; + for (const auto& child : view->children()) { + if (ui::View* found = findView(child.get(), name)) + return found; + } + return nullptr; +} + +TEST_F(WindowTest, LanguageChangeKeepsTheShellsAndTheSearch) +{ + Windows windows(m_settings); + TerminalWindow* window = windows.create(shell()); + TerminalPage* busy = window->currentPage(); + window->openTab(m_directory.path(), {"/bin/sh"}); + ASSERT_EQ(window->tabCount(), 2); + TerminalPage* page = window->currentPage(); + Konsole::TerminalSession* session = &page->session(); + Konsole::TerminalView* terminal = page->terminal(); + page->setCustomTitle("Build"); + page->openFind(); + auto* field = static_cast(findView(page, "findField")); + ASSERT_NE(field, nullptr); + field->setText("needle"); + + window->showSettings(); + ui::Window* settings = ui::Application::instance()->windows().back(); + ASSERT_NE(settings, &window->window()); + const ui::View* settingsContent = findView(settings->contentView(), "settingsContent"); + ASSERT_NE(settingsContent, nullptr); + + busy->session().sendText("sleep 30\n"); + ASSERT_TRUE(waitFor([&] { return busy->session().hasActiveProcess(); })); + window->closeOtherTabs(1); + + ui::Application::instance()->languageChanged.emit(); + ASSERT_EQ(window->tabCount(), 2); + EXPECT_EQ(window->currentPage(), page); + EXPECT_EQ(&page->session(), session); + EXPECT_EQ(page->terminal(), terminal); + EXPECT_EQ(page->title(), "Build"); + EXPECT_TRUE(busy->session().hasActiveProcess()); + EXPECT_EQ(static_cast(findView(page, "findField"))->text(), "needle"); + const ui::View* rebuilt = findView(settings->contentView(), "settingsContent"); + ASSERT_NE(rebuilt, nullptr); + EXPECT_NE(rebuilt, settingsContent); + + // The close question is asked again and still closes the other tab. + confirmLastDialog(window); + ASSERT_EQ(window->tabCount(), 1); + EXPECT_EQ(window->currentPage(), page); +} + TEST_F(WindowTest, CursorBlinksOnlyWithTheKeyboard) { Windows windows(m_settings);