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:
Eberhard Graether
2018-11-05 01:28:12 +01:00
parent bd4a554b27
commit f13aec70dd
170 changed files with 2972 additions and 823 deletions
@@ -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);
}