ui: Added history of last active symbols to UI and menu (issue 151)

* holding down back or forward button shows list of current back-forward stack
* added narrow button between back and forward to show the list
* added History menu
* moved back and forward actions to History menu
* show chronological list of recently active symbols in History menu

bug id = 151

fortune cookie message = Dein Geschäft wird gut laufen und expandieren.
This commit is contained in:
Eberhard Graether
2017-05-09 17:14:43 +02:00
parent bb8c2ae5a9
commit f4caacacf2
30 changed files with 668 additions and 65 deletions
+5
View File
@@ -158,6 +158,11 @@ bool Application::isInTrial() const
return m_isInTrial;
}
void Application::updateHistory(const std::vector<SearchMatch>& history)
{
m_mainView->updateHistoryMenu(history);
}
void Application::createAndLoadProject(const FilePath& projectSettingsFilePath)
{
MessageStatus("Loading Project: " + projectSettingsFilePath.str(), false, true).dispatch();
+2
View File
@@ -53,6 +53,8 @@ public:
bool isInTrial() const;
void updateHistory(const std::vector<SearchMatch>& history);
private:
static std::shared_ptr<Application> s_instance;
static std::string s_uuid;
+1
View File
@@ -397,6 +397,7 @@ add_files(
utility/messaging/type/MessageStatus.h
utility/messaging/type/MessageStatusFilterChanged.h
utility/messaging/type/MessageSwitchColorScheme.h
utility/messaging/type/MessageToUndoRedoPosition.h
utility/messaging/type/MessageUndo.h
utility/messaging/type/MessageWindowClosed.h
utility/messaging/type/MessageWindowFocus.h
@@ -56,6 +56,7 @@ void ActivationController::handleMessage(MessageActivateFile* message)
MessageActivateTokens m(message);
m.tokenIds.push_back(fileId);
m.tokenNames.push_back(message->filePath.str());
m.searchMatches = m_storageAccess->getSearchMatchesForTokenIds({ fileId });
m.dispatchImmediately();
}
else
@@ -87,6 +88,7 @@ void ActivationController::handleMessage(MessageActivateNodes* message)
}
m.tokenNames.push_back(node.nameHierarchy);
}
m.searchMatches = m_storageAccess->getSearchMatchesForTokenIds(m.tokenIds);
m.dispatchImmediately();
}
@@ -128,7 +130,7 @@ void ActivationController::handleMessage(MessageSearch* message)
m.tokenIds = message->getTokenIdsOfMatches();
m.searchMatches = matches;
m.tokenNames = m_storageAccess->getNameHierarchiesForNodeIds(m.tokenIds);
m.isFromSearch = true;
m.isFromSearch = message->isFromSearch;
m.dispatchImmediately();
}
@@ -5,6 +5,7 @@
#include "utility/messaging/type/MessageSearch.h"
#include "utility/utility.h"
#include "Application.h"
#include "component/view/UndoRedoView.h"
#include "data/access/StorageAccess.h"
@@ -28,6 +29,9 @@ void UndoRedoController::clear()
m_list.clear();
m_iterator = m_list.begin();
m_history.clear();
m_historyOffset = 0;
getView()->setUndoButtonEnabled(false);
getView()->setRedoButtonEnabled(false);
}
@@ -199,6 +203,8 @@ void UndoRedoController::handleMessage(MessageRedo* message)
}
replayCommands(oldIterator);
updateHistory();
}
void UndoRedoController::handleMessage(MessageRefresh* message)
@@ -288,6 +294,54 @@ void UndoRedoController::handleMessage(MessageShowScope* message)
processCommand(command);
}
void UndoRedoController::handleMessage(MessageToUndoRedoPosition* message)
{
size_t index = 0;
const size_t activeIndex = message->index + m_historyOffset;
for (std::list<Command>::reverse_iterator rit = m_list.rbegin(); rit != m_list.rend(); rit++)
{
if (rit->order == Command::ORDER_ACTIVATE)
{
if (index == activeIndex)
{
std::list<Command>::iterator it = --(rit.base());
std::list<Command>::iterator start = it;
do
{
std::advance(it, 1);
}
while (it != m_list.end() && it->order != Command::ORDER_ACTIVATE);
m_iterator = it;
if (start != m_iterator)
{
replayCommands(start);
}
else
{
replayCommand(m_iterator);
MessageFlushUpdates(false).dispatch();
}
}
index++;
if (index > activeIndex + 1)
{
break;
}
}
}
getView()->setUndoButtonEnabled(index > activeIndex + 1);
getView()->setRedoButtonEnabled(m_iterator != m_list.end() && m_iterator->order == Command::ORDER_ACTIVATE);
updateHistory();
}
void UndoRedoController::handleMessage(MessageUndo* message)
{
if (!m_list.size())
@@ -325,6 +379,8 @@ void UndoRedoController::handleMessage(MessageUndo* message)
m_iterator = it;
replayCommands();
updateHistory();
}
void UndoRedoController::replayCommands()
@@ -410,7 +466,7 @@ void UndoRedoController::replayCommand(std::list<Command>::iterator it)
if (!msg->isEdge && !msg->isAggregation)
{
msg->tokenIds = m_storageAccess->getNodeIdsForNameHierarchies(msg->tokenNames);
msg->searchMatches.clear();
msg->searchMatches = m_storageAccess->getSearchMatchesForTokenIds(msg->tokenIds);
}
}
@@ -468,6 +524,11 @@ void UndoRedoController::processCommand(Command command)
getView()->setRedoButtonEnabled(false);
}
}
if (command.order == Command::ORDER_ACTIVATE)
{
updateHistory();
}
}
bool UndoRedoController::sameMessageTypeAsLast(MessageBase* message) const
@@ -485,6 +546,101 @@ MessageBase* UndoRedoController::lastMessage() const
return std::prev(m_iterator)->message.get();
}
void UndoRedoController::updateHistory()
{
const size_t historySize = 20;
std::vector<SearchMatch> matches;
bool firstActiveMessage = false;
size_t index = 0;
int currentIndex = -1;
m_historyOffset = 0;
for (std::list<Command>::const_reverse_iterator it = m_list.rbegin(); it != m_list.rend(); it++)
{
if (m_iterator == it.base())
{
currentIndex = index;
}
if (it->order == Command::ORDER_ACTIVATE)
{
index++;
SearchMatch match = getSearchMatchForMessage(it->message.get());
if (!firstActiveMessage)
{
firstActiveMessage = true;
for (size_t i = 0; i < m_history.size(); i++)
{
if (m_history[i] == match)
{
m_history.erase(m_history.begin() + i);
break;
}
}
m_history.push_front(match);
if (m_history.size() > historySize)
{
m_history.pop_back();
}
}
if (match.text.size())
{
matches.push_back(match);
if (matches.size() > historySize)
{
matches.erase(matches.begin());
m_historyOffset++;
}
if (matches.size() == historySize && currentIndex != -1 && currentIndex - m_historyOffset != historySize - 1)
{
break;
}
}
}
}
getView()->updateHistory(matches, currentIndex - m_historyOffset);
Application::getInstance()->updateHistory(std::vector<SearchMatch>(m_history.begin(), m_history.end()));
}
SearchMatch UndoRedoController::getSearchMatchForMessage(MessageBase* message) const
{
if (message->getType() == MessageActivateAll::getStaticType())
{
return SearchMatch::createCommand(SearchMatch::COMMAND_ALL);
}
else if (message->getType() == MessageActivateTokens::getStaticType())
{
MessageActivateTokens* msg = dynamic_cast<MessageActivateTokens*>(message);
if (msg->searchMatches.size())
{
return msg->searchMatches.front();
}
}
else if (message->getType() == MessageSearchFullText::getStaticType())
{
MessageSearchFullText* msg = dynamic_cast<MessageSearchFullText*>(message);
std::string prefix(msg->caseSensitive ? 2 : 1, SearchMatch::FULLTEXT_SEARCH_CHARACTER);
SearchMatch match(prefix + msg->searchTerm);
match.searchType = SearchMatch::SEARCH_FULLTEXT;
return match;
}
else if (message->getType() == MessageShowErrors::getStaticType())
{
return SearchMatch::createCommand(SearchMatch::COMMAND_ERROR);
}
return SearchMatch();
}
void UndoRedoController::dump() const
{
std::cout << "\nUndo Redo Stack:\n----------\n";
@@ -24,6 +24,7 @@
#include "utility/messaging/type/MessageShowErrors.h"
#include "utility/messaging/type/MessageShowReference.h"
#include "utility/messaging/type/MessageShowScope.h"
#include "utility/messaging/type/MessageToUndoRedoPosition.h"
#include "utility/messaging/type/MessageUndo.h"
#include "component/controller/Controller.h"
@@ -52,6 +53,7 @@ class UndoRedoController
, public MessageListener<MessageShowErrors>
, public MessageListener<MessageShowReference>
, public MessageListener<MessageShowScope>
, public MessageListener<MessageToUndoRedoPosition>
, public MessageListener<MessageUndo>
{
public:
@@ -98,6 +100,7 @@ private:
virtual void handleMessage(MessageShowErrors* message);
virtual void handleMessage(MessageShowReference* message);
virtual void handleMessage(MessageShowScope* message);
virtual void handleMessage(MessageToUndoRedoPosition* message);
virtual void handleMessage(MessageUndo* message);
void replayCommands();
@@ -109,12 +112,18 @@ private:
bool sameMessageTypeAsLast(MessageBase* message) const;
MessageBase* lastMessage() const;
void updateHistory();
SearchMatch getSearchMatchForMessage(MessageBase* message) const;
void dump() const;
StorageAccess* m_storageAccess;
std::list<Command> m_list;
std::list<Command>::iterator m_iterator;
std::deque<SearchMatch> m_history;
size_t m_historyOffset;
};
#endif // UNDO_REDO_CONTROLLER_H
+4
View File
@@ -6,6 +6,8 @@
#include "component/view/ViewLayout.h"
struct SearchMatch;
class MainView
: public ViewLayout
{
@@ -19,7 +21,9 @@ public:
virtual void hideStartScreen() = 0;
virtual void setTitle(const std::string& title) = 0;
virtual void activateWindow() = 0;
virtual void updateRecentProjectMenu() = 0;
virtual void updateHistoryMenu(const std::vector<SearchMatch>& history) = 0;
};
#endif // MAIN_VIEW_H
+4
View File
@@ -4,6 +4,7 @@
#include "component/view/View.h"
class UndoRedoController;
struct SearchMatch;
class UndoRedoView : public View
{
@@ -15,6 +16,9 @@ public:
virtual void setRedoButtonEnabled(bool enabled) = 0;
virtual void setUndoButtonEnabled(bool enabled) = 0;
virtual void updateHistory(const std::vector<SearchMatch>& searchMatches, size_t currentIndex) = 0;
protected:
UndoRedoController* getController();
};
+5
View File
@@ -170,6 +170,11 @@ bool SearchMatch::operator<(const SearchMatch& other) const
return false;
}
bool SearchMatch::operator==(const SearchMatch& other) const
{
return text == other.text && searchType == other.searchType;
}
size_t SearchMatch::getTextSizeForSorting(const std::string* str) const
{
// check if templated symbol and only use size up to template stuff
+1
View File
@@ -42,6 +42,7 @@ struct SearchMatch
SearchMatch(const std::string& query);
bool operator<(const SearchMatch& other) const;
bool operator==(const SearchMatch& other) const;
size_t getTextSizeForSorting(const std::string* str) const;
@@ -11,7 +11,8 @@ class MessageSearch
{
public:
MessageSearch(const std::vector<SearchMatch>& matches)
: m_matches(matches)
: isFromSearch(true)
, m_matches(matches)
{
}
@@ -70,6 +71,8 @@ public:
os << getMatchesAsString();
}
bool isFromSearch;
private:
const std::vector<SearchMatch> m_matches;
};
@@ -0,0 +1,24 @@
#ifndef MESSAGE_TO_UNDO_REDO_POSITION_H
#define MESSAGE_TO_UNDO_REDO_POSITION_H
#include "utility/messaging/Message.h"
#include "utility/types.h"
class MessageToUndoRedoPosition
: public Message<MessageToUndoRedoPosition>
{
public:
MessageToUndoRedoPosition(size_t index)
: index(index)
{
}
static const std::string getStaticType()
{
return "MessageToUndoRedoPosition";
}
size_t index;
};
#endif // MESSAGE_TO_UNDO_REDO_POSITION_H
+18
View File
@@ -243,6 +243,24 @@ namespace utility
return (wsback <= wsfront ? std::string() : std::string(wsfront, wsback));
}
std::string elide(const std::string& str, ElideMode mode, size_t size)
{
if (str.size() <= size || str.size() <= 3)
{
return str;
}
switch (mode)
{
case ELIDE_LEFT:
return "..." + str.substr(str.size() - size - 3, str.size());
case ELIDE_MIDDLE:
return str.substr(0, size / 2 - 1) + "..." + str.substr(str.size() - (size / 2 - 2), str.size());
case ELIDE_RIGHT:
return str.substr(0, size - 3) + "...";
}
}
std::string substrBetween(const std::string &str, const std::string &delimiter1, const std::string &delimiter2)
{
size_t found_delimiter1 = str.find(delimiter1);
+9
View File
@@ -48,6 +48,15 @@ namespace utility
std::string trim(const std::string &str);
enum ElideMode
{
ELIDE_LEFT,
ELIDE_MIDDLE,
ELIDE_RIGHT
};
std::string elide(const std::string& str, ElideMode mode, size_t size);
template <typename ContainerType>
ContainerType split(const std::string& str, const std::string& delimiter)
{