ui: Added tabs UI to top of main window (issue #215)
* Added tab bar to top of main window with UI/shortcuts similar to web browsers * ComponentManager offers components for 2 use-cases: application and tab * Application components: log view (status/errors), bookmarks, screen search, status bar, tabs, tooltips, dialogs and all tab views disabled * Tab components: graph, code, history and search * When a project is loaded there is always at least one tab, otherwise there is none * Each tab replaces the application views of the same name in the main layout when activated * Each tab has it's own TaskScheduler, to process tasks independent from other tabs * The application has a global TaskScheduler for application wide tasks * The singloton class TaskManager is responsible for managing the TaskSchedulers * MessageListeners and Messages hold an optional schedulerId, both must be set to only send to a specific TaskScheduler * Bookmark buttons where split off into a separate view per tab, bookmark managemement is done in the application * History menu processing is done by the QtMainWindow now * Screen search is an application component, the responders are always changed to the active tab * Plugin messages are opened in a new tab * Symbols can be opened in a new tab via middle click/context menu in graph, code, history dropdown * Full text index creation is mutexed now in PersistenStorage to make it fully thread-safe * All tabs are closed when switching projects * Tab contents are only animated when visible fortune cookie message = Catch your lucky star!
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
#include "TabsController.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "MessageFind.h"
|
||||
#include "MessageIndexingFinished.h"
|
||||
#include "MessageSearch.h"
|
||||
#include "MessageScrollToLine.h"
|
||||
#include "ScreenSearchInterfaces.h"
|
||||
#include "TabId.h"
|
||||
#include "TaskLambda.h"
|
||||
#include "TaskManager.h"
|
||||
#include "TaskScheduler.h"
|
||||
|
||||
TabsController::TabsController(
|
||||
ViewLayout* mainLayout,
|
||||
const ViewFactory* viewFactory,
|
||||
StorageAccess* storageAccess,
|
||||
ScreenSearchSender* screenSearchSender
|
||||
)
|
||||
: m_mainLayout(mainLayout)
|
||||
, m_viewFactory(viewFactory)
|
||||
, m_storageAccess(storageAccess)
|
||||
, m_screenSearchSender(screenSearchSender)
|
||||
{
|
||||
}
|
||||
|
||||
void TabsController::clear()
|
||||
{
|
||||
getView()->clear();
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_tabsMutex);
|
||||
if (m_tabs.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
void TabsController::addTab(Id tabId, SearchMatch match)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_tabsMutex);
|
||||
|
||||
TaskManager::createScheduler(tabId)->startSchedulerLoopThreaded();
|
||||
|
||||
m_tabs.emplace(tabId, std::make_shared<Tab>(tabId, m_viewFactory, m_storageAccess, m_screenSearchSender));
|
||||
|
||||
if (match.isValid())
|
||||
{
|
||||
MessageSearch msg({ match }, NodeTypeSet::all());
|
||||
msg.setSchedulerId(tabId);
|
||||
msg.dispatch();
|
||||
|
||||
if (match.tokenIds.size() && std::get<0>(m_scrollToLine) == match.tokenIds[0])
|
||||
{
|
||||
MessageScrollToLine scrollMsg(std::get<1>(m_scrollToLine), std::get<2>(m_scrollToLine));
|
||||
scrollMsg.setSchedulerId(tabId);
|
||||
scrollMsg.dispatch();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageFind msg;
|
||||
msg.setSchedulerId(tabId);
|
||||
msg.dispatch();
|
||||
}
|
||||
|
||||
m_scrollToLine = std::make_tuple(0, FilePath(), 0);
|
||||
}
|
||||
|
||||
void TabsController::showTab(Id tabId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_tabsMutex);
|
||||
|
||||
auto it = m_tabs.find(tabId);
|
||||
if (it != m_tabs.end())
|
||||
{
|
||||
TabId::setCurrentTabId(tabId);
|
||||
it->second->setParentLayout(m_mainLayout);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabId::setCurrentTabId(0);
|
||||
m_mainLayout->showOriginalViews();
|
||||
}
|
||||
|
||||
Task::dispatch(
|
||||
TabId::app(),
|
||||
std::make_shared<TaskLambda>(
|
||||
[this]()
|
||||
{
|
||||
m_screenSearchSender->clearMatches();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void TabsController::removeTab(Id tabId)
|
||||
{
|
||||
// use app task scheduler thread to stop running tasks of tab
|
||||
Task::dispatch(
|
||||
TabId::background(),
|
||||
std::make_shared<TaskLambda>(
|
||||
[tabId, this]()
|
||||
{
|
||||
m_screenSearchSender->clearMatches();
|
||||
|
||||
TaskScheduler* scheduler = TaskManager::getScheduler(tabId).get();
|
||||
scheduler->terminateRunningTasks();
|
||||
scheduler->stopSchedulerLoop();
|
||||
|
||||
TaskManager::destroyScheduler(tabId);
|
||||
|
||||
getView()->destroyTab(tabId);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void TabsController::destroyTab(Id tabId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_tabsMutex);
|
||||
|
||||
// destroy the tab on the qt thread to allow view destruction
|
||||
m_tabs.erase(tabId);
|
||||
|
||||
if (m_tabs.empty() && Application::getInstance()->isProjectLoaded())
|
||||
{
|
||||
MessageTabOpen().dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void TabsController::onClearTabs()
|
||||
{
|
||||
TabId::setCurrentTabId(0);
|
||||
m_mainLayout->showOriginalViews();
|
||||
}
|
||||
|
||||
TabsView* TabsController::getView() const
|
||||
{
|
||||
return Controller::getView<TabsView>();
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageIndexingFinished* message)
|
||||
{
|
||||
if (m_tabs.empty() && Application::getInstance()->isProjectLoaded())
|
||||
{
|
||||
MessageTabOpenWith(SearchMatch::createCommand(SearchMatch::COMMAND_ALL)).dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageTabClose* message)
|
||||
{
|
||||
getView()->closeTab();
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageTabOpen* message)
|
||||
{
|
||||
if (Application::getInstance()->isProjectLoaded())
|
||||
{
|
||||
getView()->openTab(true, SearchMatch());
|
||||
}
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageTabOpenWith* message)
|
||||
{
|
||||
if (!Application::getInstance()->isProjectLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SearchMatch match = message->match;
|
||||
if (!match.isValid())
|
||||
{
|
||||
Id tokenId = message->tokenId;
|
||||
if (!tokenId && message->locationId)
|
||||
{
|
||||
std::vector<Id> tokenIds = m_storageAccess->getNodeIdsForLocationIds({ message->locationId });
|
||||
if (tokenIds.size())
|
||||
{
|
||||
tokenId = tokenIds[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokenId && !message->filePath.empty())
|
||||
{
|
||||
tokenId = m_storageAccess->getNodeIdForFileNode(message->filePath);
|
||||
|
||||
if (message->line)
|
||||
{
|
||||
m_scrollToLine = std::make_tuple(tokenId, message->filePath, message->line);
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenId)
|
||||
{
|
||||
std::vector<SearchMatch> matches = m_storageAccess->getSearchMatchesForTokenIds({ tokenId });
|
||||
if (matches.size())
|
||||
{
|
||||
match = matches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match.isValid())
|
||||
{
|
||||
getView()->openTab(message->showTab, match);
|
||||
}
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageTabSelect* message)
|
||||
{
|
||||
getView()->selectTab(message->next);
|
||||
}
|
||||
|
||||
void TabsController::handleMessage(MessageTabState* message)
|
||||
{
|
||||
getView()->updateTab(message->tabId, message->searchMatches);
|
||||
}
|
||||
Reference in New Issue
Block a user