forked from organicmaps/organicmaps
[search] [assessment-tool] Samples can now be marked as useless.
This commit is contained in:
parent
b4e8f38c7c
commit
861bc09eeb
15 changed files with 200 additions and 32 deletions
|
@ -21,9 +21,18 @@ void Context::Clear()
|
|||
m_nonFoundResults.clear();
|
||||
m_nonFoundResultsEdits.Clear();
|
||||
|
||||
m_sampleEdits.Clear();
|
||||
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
void Context::LoadFromSample(search::Sample const & sample)
|
||||
{
|
||||
Clear();
|
||||
m_sample = sample;
|
||||
m_sampleEdits.Reset(sample.m_useless);
|
||||
}
|
||||
|
||||
search::Sample Context::MakeSample(search::FeatureLoader & loader) const
|
||||
{
|
||||
search::Sample outSample = m_sample;
|
||||
|
@ -93,6 +102,8 @@ search::Sample Context::MakeSample(search::FeatureLoader & loader) const
|
|||
outResults.push_back(search::Sample::Result::Build(*ft, *foundEntries[i].m_currRelevance));
|
||||
}
|
||||
|
||||
outSample.m_useless = m_sampleEdits.m_currUseless;
|
||||
|
||||
return outSample;
|
||||
}
|
||||
|
||||
|
@ -102,12 +113,15 @@ void Context::ApplyEdits()
|
|||
return;
|
||||
m_foundResultsEdits.Apply();
|
||||
m_nonFoundResultsEdits.Apply();
|
||||
m_sampleEdits.Apply();
|
||||
}
|
||||
|
||||
// ContextList -------------------------------------------------------------------------------------
|
||||
ContextList::ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate)
|
||||
ContextList::ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate,
|
||||
OnSampleUpdate onSampleUpdate)
|
||||
: m_onResultsUpdate(onResultsUpdate)
|
||||
, m_onNonFoundResultsUpdate(onNonFoundResultsUpdate)
|
||||
, m_onSampleUpdate(onSampleUpdate)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -133,6 +147,11 @@ void ContextList::Resize(size_t size)
|
|||
OnContextUpdated(i);
|
||||
if (m_onNonFoundResultsUpdate)
|
||||
m_onNonFoundResultsUpdate(i, update);
|
||||
},
|
||||
[this, i]() {
|
||||
OnContextUpdated(i);
|
||||
if (m_onSampleUpdate)
|
||||
m_onSampleUpdate(i);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,11 @@ struct Context
|
|||
Completed
|
||||
};
|
||||
|
||||
Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate)
|
||||
: m_foundResultsEdits(onFoundResultsUpdate), m_nonFoundResultsEdits(onNonFoundResultsUpdate)
|
||||
Context(Edits::OnUpdate onFoundResultsUpdate, Edits::OnUpdate onNonFoundResultsUpdate,
|
||||
SampleEdits::OnUpdate onSampleUpdate)
|
||||
: m_foundResultsEdits(onFoundResultsUpdate)
|
||||
, m_nonFoundResultsEdits(onNonFoundResultsUpdate)
|
||||
, m_sampleEdits(onSampleUpdate)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -42,8 +45,12 @@ struct Context
|
|||
m_nonFoundResultsEdits.Add(result.m_relevance);
|
||||
}
|
||||
|
||||
bool IsUseless() const { return m_sampleEdits.m_currUseless; }
|
||||
|
||||
bool HasChanges() const
|
||||
{
|
||||
if (m_sampleEdits.HasChanges())
|
||||
return true;
|
||||
if (!m_initialized)
|
||||
return false;
|
||||
return m_foundResultsEdits.HasChanges() || m_nonFoundResultsEdits.HasChanges();
|
||||
|
@ -51,6 +58,8 @@ struct Context
|
|||
|
||||
void Clear();
|
||||
|
||||
void LoadFromSample(search::Sample const & sample);
|
||||
|
||||
// Makes sample in accordance with uncommited edits.
|
||||
search::Sample MakeSample(search::FeatureLoader & loader) const;
|
||||
|
||||
|
@ -67,6 +76,8 @@ struct Context
|
|||
std::vector<search::Sample::Result> m_nonFoundResults;
|
||||
Edits m_nonFoundResultsEdits;
|
||||
|
||||
SampleEdits m_sampleEdits;
|
||||
|
||||
SearchState m_searchState = SearchState::Untouched;
|
||||
|
||||
bool m_initialized = false;
|
||||
|
@ -95,6 +106,8 @@ public:
|
|||
return (*m_contexts)[index].m_searchState;
|
||||
}
|
||||
|
||||
bool IsUseless(size_t index) const { return (*m_contexts)[index].m_sampleEdits.m_currUseless; }
|
||||
|
||||
size_t Size() const { return m_contexts->Size(); }
|
||||
|
||||
private:
|
||||
|
@ -102,8 +115,10 @@ public:
|
|||
};
|
||||
|
||||
using OnUpdate = std::function<void(size_t index, Edits::Update const & update)>;
|
||||
using OnSampleUpdate = std::function<void(size_t index)>;
|
||||
|
||||
ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate);
|
||||
ContextList(OnUpdate onResultsUpdate, OnUpdate onNonFoundResultsUpdate,
|
||||
OnSampleUpdate onSampleUpdate);
|
||||
|
||||
void Resize(size_t size);
|
||||
size_t Size() const { return m_contexts.size(); }
|
||||
|
@ -128,4 +143,5 @@ private:
|
|||
|
||||
OnUpdate m_onResultsUpdate;
|
||||
OnUpdate m_onNonFoundResultsUpdate;
|
||||
OnSampleUpdate m_onSampleUpdate;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,38 @@
|
|||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
struct SampleEdits
|
||||
{
|
||||
using OnUpdate = std::function<void()>;
|
||||
|
||||
SampleEdits(OnUpdate onUpdate) : m_onUpdate(onUpdate) {}
|
||||
|
||||
void Reset(bool origUseless)
|
||||
{
|
||||
m_origUseless = origUseless;
|
||||
m_currUseless = origUseless;
|
||||
}
|
||||
|
||||
void FlipUsefulness()
|
||||
{
|
||||
m_currUseless ^= true;
|
||||
if (m_onUpdate)
|
||||
m_onUpdate();
|
||||
}
|
||||
|
||||
void Apply() { m_origUseless = m_currUseless; }
|
||||
|
||||
bool HasChanges() const { return m_origUseless != m_currUseless; }
|
||||
|
||||
void Clear() {}
|
||||
|
||||
bool m_origUseless = false;
|
||||
bool m_currUseless = false;
|
||||
|
||||
OnUpdate m_onUpdate;
|
||||
};
|
||||
|
||||
// todo(@m) Rename to ResultsEdits?
|
||||
class Edits
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -36,12 +36,13 @@ MainModel::MainModel(Framework & framework)
|
|||
},
|
||||
[this](size_t sampleIndex, Edits::Update const & update) {
|
||||
OnUpdate(View::ResultType::NonFound, sampleIndex, update);
|
||||
})
|
||||
},
|
||||
[this](size_t sampleIndex) { OnSampleUpdate(sampleIndex); })
|
||||
, m_runner(m_framework, m_dataSource, m_contexts,
|
||||
[this](search::Results const & results) { UpdateViewOnResults(results); },
|
||||
[this](size_t index) {
|
||||
// The second parameter does not matter, we only change SearchStatus.
|
||||
m_view->OnSampleChanged(index, false /* hasEdits */);
|
||||
// Only the first parameter matters because we only change SearchStatus.
|
||||
m_view->OnSampleChanged(index, false /* isUseless */, false /* hasEdits */);
|
||||
})
|
||||
{
|
||||
search::CheckLocale();
|
||||
|
@ -77,11 +78,8 @@ void MainModel::Open(string const & path)
|
|||
|
||||
m_contexts.Resize(samples.size());
|
||||
for (size_t i = 0; i < samples.size(); ++i)
|
||||
{
|
||||
auto & context = m_contexts[i];
|
||||
context.Clear();
|
||||
context.m_sample = samples[i];
|
||||
}
|
||||
m_contexts[i].LoadFromSample(samples[i]);
|
||||
|
||||
m_path = path;
|
||||
|
||||
m_view->SetSamples(ContextList::SamplesSlice(m_contexts));
|
||||
|
@ -134,7 +132,7 @@ void MainModel::OnSampleSelected(int index)
|
|||
auto & context = m_contexts[index];
|
||||
auto const & sample = context.m_sample;
|
||||
|
||||
m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
|
||||
m_view->ShowSample(index, sample, sample.m_pos, context.IsUseless(), context.HasChanges());
|
||||
|
||||
m_runner.ResetForegroundSearch();
|
||||
m_numShownResults = 0;
|
||||
|
@ -271,12 +269,22 @@ void MainModel::AddNonFoundResult(FeatureID const & id)
|
|||
context.AddNonFoundResult(result);
|
||||
}
|
||||
|
||||
void MainModel::FlipSampleUsefulness(int index)
|
||||
{
|
||||
CHECK_EQUAL(m_selectedSample, index, ());
|
||||
|
||||
m_contexts[index].m_sampleEdits.FlipUsefulness();
|
||||
|
||||
// Don't bother with resetting search: we cannot tell whether
|
||||
// the sample is useless without its results anyway.
|
||||
}
|
||||
|
||||
void MainModel::InitiateForegroundSearch(size_t index)
|
||||
{
|
||||
auto & context = m_contexts[index];
|
||||
auto const & sample = context.m_sample;
|
||||
|
||||
m_view->ShowSample(index, sample, sample.m_pos, context.HasChanges());
|
||||
m_view->ShowSample(index, sample, sample.m_pos, context.IsUseless(), context.HasChanges());
|
||||
m_runner.InitiateForegroundSearch(index);
|
||||
m_view->OnSearchStarted();
|
||||
}
|
||||
|
@ -299,7 +307,7 @@ void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Updat
|
|||
}
|
||||
|
||||
m_view->OnResultChanged(sampleIndex, type, update);
|
||||
m_view->OnSampleChanged(sampleIndex, context.HasChanges());
|
||||
m_view->OnSampleChanged(sampleIndex, context.IsUseless(), context.HasChanges());
|
||||
m_view->OnSamplesChanged(m_contexts.HasChanges());
|
||||
|
||||
if (update.m_type == Type::Add || update.m_type == Type::Resurrect ||
|
||||
|
@ -311,6 +319,14 @@ void MainModel::OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Updat
|
|||
}
|
||||
}
|
||||
|
||||
void MainModel::OnSampleUpdate(size_t sampleIndex)
|
||||
{
|
||||
auto & context = m_contexts[sampleIndex];
|
||||
|
||||
m_view->OnSampleChanged(sampleIndex, context.IsUseless(), context.HasChanges());
|
||||
m_view->OnSamplesChanged(m_contexts.HasChanges());
|
||||
}
|
||||
|
||||
void MainModel::UpdateViewOnResults(search::Results const & results)
|
||||
{
|
||||
CHECK(m_threadChecker.CalledOnOriginalThread(), ());
|
||||
|
@ -329,7 +345,8 @@ void MainModel::UpdateViewOnResults(search::Results const & results)
|
|||
m_view->ShowMarks(context);
|
||||
m_view->OnResultChanged(m_selectedSample, View::ResultType::Found, Edits::Update::MakeAll());
|
||||
m_view->OnResultChanged(m_selectedSample, View::ResultType::NonFound, Edits::Update::MakeAll());
|
||||
m_view->OnSampleChanged(m_selectedSample, context.HasChanges());
|
||||
m_view->OnSampleChanged(m_selectedSample, context.IsUseless(), context.HasChanges());
|
||||
m_view->OnSamplesChanged(m_contexts.HasChanges());
|
||||
|
||||
m_view->SetEdits(m_selectedSample, context.m_foundResultsEdits, context.m_nonFoundResultsEdits);
|
||||
m_view->OnSearchCompleted();
|
||||
|
@ -346,7 +363,7 @@ void MainModel::OnChangeAllRelevancesClicked(Edits::Relevance relevance)
|
|||
|
||||
m_view->OnResultChanged(m_selectedSample, View::ResultType::Found, Edits::Update::MakeAll());
|
||||
m_view->OnResultChanged(m_selectedSample, View::ResultType::NonFound, Edits::Update::MakeAll());
|
||||
m_view->OnSampleChanged(m_selectedSample, context.HasChanges());
|
||||
m_view->OnSampleChanged(m_selectedSample, context.IsUseless(), context.HasChanges());
|
||||
m_view->OnSamplesChanged(m_contexts.HasChanges());
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
bool HasChanges() override;
|
||||
bool AlreadyInSamples(FeatureID const & id) override;
|
||||
void AddNonFoundResult(FeatureID const & id) override;
|
||||
void FlipSampleUsefulness(int index) override;
|
||||
|
||||
private:
|
||||
static int constexpr kInvalidIndex = -1;
|
||||
|
@ -54,6 +55,7 @@ private:
|
|||
void InitiateForegroundSearch(size_t index);
|
||||
|
||||
void OnUpdate(View::ResultType type, size_t sampleIndex, Edits::Update const & update);
|
||||
void OnSampleUpdate(size_t sampleIndex);
|
||||
|
||||
void UpdateViewOnResults(search::Results const & results);
|
||||
void ShowMarks(Context const & context);
|
||||
|
|
|
@ -110,7 +110,8 @@ void MainView::OnSearchCompleted()
|
|||
}
|
||||
|
||||
void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample,
|
||||
boost::optional<m2::PointD> const & position, bool hasEdits)
|
||||
boost::optional<m2::PointD> const & position, bool isUseless,
|
||||
bool hasEdits)
|
||||
{
|
||||
m_sampleLocale = sample.m_locale;
|
||||
|
||||
|
@ -121,7 +122,7 @@ void MainView::ShowSample(size_t sampleIndex, search::Sample const & sample,
|
|||
|
||||
OnResultChanged(sampleIndex, ResultType::Found, Edits::Update::MakeAll());
|
||||
OnResultChanged(sampleIndex, ResultType::NonFound, Edits::Update::MakeAll());
|
||||
OnSampleChanged(sampleIndex, hasEdits);
|
||||
OnSampleChanged(sampleIndex, isUseless, hasEdits);
|
||||
}
|
||||
|
||||
void MainView::AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end)
|
||||
|
@ -190,12 +191,13 @@ void MainView::OnResultChanged(size_t sampleIndex, ResultType type, Edits::Updat
|
|||
}
|
||||
}
|
||||
|
||||
void MainView::OnSampleChanged(size_t sampleIndex, bool hasEdits)
|
||||
void MainView::OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits)
|
||||
{
|
||||
m_samplesView->OnUpdate(sampleIndex);
|
||||
if (!m_samplesView->IsSelected(sampleIndex))
|
||||
return;
|
||||
SetSampleDockTitle(hasEdits);
|
||||
SetSampleDockTitle(isUseless, hasEdits);
|
||||
m_sampleView->OnUselessnessChanged(isUseless);
|
||||
}
|
||||
|
||||
void MainView::OnSamplesChanged(bool hasEdits)
|
||||
|
@ -225,7 +227,7 @@ void MainView::Clear()
|
|||
SetSamplesDockTitle(false /* hasEdits */);
|
||||
|
||||
m_sampleView->Clear();
|
||||
SetSampleDockTitle(false /* hasEdits */);
|
||||
SetSampleDockTitle(false /* isUseless */, false /* hasEdits */);
|
||||
|
||||
m_skipFeatureInfoDialog = false;
|
||||
m_sampleLocale.clear();
|
||||
|
@ -361,6 +363,9 @@ void MainView::InitDocks()
|
|||
connect(model, &QItemSelectionModel::selectionChanged, this, &MainView::OnSampleSelected);
|
||||
}
|
||||
|
||||
connect(m_samplesView, &SamplesView::FlipSampleUsefulness,
|
||||
[this](int index) { m_model->FlipSampleUsefulness(index); });
|
||||
|
||||
m_samplesDock = CreateDock(*m_samplesView);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, m_samplesDock);
|
||||
SetSamplesDockTitle(false /* hasEdits */);
|
||||
|
@ -393,7 +398,7 @@ void MainView::InitDocks()
|
|||
connect(m_sampleDock, &QDockWidget::dockLocationChanged,
|
||||
[this](Qt::DockWidgetArea area) { m_sampleView->OnLocationChanged(area); });
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_sampleDock);
|
||||
SetSampleDockTitle(false /* hasEdits */);
|
||||
SetSampleDockTitle(false /* isUseless */, false /* hasEdits */);
|
||||
}
|
||||
|
||||
void MainView::Open()
|
||||
|
@ -479,13 +484,15 @@ void MainView::SetSamplesDockTitle(bool hasEdits)
|
|||
m_samplesDock->setWindowTitle(tr("Samples"));
|
||||
}
|
||||
|
||||
void MainView::SetSampleDockTitle(bool hasEdits)
|
||||
void MainView::SetSampleDockTitle(bool isUseless, bool hasEdits)
|
||||
{
|
||||
CHECK(m_sampleDock, ());
|
||||
std::string title = "Sample";
|
||||
if (hasEdits)
|
||||
m_sampleDock->setWindowTitle(tr("Sample *"));
|
||||
else
|
||||
m_sampleDock->setWindowTitle(tr("Sample"));
|
||||
title += " *";
|
||||
if (isUseless)
|
||||
title += " (useless)";
|
||||
m_sampleDock->setWindowTitle(tr(title.data()));
|
||||
}
|
||||
|
||||
MainView::SaveResult MainView::TryToSaveEdits(QString const & msg)
|
||||
|
|
|
@ -33,7 +33,8 @@ public:
|
|||
void OnSearchStarted() override;
|
||||
void OnSearchCompleted() override;
|
||||
void ShowSample(size_t sampleIndex, search::Sample const & sample,
|
||||
boost::optional<m2::PointD> const & position, bool hasEdits) override;
|
||||
boost::optional<m2::PointD> const & position, bool isUseless,
|
||||
bool hasEdits) override;
|
||||
|
||||
void AddFoundResults(search::Results::ConstIter begin, search::Results::ConstIter end) override;
|
||||
void ShowNonFoundResults(std::vector<search::Sample::Result> const & results,
|
||||
|
@ -53,7 +54,7 @@ public:
|
|||
void OnResultChanged(size_t sampleIndex, ResultType type, Edits::Update const & update) override;
|
||||
void SetEdits(size_t sampleIndex, Edits & foundResultsEdits,
|
||||
Edits & nonFoundResultsEdits) override;
|
||||
void OnSampleChanged(size_t sampleIndex, bool hasEdits) override;
|
||||
void OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits) override;
|
||||
void OnSamplesChanged(bool hasEdits) override;
|
||||
|
||||
void ShowError(std::string const & msg) override;
|
||||
|
@ -105,7 +106,7 @@ private:
|
|||
void InitiateBackgroundSearch();
|
||||
|
||||
void SetSamplesDockTitle(bool hasEdits);
|
||||
void SetSampleDockTitle(bool hasEdits);
|
||||
void SetSampleDockTitle(bool isUseless, bool hasEdits);
|
||||
SaveResult TryToSaveEdits(QString const & msg);
|
||||
|
||||
void AddSelectedFeature(QPoint const & p);
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
|
||||
virtual bool AlreadyInSamples(FeatureID const & id) = 0;
|
||||
virtual void AddNonFoundResult(FeatureID const & id) = 0;
|
||||
virtual void FlipSampleUsefulness(int index) = 0;
|
||||
|
||||
protected:
|
||||
View * m_view = nullptr;
|
||||
|
|
|
@ -101,6 +101,7 @@ SampleView::SampleView(QWidget * parent, Framework & framework)
|
|||
auto * layout =
|
||||
BuildSubLayout<QVBoxLayout>(*mainLayout, *this /* parent */, &m_relatedQueriesBox);
|
||||
SetVerticalStretch(*m_relatedQueriesBox, 1 /* stretch */);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Related queries")));
|
||||
|
||||
m_relatedQueries = new QListWidget();
|
||||
|
@ -121,6 +122,13 @@ SampleView::SampleView(QWidget * parent, Framework & framework)
|
|||
layout->addWidget(m_markAllAsIrrelevant);
|
||||
}
|
||||
|
||||
{
|
||||
m_uselessnessLabel = new QLabel(this /* parent */);
|
||||
m_uselessnessLabel->setText(tr("Sample is marked as useless"));
|
||||
m_uselessnessLabel->hide();
|
||||
mainLayout->addWidget(m_uselessnessLabel);
|
||||
}
|
||||
|
||||
{
|
||||
auto * layout =
|
||||
BuildSubLayout<QVBoxLayout>(*mainLayout, *this /* parent */, &m_foundResultsBox);
|
||||
|
@ -305,6 +313,25 @@ void SampleView::SetEdits(Edits & resultsEdits, Edits & nonFoundResultsEdits)
|
|||
m_nonFoundResultsEdits = &nonFoundResultsEdits;
|
||||
}
|
||||
|
||||
void SampleView::OnUselessnessChanged(bool isUseless)
|
||||
{
|
||||
if (isUseless)
|
||||
{
|
||||
m_uselessnessLabel->show();
|
||||
m_foundResultsBox->hide();
|
||||
m_nonFoundResultsBox->hide();
|
||||
m_markAllAsRelevant->hide();
|
||||
m_markAllAsIrrelevant->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_uselessnessLabel->hide();
|
||||
m_foundResultsBox->show();
|
||||
m_markAllAsRelevant->show();
|
||||
m_markAllAsIrrelevant->show();
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::Clear()
|
||||
{
|
||||
m_query->hide();
|
||||
|
|
|
@ -44,6 +44,8 @@ public:
|
|||
|
||||
void SetEdits(Edits & resultsEdits, Edits & nonFoundResultsEdits);
|
||||
|
||||
void OnUselessnessChanged(bool isUseless);
|
||||
|
||||
void Clear();
|
||||
|
||||
ResultsView & GetFoundResultsView() { return *m_foundResults; }
|
||||
|
@ -81,6 +83,8 @@ private:
|
|||
QPushButton * m_markAllAsRelevant = nullptr;
|
||||
QPushButton * m_markAllAsIrrelevant = nullptr;
|
||||
|
||||
QLabel * m_uselessnessLabel = nullptr;
|
||||
|
||||
ResultsView * m_foundResults = nullptr;
|
||||
QWidget * m_foundResultsBox = nullptr;
|
||||
|
||||
|
|
|
@ -3,9 +3,15 @@
|
|||
#include "search/search_quality/assessment_tool/helpers.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtGui/QContextMenuEvent>
|
||||
#include <QtGui/QStandardItem>
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
// SamplesView::Model ------------------------------------------------------------------------------
|
||||
SamplesView::Model::Model(QWidget * parent)
|
||||
|
@ -55,3 +61,19 @@ bool SamplesView::IsSelected(size_t index) const
|
|||
{
|
||||
return selectionModel()->isRowSelected(base::checked_cast<int>(index), QModelIndex());
|
||||
}
|
||||
|
||||
void SamplesView::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
QModelIndex modelIndex = selectionModel()->currentIndex();
|
||||
if (!modelIndex.isValid())
|
||||
return;
|
||||
|
||||
int const index = modelIndex.row();
|
||||
bool const isUseless = m_model->SampleIsUseless(index);
|
||||
|
||||
QMenu menu(this);
|
||||
auto const text = std::string(isUseless ? "unmark" : "mark") + " sample as useless";
|
||||
auto const * action = menu.addAction(text.c_str());
|
||||
connect(action, &QAction::triggered, [this, index]() { emit FlipSampleUsefulness(index); });
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
#include <vector>
|
||||
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QTableView>
|
||||
|
||||
class SamplesView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SamplesView(QWidget * parent);
|
||||
|
||||
|
@ -20,6 +23,12 @@ public:
|
|||
void OnUpdate(size_t index) { m_model->OnUpdate(index); }
|
||||
void Clear() { m_model->SetSamples(ContextList::SamplesSlice{}); }
|
||||
|
||||
// QMainWindow overrides:
|
||||
void contextMenuEvent(QContextMenuEvent * event) override;
|
||||
|
||||
signals:
|
||||
void FlipSampleUsefulness(int index);
|
||||
|
||||
private:
|
||||
class Model : public QStandardItemModel
|
||||
{
|
||||
|
@ -42,6 +51,8 @@ private:
|
|||
emit dataChanged(ix, ix);
|
||||
}
|
||||
|
||||
bool SampleIsUseless(int index) const { return m_samples.IsUseless(index); }
|
||||
|
||||
// QStandardItemModel overrides:
|
||||
QVariant data(QModelIndex const & index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ public:
|
|||
virtual void OnSearchStarted() = 0;
|
||||
virtual void OnSearchCompleted() = 0;
|
||||
virtual void ShowSample(size_t index, search::Sample const & sample,
|
||||
boost::optional<m2::PointD> const & position, bool hasEdits) = 0;
|
||||
boost::optional<m2::PointD> const & position, bool isUseless,
|
||||
bool hasEdits) = 0;
|
||||
|
||||
virtual void AddFoundResults(search::Results::ConstIter begin,
|
||||
search::Results::ConstIter end) = 0;
|
||||
|
@ -55,7 +56,7 @@ public:
|
|||
virtual void OnResultChanged(size_t sampleIndex, ResultType type,
|
||||
Edits::Update const & update) = 0;
|
||||
virtual void SetEdits(size_t index, Edits & foundResultsEdits, Edits & nonFoundResultsEdits) = 0;
|
||||
virtual void OnSampleChanged(size_t sampleIndex, bool hasEdits) = 0;
|
||||
virtual void OnSampleChanged(size_t sampleIndex, bool isUseless, bool hasEdits) = 0;
|
||||
virtual void OnSamplesChanged(bool hasEdits) = 0;
|
||||
|
||||
virtual void ShowError(std::string const & msg) = 0;
|
||||
|
|
|
@ -168,6 +168,7 @@ void Sample::DeserializeFromJSONImpl(json_t * root)
|
|||
FromJSONObject(root, "viewport", m_viewport);
|
||||
FromJSONObjectOptional(root, "results", m_results);
|
||||
FromJSONObjectOptional(root, "related_queries", m_relatedQueries);
|
||||
FromJSONObjectOptionalField(root, "useless", m_useless);
|
||||
}
|
||||
|
||||
void Sample::SerializeToJSONImpl(json_t & root) const
|
||||
|
@ -178,6 +179,8 @@ void Sample::SerializeToJSONImpl(json_t & root) const
|
|||
ToJSONObject(root, "viewport", m_viewport);
|
||||
ToJSONObject(root, "results", m_results);
|
||||
ToJSONObject(root, "related_queries", m_relatedQueries);
|
||||
if (m_useless)
|
||||
ToJSONObject(root, "useless", m_useless);
|
||||
}
|
||||
|
||||
void Sample::FillSearchParams(search::SearchParams & params) const
|
||||
|
|
|
@ -76,6 +76,11 @@ struct Sample
|
|||
m2::RectD m_viewport = m2::RectD(0, 0, 0, 0);
|
||||
std::vector<Result> m_results;
|
||||
std::vector<strings::UniString> m_relatedQueries;
|
||||
|
||||
// A useless sample is usually a result of the user exploring
|
||||
// the search engine without a clear search intent or a sample
|
||||
// that cannot be assessed properly using only the data it contains.
|
||||
bool m_useless = false;
|
||||
};
|
||||
|
||||
void FromJSONObject(json_t * root, char const * field,
|
||||
|
|
Loading…
Add table
Reference in a new issue