mirror of https://github.com/procxx/kepka.git
Display export progress.
This commit is contained in:
parent
5f01751660
commit
4115d3d13d
|
@ -571,6 +571,7 @@ void ApiWrap::finishUserpicsSlice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApiWrap::loadUserpicProgress(FileProgress progress) {
|
bool ApiWrap::loadUserpicProgress(FileProgress progress) {
|
||||||
|
Expects(_fileProcess != nullptr);
|
||||||
Expects(_userpicsProcess != nullptr);
|
Expects(_userpicsProcess != nullptr);
|
||||||
Expects(_userpicsProcess->slice.has_value());
|
Expects(_userpicsProcess->slice.has_value());
|
||||||
Expects((_userpicsProcess->fileIndex >= 0)
|
Expects((_userpicsProcess->fileIndex >= 0)
|
||||||
|
@ -578,6 +579,7 @@ bool ApiWrap::loadUserpicProgress(FileProgress progress) {
|
||||||
< _userpicsProcess->slice->list.size()));
|
< _userpicsProcess->slice->list.size()));
|
||||||
|
|
||||||
return _userpicsProcess->fileProgress(DownloadProgress{
|
return _userpicsProcess->fileProgress(DownloadProgress{
|
||||||
|
_fileProcess->relativePath,
|
||||||
_userpicsProcess->fileIndex,
|
_userpicsProcess->fileIndex,
|
||||||
progress.ready,
|
progress.ready,
|
||||||
progress.total });
|
progress.total });
|
||||||
|
@ -886,12 +888,14 @@ void ApiWrap::finishMessagesSlice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
|
bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
|
||||||
|
Expects(_fileProcess != nullptr);
|
||||||
Expects(_chatProcess != nullptr);
|
Expects(_chatProcess != nullptr);
|
||||||
Expects(_chatProcess->slice.has_value());
|
Expects(_chatProcess->slice.has_value());
|
||||||
Expects((_chatProcess->fileIndex >= 0)
|
Expects((_chatProcess->fileIndex >= 0)
|
||||||
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
||||||
|
|
||||||
return _chatProcess->fileProgress(DownloadProgress{
|
return _chatProcess->fileProgress(DownloadProgress{
|
||||||
|
_fileProcess->relativePath,
|
||||||
_chatProcess->fileIndex,
|
_chatProcess->fileIndex,
|
||||||
progress.ready,
|
progress.ready,
|
||||||
progress.total });
|
progress.total });
|
||||||
|
@ -996,7 +1000,6 @@ void ApiWrap::loadFile(
|
||||||
_fileProcess->progress = std::move(progress);
|
_fileProcess->progress = std::move(progress);
|
||||||
_fileProcess->done = std::move(done);
|
_fileProcess->done = std::move(done);
|
||||||
|
|
||||||
|
|
||||||
if (_fileProcess->progress) {
|
if (_fileProcess->progress) {
|
||||||
const auto progress = FileProgress{
|
const auto progress = FileProgress{
|
||||||
_fileProcess->file.size(),
|
_fileProcess->file.size(),
|
||||||
|
|
|
@ -57,6 +57,7 @@ public:
|
||||||
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
|
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
|
||||||
|
|
||||||
struct DownloadProgress {
|
struct DownloadProgress {
|
||||||
|
QString path;
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int ready = 0;
|
int ready = 0;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
|
@ -68,11 +68,17 @@ private:
|
||||||
ProcessingState stateLeftChannelsList(int processed) const;
|
ProcessingState stateLeftChannelsList(int processed) const;
|
||||||
ProcessingState stateDialogsList(int processed) const;
|
ProcessingState stateDialogsList(int processed) const;
|
||||||
ProcessingState statePersonalInfo() const;
|
ProcessingState statePersonalInfo() const;
|
||||||
ProcessingState stateUserpics(DownloadProgress progress) const;
|
ProcessingState stateUserpics(const DownloadProgress &progress) const;
|
||||||
ProcessingState stateContacts() const;
|
ProcessingState stateContacts() const;
|
||||||
ProcessingState stateSessions() const;
|
ProcessingState stateSessions() const;
|
||||||
ProcessingState stateLeftChannels(DownloadProgress progress) const;
|
ProcessingState stateLeftChannels(
|
||||||
ProcessingState stateDialogs(DownloadProgress progress) const;
|
const DownloadProgress &progress) const;
|
||||||
|
ProcessingState stateDialogs(const DownloadProgress &progress) const;
|
||||||
|
void fillMessagesState(
|
||||||
|
ProcessingState &result,
|
||||||
|
const Data::DialogsInfo &info,
|
||||||
|
int index,
|
||||||
|
const DownloadProgress &progress) const;
|
||||||
|
|
||||||
int substepsInStep(Step step) const;
|
int substepsInStep(Step step) const;
|
||||||
|
|
||||||
|
@ -96,7 +102,10 @@ private:
|
||||||
// rpl::variable<State> fails to compile in MSVC :(
|
// rpl::variable<State> fails to compile in MSVC :(
|
||||||
State _state;
|
State _state;
|
||||||
rpl::event_stream<State> _stateChanges;
|
rpl::event_stream<State> _stateChanges;
|
||||||
std::shared_ptr<const std::vector<int>> _substepsInStep;
|
std::vector<int> _substepsInStep;
|
||||||
|
int _substepsTotal = 0;
|
||||||
|
mutable int _substepsPassed = 0;
|
||||||
|
mutable Step _lastProcessingStep = Step::Initializing;
|
||||||
|
|
||||||
std::unique_ptr<Output::AbstractWriter> _writer;
|
std::unique_ptr<Output::AbstractWriter> _writer;
|
||||||
std::vector<Step> _steps;
|
std::vector<Step> _steps;
|
||||||
|
@ -292,7 +301,7 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
|
||||||
push(Step::PersonalInfo, 1);
|
push(Step::PersonalInfo, 1);
|
||||||
}
|
}
|
||||||
if (_settings.types & Settings::Type::Userpics) {
|
if (_settings.types & Settings::Type::Userpics) {
|
||||||
push(Step::Userpics, info.userpicsCount);
|
push(Step::Userpics, 1);
|
||||||
}
|
}
|
||||||
if (_settings.types & Settings::Type::Contacts) {
|
if (_settings.types & Settings::Type::Contacts) {
|
||||||
push(Step::Contacts, 1);
|
push(Step::Contacts, 1);
|
||||||
|
@ -306,8 +315,8 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
|
||||||
if (_settings.types & Settings::Type::AnyChatsMask) {
|
if (_settings.types & Settings::Type::AnyChatsMask) {
|
||||||
push(Step::Dialogs, info.dialogsCount);
|
push(Step::Dialogs, info.dialogsCount);
|
||||||
}
|
}
|
||||||
_substepsInStep = std::make_shared<const std::vector<int>>(
|
_substepsInStep = std::move(result);
|
||||||
std::move(result));
|
_substepsTotal = ranges::accumulate(_substepsInStep, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::exportNext() {
|
void Controller::exportNext() {
|
||||||
|
@ -512,10 +521,17 @@ template <typename Callback>
|
||||||
ProcessingState Controller::prepareState(
|
ProcessingState Controller::prepareState(
|
||||||
Step step,
|
Step step,
|
||||||
Callback &&callback) const {
|
Callback &&callback) const {
|
||||||
|
if (step != _lastProcessingStep) {
|
||||||
|
_substepsPassed += substepsInStep(_lastProcessingStep);
|
||||||
|
_lastProcessingStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
auto result = ProcessingState();
|
auto result = ProcessingState();
|
||||||
callback(result);
|
callback(result);
|
||||||
result.step = step;
|
result.step = step;
|
||||||
result.substepsInStep = _substepsInStep;
|
result.substepsPassed = _substepsPassed;
|
||||||
|
result.substepsNow = substepsInStep(_lastProcessingStep);
|
||||||
|
result.substepsTotal = _substepsTotal;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,10 +540,12 @@ ProcessingState Controller::stateInitializing() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessingState Controller::stateLeftChannelsList(int processed) const {
|
ProcessingState Controller::stateLeftChannelsList(int processed) const {
|
||||||
const auto step = Step::LeftChannelsList;
|
return prepareState(Step::LeftChannelsList, [&](
|
||||||
return prepareState(step, [&](ProcessingState &result) {
|
ProcessingState &result) {
|
||||||
result.entityIndex = processed;
|
result.entityIndex = processed;
|
||||||
result.entityCount = std::max(processed, substepsInStep(step));
|
result.entityCount = std::max(
|
||||||
|
processed,
|
||||||
|
substepsInStep(Step::LeftChannels));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,17 +553,25 @@ ProcessingState Controller::stateDialogsList(int processed) const {
|
||||||
const auto step = Step::DialogsList;
|
const auto step = Step::DialogsList;
|
||||||
return prepareState(step, [&](ProcessingState &result) {
|
return prepareState(step, [&](ProcessingState &result) {
|
||||||
result.entityIndex = processed;
|
result.entityIndex = processed;
|
||||||
result.entityCount = std::max(processed, substepsInStep(step));
|
result.entityCount = std::max(
|
||||||
|
processed,
|
||||||
|
substepsInStep(Step::Dialogs));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ProcessingState Controller::statePersonalInfo() const {
|
ProcessingState Controller::statePersonalInfo() const {
|
||||||
return prepareState(Step::PersonalInfo);
|
return prepareState(Step::PersonalInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessingState Controller::stateUserpics(DownloadProgress progress) const {
|
ProcessingState Controller::stateUserpics(
|
||||||
|
const DownloadProgress &progress) const {
|
||||||
return prepareState(Step::Userpics, [&](ProcessingState &result) {
|
return prepareState(Step::Userpics, [&](ProcessingState &result) {
|
||||||
result.entityIndex = _userpicsWritten + progress.itemIndex;
|
result.entityIndex = _userpicsWritten + progress.itemIndex;
|
||||||
result.entityCount = std::max(_userpicsCount, result.entityIndex);
|
result.entityCount = std::max(_userpicsCount, result.entityIndex);
|
||||||
|
result.bytesType = ProcessingState::FileType::Photo;
|
||||||
|
if (!progress.path.isEmpty()) {
|
||||||
|
const auto last = progress.path.lastIndexOf('/');
|
||||||
|
result.bytesName = progress.path.mid(last + 1);
|
||||||
|
}
|
||||||
result.bytesLoaded = progress.ready;
|
result.bytesLoaded = progress.ready;
|
||||||
result.bytesCount = progress.total;
|
result.bytesCount = progress.total;
|
||||||
});
|
});
|
||||||
|
@ -560,35 +586,59 @@ ProcessingState Controller::stateSessions() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessingState Controller::stateLeftChannels(
|
ProcessingState Controller::stateLeftChannels(
|
||||||
DownloadProgress progress) const {
|
const DownloadProgress & progress) const {
|
||||||
const auto step = Step::LeftChannels;
|
const auto step = Step::LeftChannels;
|
||||||
return prepareState(step, [&](ProcessingState &result) {
|
return prepareState(step, [&](ProcessingState &result) {
|
||||||
result.entityIndex = _leftChannelIndex;
|
fillMessagesState(
|
||||||
result.entityCount = _leftChannelsInfo.list.size();
|
result,
|
||||||
result.itemIndex = _messagesWritten + progress.itemIndex;
|
_leftChannelsInfo,
|
||||||
result.itemCount = std::max(_messagesCount, result.entityIndex);
|
_leftChannelIndex,
|
||||||
result.bytesLoaded = progress.ready;
|
progress);
|
||||||
result.bytesCount = progress.total;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessingState Controller::stateDialogs(DownloadProgress progress) const {
|
ProcessingState Controller::stateDialogs(
|
||||||
|
const DownloadProgress &progress) const {
|
||||||
const auto step = Step::Dialogs;
|
const auto step = Step::Dialogs;
|
||||||
return prepareState(step, [&](ProcessingState &result) {
|
return prepareState(step, [&](ProcessingState &result) {
|
||||||
result.entityIndex = _dialogIndex;
|
fillMessagesState(
|
||||||
result.entityCount = _dialogsInfo.list.size();
|
result,
|
||||||
result.itemIndex = _messagesWritten + progress.itemIndex;
|
_dialogsInfo,
|
||||||
result.itemCount = std::max(_messagesCount, result.entityIndex);
|
_dialogIndex,
|
||||||
result.bytesLoaded = progress.ready;
|
progress);
|
||||||
result.bytesCount = progress.total;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::fillMessagesState(
|
||||||
|
ProcessingState &result,
|
||||||
|
const Data::DialogsInfo &info,
|
||||||
|
int index,
|
||||||
|
const DownloadProgress &progress) const {
|
||||||
|
const auto &dialog = info.list[index];
|
||||||
|
auto count = 0;
|
||||||
|
for (const auto &dialog : info.list) {
|
||||||
|
if (dialog.name.isEmpty()) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.entityIndex = index;
|
||||||
|
result.entityCount = info.list.size();
|
||||||
|
result.entityName = dialog.name;
|
||||||
|
result.itemIndex = _messagesWritten + progress.itemIndex;
|
||||||
|
result.itemCount = std::max(_messagesCount, result.entityIndex);
|
||||||
|
result.bytesType = ProcessingState::FileType::File; // TODO
|
||||||
|
if (!progress.path.isEmpty()) {
|
||||||
|
const auto last = progress.path.lastIndexOf('/');
|
||||||
|
result.bytesName = progress.path.mid(last + 1);
|
||||||
|
}
|
||||||
|
result.bytesLoaded = progress.ready;
|
||||||
|
result.bytesCount = progress.total;
|
||||||
|
}
|
||||||
|
|
||||||
int Controller::substepsInStep(Step step) const {
|
int Controller::substepsInStep(Step step) const {
|
||||||
Expects(_substepsInStep != 0);
|
Expects(_substepsInStep.size() > static_cast<int>(step));
|
||||||
Expects(_substepsInStep->size() > static_cast<int>(step));
|
|
||||||
|
|
||||||
return (*_substepsInStep)[static_cast<int>(step)];
|
return _substepsInStep[static_cast<int>(step)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::setFinishedState() {
|
void Controller::setFinishedState() {
|
||||||
|
|
|
@ -37,8 +37,8 @@ struct ProcessingState {
|
||||||
LeftChannels,
|
LeftChannels,
|
||||||
Dialogs,
|
Dialogs,
|
||||||
};
|
};
|
||||||
enum class Item {
|
enum class FileType {
|
||||||
Other,
|
None,
|
||||||
Photo,
|
Photo,
|
||||||
Video,
|
Video,
|
||||||
VoiceMessage,
|
VoiceMessage,
|
||||||
|
@ -50,21 +50,21 @@ struct ProcessingState {
|
||||||
|
|
||||||
Step step = Step::Initializing;
|
Step step = Step::Initializing;
|
||||||
|
|
||||||
std::shared_ptr<const std::vector<int>> substepsInStep;
|
int substepsPassed = 0;
|
||||||
|
int substepsNow = 0;
|
||||||
|
int substepsTotal = 0;
|
||||||
|
|
||||||
|
QString entityName;
|
||||||
int entityIndex = 0;
|
int entityIndex = 0;
|
||||||
int entityCount = 1;
|
int entityCount = 1;
|
||||||
QString entityName;
|
|
||||||
|
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int itemCount = 0;
|
int itemCount = 0;
|
||||||
Item itemType = Item::Other;
|
|
||||||
QString itemName;
|
|
||||||
QString itemId;
|
|
||||||
|
|
||||||
|
FileType bytesType = FileType::None;
|
||||||
|
QString bytesName;
|
||||||
int bytesLoaded = 0;
|
int bytesLoaded = 0;
|
||||||
int bytesCount = 0;
|
int bytesCount = 0;
|
||||||
QString objectId;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,3 +42,23 @@ exportErrorLabel: FlatLabel(boxLabel) {
|
||||||
align: align(top);
|
align: align(top);
|
||||||
textFg: boxTextFgError;
|
textFg: boxTextFgError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exportProgressDuration: 200;
|
||||||
|
exportProgressRowHeight: 30px;
|
||||||
|
exportProgressRowPadding: margins(22px, 10px, 22px, 20px);
|
||||||
|
exportProgressLabel: FlatLabel(boxLabel) {
|
||||||
|
textFg: windowBoldFg;
|
||||||
|
maxHeight: 20px;
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(14px semibold);
|
||||||
|
linkFont: font(14px semibold);
|
||||||
|
linkFontOver: font(14px semibold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exportProgressInfoLabel: FlatLabel(boxLabel) {
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
maxHeight: 20px;
|
||||||
|
}
|
||||||
|
exportProgressWidth: 3px;
|
||||||
|
exportProgressFg: mediaPlayerActiveFg;
|
||||||
|
exportProgressBg: mediaPlayerInactiveFg;
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "export/view/export_view_content.h"
|
#include "export/view/export_view_content.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
@ -23,26 +24,80 @@ Content ContentFromState(const ProcessingState &state) {
|
||||||
float64 progress) {
|
float64 progress) {
|
||||||
result.rows.push_back({ id, label, info, progress });
|
result.rows.push_back({ id, label, info, progress });
|
||||||
};
|
};
|
||||||
|
const auto pushMain = [&](const QString &label) {
|
||||||
|
const auto info = (state.entityCount > 0)
|
||||||
|
? (QString::number(state.entityIndex)
|
||||||
|
+ " / "
|
||||||
|
+ QString::number(state.entityCount))
|
||||||
|
: QString();
|
||||||
|
if (!state.substepsTotal) {
|
||||||
|
push("main", label, info, 0.);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto substepsTotal = state.substepsTotal;
|
||||||
|
const auto step = static_cast<int>(state.step);
|
||||||
|
const auto done = state.substepsPassed;
|
||||||
|
const auto add = state.substepsNow;
|
||||||
|
const auto doneProgress = done / float64(substepsTotal);
|
||||||
|
const auto addProgress = (state.entityCount > 0)
|
||||||
|
? ((float64(add) * state.entityIndex)
|
||||||
|
/ (float64(substepsTotal) * state.entityCount))
|
||||||
|
: 0.;
|
||||||
|
push("main", label, info, doneProgress + addProgress);
|
||||||
|
};
|
||||||
|
const auto pushBytes = [&](const QString &id, const QString &label) {
|
||||||
|
if (!state.bytesCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto progress = state.bytesLoaded / float64(state.bytesCount);
|
||||||
|
const auto info = formatDownloadText(
|
||||||
|
state.bytesLoaded,
|
||||||
|
state.bytesCount);
|
||||||
|
push(id, label, info, progress);
|
||||||
|
};
|
||||||
switch (state.step) {
|
switch (state.step) {
|
||||||
case Step::Initializing:
|
case Step::Initializing:
|
||||||
|
pushMain(lang(lng_export_state_initializing));
|
||||||
|
break;
|
||||||
case Step::LeftChannelsList:
|
case Step::LeftChannelsList:
|
||||||
case Step::DialogsList:
|
case Step::DialogsList:
|
||||||
|
pushMain(lang(lng_export_state_chats_list));
|
||||||
|
break;
|
||||||
case Step::PersonalInfo:
|
case Step::PersonalInfo:
|
||||||
|
pushMain(lang(lng_export_option_info));
|
||||||
|
break;
|
||||||
case Step::Userpics:
|
case Step::Userpics:
|
||||||
|
pushMain(lang(lng_export_state_userpics));
|
||||||
|
pushBytes(
|
||||||
|
"userpic" + QString::number(state.entityIndex),
|
||||||
|
"Photo_" + QString::number(state.entityIndex + 1) + ".jpg");
|
||||||
|
break;
|
||||||
case Step::Contacts:
|
case Step::Contacts:
|
||||||
|
pushMain(lang(lng_export_option_contacts));
|
||||||
|
break;
|
||||||
case Step::Sessions:
|
case Step::Sessions:
|
||||||
|
pushMain(lang(lng_export_option_sessions));
|
||||||
|
break;
|
||||||
case Step::LeftChannels:
|
case Step::LeftChannels:
|
||||||
case Step::Dialogs:
|
case Step::Dialogs:
|
||||||
push("init", lang(lng_export_state_initializing), QString(), 0.);
|
pushMain(lang(lng_export_state_chats));
|
||||||
if (state.entityCount > 0) {
|
|
||||||
push("entity", QString(), QString::number(state.entityIndex) + '/' + QString::number(state.entityCount), 0.);
|
|
||||||
}
|
|
||||||
if (state.itemCount > 0) {
|
if (state.itemCount > 0) {
|
||||||
push("item", QString(), QString::number(state.itemIndex) + '/' + QString::number(state.itemCount), 0.);
|
push(
|
||||||
}
|
"chat" + QString::number(state.entityIndex),
|
||||||
if (state.bytesCount > 0) {
|
(state.entityName.isEmpty()
|
||||||
push("bytes", QString(), QString::number(state.bytesLoaded) + '/' + QString::number(state.bytesCount), 0.);
|
? lang(lng_deleted)
|
||||||
|
: state.entityName),
|
||||||
|
(QString::number(state.itemIndex)
|
||||||
|
+ " / "
|
||||||
|
+ QString::number(state.itemCount)),
|
||||||
|
state.itemIndex / float64(state.itemCount));
|
||||||
}
|
}
|
||||||
|
pushBytes(
|
||||||
|
("file"
|
||||||
|
+ QString::number(state.entityIndex)
|
||||||
|
+ '_'
|
||||||
|
+ QString::number(state.itemIndex)),
|
||||||
|
state.bytesName);
|
||||||
break;
|
break;
|
||||||
default: Unexpected("Step in ContentFromState.");
|
default: Unexpected("Step in ContentFromState.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,14 @@ struct Content {
|
||||||
Content ContentFromState(const ProcessingState &state);
|
Content ContentFromState(const ProcessingState &state);
|
||||||
|
|
||||||
inline auto ContentFromState(rpl::producer<State> state) {
|
inline auto ContentFromState(rpl::producer<State> state) {
|
||||||
return std::move(
|
return rpl::single(Content()) | rpl::then(std::move(
|
||||||
state
|
state
|
||||||
) | rpl::filter([](const State &state) {
|
) | rpl::filter([](const State &state) {
|
||||||
return state.template is<ProcessingState>();
|
return state.template is<ProcessingState>();
|
||||||
}) | rpl::map([](const State &state) {
|
}) | rpl::map([](const State &state) {
|
||||||
return ContentFromState(
|
return ContentFromState(
|
||||||
state.template get_unchecked<ProcessingState>());
|
state.template get_unchecked<ProcessingState>());
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace View
|
} // namespace View
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "platform/platform_specific.h"
|
#include "platform/platform_specific.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
@ -24,6 +25,8 @@ DoneWidget::DoneWidget(QWidget *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoneWidget::setupContent() {
|
void DoneWidget::setupContent() {
|
||||||
|
initFooter();
|
||||||
|
|
||||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
|
||||||
const auto label = content->add(
|
const auto label = content->add(
|
||||||
|
@ -47,5 +50,38 @@ rpl::producer<> DoneWidget::showClicks() const {
|
||||||
return _showClicks.events();
|
return _showClicks.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> DoneWidget::closeClicks() const {
|
||||||
|
return _close->clicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoneWidget::initFooter() {
|
||||||
|
const auto buttonsPadding = st::boxButtonPadding;
|
||||||
|
const auto buttonsHeight = buttonsPadding.top()
|
||||||
|
+ st::defaultBoxButton.height
|
||||||
|
+ buttonsPadding.bottom();
|
||||||
|
const auto buttons = Ui::CreateChild<Ui::FixedHeightWidget>(
|
||||||
|
this,
|
||||||
|
buttonsHeight);
|
||||||
|
|
||||||
|
sizeValue(
|
||||||
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
|
buttons->resizeToWidth(size.width());
|
||||||
|
buttons->moveToLeft(0, size.height() - buttons->height());
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_close = Ui::CreateChild<Ui::RoundButton>(
|
||||||
|
buttons,
|
||||||
|
langFactory(lng_close),
|
||||||
|
st::defaultBoxButton);
|
||||||
|
_close->show();
|
||||||
|
|
||||||
|
buttons->widthValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto right = st::boxButtonPadding.right();
|
||||||
|
const auto top = st::boxButtonPadding.top();
|
||||||
|
_close->moveToRight(right, top);
|
||||||
|
}, _close->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace View
|
} // namespace View
|
||||||
} // namespace Export
|
} // namespace Export
|
||||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RoundButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
|
||||||
|
@ -17,12 +21,16 @@ public:
|
||||||
DoneWidget(QWidget *parent);
|
DoneWidget(QWidget *parent);
|
||||||
|
|
||||||
rpl::producer<> showClicks() const;
|
rpl::producer<> showClicks() const;
|
||||||
|
rpl::producer<> closeClicks() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initFooter();
|
||||||
void setupContent();
|
void setupContent();
|
||||||
|
|
||||||
rpl::event_stream<> _showClicks;
|
rpl::event_stream<> _showClicks;
|
||||||
|
|
||||||
|
QPointer<Ui::RoundButton> _close;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace View
|
} // namespace View
|
||||||
|
|
|
@ -46,9 +46,7 @@ void PanelController::showSettings() {
|
||||||
|
|
||||||
settings->startClicks(
|
settings->startClicks(
|
||||||
) | rpl::start_with_next([=](const Settings &settings) {
|
) | rpl::start_with_next([=](const Settings &settings) {
|
||||||
_panel->showInner(base::make_unique_q<ProgressWidget>(
|
showProgress();
|
||||||
_panel.get(),
|
|
||||||
ContentFromState(_process->state())));
|
|
||||||
_process->startExport(settings);
|
_process->startExport(settings);
|
||||||
}, settings->lifetime());
|
}, settings->lifetime());
|
||||||
|
|
||||||
|
@ -88,6 +86,36 @@ void PanelController::showError(const QString &text) {
|
||||||
_panel->showInner(std::move(container));
|
_panel->showInner(std::move(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PanelController::showProgress() {
|
||||||
|
auto progress = base::make_unique_q<ProgressWidget>(
|
||||||
|
_panel.get(),
|
||||||
|
ContentFromState(_process->state()));
|
||||||
|
|
||||||
|
progress->cancelClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_panel->hideGetDuration();
|
||||||
|
}, progress->lifetime());
|
||||||
|
|
||||||
|
_panel->showInner(std::move(progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PanelController::showDone(const QString &path) {
|
||||||
|
auto done = base::make_unique_q<DoneWidget>(_panel.get());
|
||||||
|
|
||||||
|
done->showClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
File::ShowInFolder(path);
|
||||||
|
_panel->hideGetDuration();
|
||||||
|
}, done->lifetime());
|
||||||
|
|
||||||
|
done->closeClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_panel->hideGetDuration();
|
||||||
|
}, done->lifetime());
|
||||||
|
|
||||||
|
_panel->showInner(std::move(done));
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<> PanelController::closed() const {
|
rpl::producer<> PanelController::closed() const {
|
||||||
return _panelCloseEvents.events(
|
return _panelCloseEvents.events(
|
||||||
) | rpl::flatten_latest(
|
) | rpl::flatten_latest(
|
||||||
|
@ -106,17 +134,7 @@ void PanelController::updateState(State &&state) {
|
||||||
} else if (const auto error = base::get_if<OutputErrorState>(&_state)) {
|
} else if (const auto error = base::get_if<OutputErrorState>(&_state)) {
|
||||||
showError(*error);
|
showError(*error);
|
||||||
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
|
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
|
||||||
const auto path = finished->path;
|
showDone(finished->path);
|
||||||
|
|
||||||
auto done = base::make_unique_q<DoneWidget>(_panel.get());
|
|
||||||
|
|
||||||
done->showClicks(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
File::ShowInFolder(path);
|
|
||||||
_panel->hideGetDuration();
|
|
||||||
}, done->lifetime());
|
|
||||||
|
|
||||||
_panel->showInner(std::move(done));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ private:
|
||||||
void createPanel();
|
void createPanel();
|
||||||
void updateState(State &&state);
|
void updateState(State &&state);
|
||||||
void showSettings();
|
void showSettings();
|
||||||
|
void showProgress();
|
||||||
|
void showDone(const QString &path);
|
||||||
void showError(const ApiErrorState &error);
|
void showError(const ApiErrorState &error);
|
||||||
void showError(const OutputErrorState &error);
|
void showError(const OutputErrorState &error);
|
||||||
void showError(const QString &text);
|
void showError(const QString &text);
|
||||||
|
|
|
@ -8,36 +8,298 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "export/view/export_view_progress.h"
|
#include "export/view/export_view_progress.h"
|
||||||
|
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/wrap/fade_wrap.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_export.h"
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
|
||||||
|
class ProgressWidget::Row : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
Row(QWidget *parent, Content::Row &&data);
|
||||||
|
|
||||||
|
void updateData(Content::Row &&data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Instance {
|
||||||
|
base::unique_qptr<Ui::FadeWrap<Ui::FlatLabel>> label;
|
||||||
|
base::unique_qptr<Ui::FadeWrap<Ui::FlatLabel>> info;
|
||||||
|
|
||||||
|
float64 value = 0.;
|
||||||
|
Animation progress;
|
||||||
|
|
||||||
|
bool hiding = true;
|
||||||
|
Animation opacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
void fillCurrentInstance();
|
||||||
|
void hideCurrentInstance();
|
||||||
|
void setInstanceProgress(Instance &instance, float64 progress);
|
||||||
|
void toggleInstance(Instance &data, bool shown);
|
||||||
|
void instanceOpacityCallback(QPointer<Ui::FlatLabel> label);
|
||||||
|
void removeOldInstance(QPointer<Ui::FlatLabel> label);
|
||||||
|
void paintInstance(Painter &p, const Instance &data);
|
||||||
|
|
||||||
|
void updateControlsGeometry(int newWidth);
|
||||||
|
void updateInstanceGeometry(const Instance &instance, int newWidth);
|
||||||
|
|
||||||
|
Content::Row _data;
|
||||||
|
Instance _current;
|
||||||
|
std::vector<Instance> _old;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressWidget::Row::Row(QWidget *parent, Content::Row &&data)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _data(std::move(data)) {
|
||||||
|
fillCurrentInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::updateData(Content::Row &&data) {
|
||||||
|
const auto wasId = _data.id;
|
||||||
|
const auto nowId = data.id;
|
||||||
|
_data = std::move(data);
|
||||||
|
if (nowId.isEmpty()) {
|
||||||
|
hideCurrentInstance();
|
||||||
|
} else if (wasId.isEmpty()) {
|
||||||
|
fillCurrentInstance();
|
||||||
|
} else {
|
||||||
|
_current.label->entity()->setText(_data.label);
|
||||||
|
_current.info->entity()->setText(_data.info);
|
||||||
|
setInstanceProgress(_current, _data.progress);
|
||||||
|
if (nowId != wasId) {
|
||||||
|
_current.progress.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateControlsGeometry(width());
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::fillCurrentInstance() {
|
||||||
|
_current.label = base::make_unique_q<Ui::FadeWrap<Ui::FlatLabel>>(
|
||||||
|
this,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
this,
|
||||||
|
_data.label,
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::exportProgressLabel));
|
||||||
|
_current.info = base::make_unique_q<Ui::FadeWrap<Ui::FlatLabel>>(
|
||||||
|
this,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
this,
|
||||||
|
_data.info,
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::exportProgressInfoLabel));
|
||||||
|
_current.label->hide(anim::type::instant);
|
||||||
|
_current.info->hide(anim::type::instant);
|
||||||
|
|
||||||
|
setInstanceProgress(_current, _data.progress);
|
||||||
|
toggleInstance(_current, true);
|
||||||
|
if (_data.id == "main") {
|
||||||
|
_current.opacity.finish();
|
||||||
|
_current.label->finishAnimating();
|
||||||
|
_current.info->finishAnimating();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::hideCurrentInstance() {
|
||||||
|
if (!_current.label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInstanceProgress(_current, 1.);
|
||||||
|
toggleInstance(_current, false);
|
||||||
|
_old.push_back(std::move(_current));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::setInstanceProgress(
|
||||||
|
Instance &instance,
|
||||||
|
float64 progress) {
|
||||||
|
if (_current.value < progress) {
|
||||||
|
_current.progress.start(
|
||||||
|
[=] { update(); },
|
||||||
|
_current.value,
|
||||||
|
progress,
|
||||||
|
st::exportProgressDuration,
|
||||||
|
anim::sineInOut);
|
||||||
|
} else if (_current.value > progress) {
|
||||||
|
_current.progress.finish();
|
||||||
|
}
|
||||||
|
_current.value = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::toggleInstance(Instance &instance, bool shown) {
|
||||||
|
Expects(instance.label != nullptr);
|
||||||
|
|
||||||
|
if (instance.hiding != shown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto label = make_weak(instance.label->entity());
|
||||||
|
instance.opacity.start(
|
||||||
|
[=] { instanceOpacityCallback(label); },
|
||||||
|
shown ? 0. : 1.,
|
||||||
|
shown ? 1. : 0.,
|
||||||
|
st::exportProgressDuration);
|
||||||
|
instance.hiding = !shown;
|
||||||
|
_current.label->toggle(shown, anim::type::normal);
|
||||||
|
_current.info->toggle(shown, anim::type::normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::instanceOpacityCallback(
|
||||||
|
QPointer<Ui::FlatLabel> label) {
|
||||||
|
update();
|
||||||
|
const auto i = ranges::find(_old, label, [](const Instance &instance) {
|
||||||
|
return make_weak(instance.label->entity());
|
||||||
|
});
|
||||||
|
if (i != end(_old) && i->hiding && !i->opacity.animating()) {
|
||||||
|
crl::on_main(this, [=] {
|
||||||
|
removeOldInstance(label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::removeOldInstance(QPointer<Ui::FlatLabel> label) {
|
||||||
|
const auto i = ranges::find(_old, label, [](const Instance &instance) {
|
||||||
|
return make_weak(instance.label->entity());
|
||||||
|
});
|
||||||
|
if (i != end(_old)) {
|
||||||
|
_old.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProgressWidget::Row::resizeGetHeight(int newWidth) {
|
||||||
|
updateControlsGeometry(newWidth);
|
||||||
|
return st::exportProgressRowHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
const auto thickness = st::exportProgressWidth;
|
||||||
|
const auto top = height() - thickness;
|
||||||
|
p.fillRect(0, top, width(), thickness, st::shadowFg);
|
||||||
|
|
||||||
|
for (const auto &instance : _old) {
|
||||||
|
paintInstance(p, instance);
|
||||||
|
}
|
||||||
|
paintInstance(p, _current);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::paintInstance(Painter &p, const Instance &data) {
|
||||||
|
const auto opacity = data.opacity.current(data.hiding ? 0. : 1.);
|
||||||
|
|
||||||
|
if (!opacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.setOpacity(opacity);
|
||||||
|
|
||||||
|
const auto thickness = st::exportProgressWidth;
|
||||||
|
const auto top = height() - thickness;
|
||||||
|
const auto till = qRound(data.progress.current(data.value) * width());
|
||||||
|
if (till > 0) {
|
||||||
|
p.fillRect(0, top, till, thickness, st::exportProgressFg);
|
||||||
|
}
|
||||||
|
if (till < width()) {
|
||||||
|
const auto left = width() - till;
|
||||||
|
p.fillRect(till, top, left, thickness, st::exportProgressBg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::updateControlsGeometry(int newWidth) {
|
||||||
|
updateInstanceGeometry(_current, newWidth);
|
||||||
|
for (const auto &instance : _old) {
|
||||||
|
updateInstanceGeometry(instance, newWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::Row::updateInstanceGeometry(
|
||||||
|
const Instance &instance,
|
||||||
|
int newWidth) {
|
||||||
|
if (!instance.label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance.info->resizeToNaturalWidth(newWidth);
|
||||||
|
instance.label->resizeToWidth(newWidth - instance.info->width());
|
||||||
|
instance.info->moveToRight(0, 0, newWidth);
|
||||||
|
instance.label->moveToLeft(0, 0, newWidth);
|
||||||
|
}
|
||||||
|
|
||||||
ProgressWidget::ProgressWidget(
|
ProgressWidget::ProgressWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<Content> content)
|
rpl::producer<Content> content)
|
||||||
: RpWidget(parent) {
|
: RpWidget(parent)
|
||||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(this, st::boxLabel);
|
, _body(this) {
|
||||||
sizeValue(
|
initFooter();
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
|
||||||
label->setGeometry(QRect(QPoint(), size));
|
widthValue(
|
||||||
}, label->lifetime());
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
_body->resizeToWidth(width);
|
||||||
|
_body->moveToLeft(0, 0);
|
||||||
|
}, _body->lifetime());
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
content
|
content
|
||||||
) | rpl::start_with_next([=](Content &&content) {
|
) | rpl::start_with_next([=](Content &&content) {
|
||||||
auto text = QString();
|
|
||||||
for (const auto &row : content.rows) {
|
|
||||||
text += row.id + ' ' + row.info + ' ' + row.label + '\n';
|
|
||||||
}
|
|
||||||
label->setText(text);
|
|
||||||
updateState(std::move(content));
|
updateState(std::move(content));
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ProgressWidget::cancelClicks() const {
|
||||||
|
return _cancel->clicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressWidget::initFooter() {
|
||||||
|
const auto buttonsPadding = st::boxButtonPadding;
|
||||||
|
const auto buttonsHeight = buttonsPadding.top()
|
||||||
|
+ st::defaultBoxButton.height
|
||||||
|
+ buttonsPadding.bottom();
|
||||||
|
const auto buttons = Ui::CreateChild<Ui::FixedHeightWidget>(
|
||||||
|
this,
|
||||||
|
buttonsHeight);
|
||||||
|
|
||||||
|
sizeValue(
|
||||||
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
|
buttons->resizeToWidth(size.width());
|
||||||
|
buttons->moveToLeft(0, size.height() - buttons->height());
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_cancel = Ui::CreateChild<Ui::RoundButton>(
|
||||||
|
buttons,
|
||||||
|
langFactory(lng_cancel),
|
||||||
|
st::defaultBoxButton);
|
||||||
|
_cancel->show();
|
||||||
|
|
||||||
|
buttons->widthValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto right = st::boxButtonPadding.right();
|
||||||
|
const auto top = st::boxButtonPadding.top();
|
||||||
|
_cancel->moveToRight(right, top);
|
||||||
|
}, _cancel->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
void ProgressWidget::updateState(Content &&content) {
|
void ProgressWidget::updateState(Content &&content) {
|
||||||
_content = std::move(content);
|
auto index = 0;
|
||||||
|
for (auto &row : content.rows) {
|
||||||
|
if (index < _rows.size()) {
|
||||||
|
_rows[index]->updateData(std::move(row));
|
||||||
|
} else {
|
||||||
|
_rows.push_back(_body->add(
|
||||||
|
object_ptr<Row>(this, std::move(row)),
|
||||||
|
st::exportProgressRowPadding));
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
for (const auto count = _rows.size(); index != count; ++index) {
|
||||||
|
_rows[index]->updateData(Content::Row());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressWidget::~ProgressWidget() = default;
|
ProgressWidget::~ProgressWidget() = default;
|
||||||
|
|
|
@ -10,6 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "export/view/export_view_content.h"
|
#include "export/view/export_view_content.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class VerticalLayout;
|
||||||
|
class RoundButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
|
||||||
|
@ -19,15 +24,21 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<Content> content);
|
rpl::producer<Content> content);
|
||||||
|
|
||||||
|
rpl::producer<> cancelClicks() const;
|
||||||
|
|
||||||
~ProgressWidget();
|
~ProgressWidget();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initFooter();
|
||||||
void updateState(Content &&content);
|
void updateState(Content &&content);
|
||||||
|
|
||||||
Content _content;
|
Content _content;
|
||||||
|
|
||||||
class Row;
|
class Row;
|
||||||
std::vector<base::unique_qptr<Row>> _rows;
|
object_ptr<Ui::VerticalLayout> _body;
|
||||||
|
std::vector<not_null<Row*>> _rows;
|
||||||
|
|
||||||
|
QPointer<Ui::RoundButton> _cancel;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue