Display export progress.

This commit is contained in:
John Preston 2018-06-19 19:31:30 +01:00
parent 5f01751660
commit 4115d3d13d
13 changed files with 544 additions and 78 deletions

View File

@ -571,6 +571,7 @@ void ApiWrap::finishUserpicsSlice() {
}
bool ApiWrap::loadUserpicProgress(FileProgress progress) {
Expects(_fileProcess != nullptr);
Expects(_userpicsProcess != nullptr);
Expects(_userpicsProcess->slice.has_value());
Expects((_userpicsProcess->fileIndex >= 0)
@ -578,6 +579,7 @@ bool ApiWrap::loadUserpicProgress(FileProgress progress) {
< _userpicsProcess->slice->list.size()));
return _userpicsProcess->fileProgress(DownloadProgress{
_fileProcess->relativePath,
_userpicsProcess->fileIndex,
progress.ready,
progress.total });
@ -886,12 +888,14 @@ void ApiWrap::finishMessagesSlice() {
}
bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
Expects(_fileProcess != nullptr);
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
Expects((_chatProcess->fileIndex >= 0)
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
return _chatProcess->fileProgress(DownloadProgress{
_fileProcess->relativePath,
_chatProcess->fileIndex,
progress.ready,
progress.total });
@ -996,7 +1000,6 @@ void ApiWrap::loadFile(
_fileProcess->progress = std::move(progress);
_fileProcess->done = std::move(done);
if (_fileProcess->progress) {
const auto progress = FileProgress{
_fileProcess->file.size(),

View File

@ -57,6 +57,7 @@ public:
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
struct DownloadProgress {
QString path;
int itemIndex = 0;
int ready = 0;
int total = 0;

View File

@ -68,11 +68,17 @@ private:
ProcessingState stateLeftChannelsList(int processed) const;
ProcessingState stateDialogsList(int processed) const;
ProcessingState statePersonalInfo() const;
ProcessingState stateUserpics(DownloadProgress progress) const;
ProcessingState stateUserpics(const DownloadProgress &progress) const;
ProcessingState stateContacts() const;
ProcessingState stateSessions() const;
ProcessingState stateLeftChannels(DownloadProgress progress) const;
ProcessingState stateDialogs(DownloadProgress progress) const;
ProcessingState stateLeftChannels(
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;
@ -96,7 +102,10 @@ private:
// rpl::variable<State> fails to compile in MSVC :(
State _state;
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::vector<Step> _steps;
@ -292,7 +301,7 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
push(Step::PersonalInfo, 1);
}
if (_settings.types & Settings::Type::Userpics) {
push(Step::Userpics, info.userpicsCount);
push(Step::Userpics, 1);
}
if (_settings.types & Settings::Type::Contacts) {
push(Step::Contacts, 1);
@ -306,8 +315,8 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
if (_settings.types & Settings::Type::AnyChatsMask) {
push(Step::Dialogs, info.dialogsCount);
}
_substepsInStep = std::make_shared<const std::vector<int>>(
std::move(result));
_substepsInStep = std::move(result);
_substepsTotal = ranges::accumulate(_substepsInStep, 0);
}
void Controller::exportNext() {
@ -512,10 +521,17 @@ template <typename Callback>
ProcessingState Controller::prepareState(
Step step,
Callback &&callback) const {
if (step != _lastProcessingStep) {
_substepsPassed += substepsInStep(_lastProcessingStep);
_lastProcessingStep = step;
}
auto result = ProcessingState();
callback(result);
result.step = step;
result.substepsInStep = _substepsInStep;
result.substepsPassed = _substepsPassed;
result.substepsNow = substepsInStep(_lastProcessingStep);
result.substepsTotal = _substepsTotal;
return result;
}
@ -524,10 +540,12 @@ ProcessingState Controller::stateInitializing() const {
}
ProcessingState Controller::stateLeftChannelsList(int processed) const {
const auto step = Step::LeftChannelsList;
return prepareState(step, [&](ProcessingState &result) {
return prepareState(Step::LeftChannelsList, [&](
ProcessingState &result) {
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;
return prepareState(step, [&](ProcessingState &result) {
result.entityIndex = processed;
result.entityCount = std::max(processed, substepsInStep(step));
result.entityCount = std::max(
processed,
substepsInStep(Step::Dialogs));
});
}
ProcessingState Controller::statePersonalInfo() const {
return prepareState(Step::PersonalInfo);
}
ProcessingState Controller::stateUserpics(DownloadProgress progress) const {
ProcessingState Controller::stateUserpics(
const DownloadProgress &progress) const {
return prepareState(Step::Userpics, [&](ProcessingState &result) {
result.entityIndex = _userpicsWritten + progress.itemIndex;
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.bytesCount = progress.total;
});
@ -560,35 +586,59 @@ ProcessingState Controller::stateSessions() const {
}
ProcessingState Controller::stateLeftChannels(
DownloadProgress progress) const {
const DownloadProgress & progress) const {
const auto step = Step::LeftChannels;
return prepareState(step, [&](ProcessingState &result) {
result.entityIndex = _leftChannelIndex;
result.entityCount = _leftChannelsInfo.list.size();
result.itemIndex = _messagesWritten + progress.itemIndex;
result.itemCount = std::max(_messagesCount, result.entityIndex);
result.bytesLoaded = progress.ready;
result.bytesCount = progress.total;
fillMessagesState(
result,
_leftChannelsInfo,
_leftChannelIndex,
progress);
});
}
ProcessingState Controller::stateDialogs(DownloadProgress progress) const {
ProcessingState Controller::stateDialogs(
const DownloadProgress &progress) const {
const auto step = Step::Dialogs;
return prepareState(step, [&](ProcessingState &result) {
result.entityIndex = _dialogIndex;
result.entityCount = _dialogsInfo.list.size();
result.itemIndex = _messagesWritten + progress.itemIndex;
result.itemCount = std::max(_messagesCount, result.entityIndex);
result.bytesLoaded = progress.ready;
result.bytesCount = progress.total;
fillMessagesState(
result,
_dialogsInfo,
_dialogIndex,
progress);
});
}
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 {
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() {

View File

@ -37,8 +37,8 @@ struct ProcessingState {
LeftChannels,
Dialogs,
};
enum class Item {
Other,
enum class FileType {
None,
Photo,
Video,
VoiceMessage,
@ -50,21 +50,21 @@ struct ProcessingState {
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 entityCount = 1;
QString entityName;
int itemIndex = 0;
int itemCount = 0;
Item itemType = Item::Other;
QString itemName;
QString itemId;
FileType bytesType = FileType::None;
QString bytesName;
int bytesLoaded = 0;
int bytesCount = 0;
QString objectId;
};

View File

@ -42,3 +42,23 @@ exportErrorLabel: FlatLabel(boxLabel) {
align: align(top);
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;

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "export/view/export_view_content.h"
#include "lang/lang_keys.h"
#include "layout.h"
namespace Export {
namespace View {
@ -23,26 +24,80 @@ Content ContentFromState(const ProcessingState &state) {
float64 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) {
case Step::Initializing:
pushMain(lang(lng_export_state_initializing));
break;
case Step::LeftChannelsList:
case Step::DialogsList:
pushMain(lang(lng_export_state_chats_list));
break;
case Step::PersonalInfo:
pushMain(lang(lng_export_option_info));
break;
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:
pushMain(lang(lng_export_option_contacts));
break;
case Step::Sessions:
pushMain(lang(lng_export_option_sessions));
break;
case Step::LeftChannels:
case Step::Dialogs:
push("init", lang(lng_export_state_initializing), QString(), 0.);
if (state.entityCount > 0) {
push("entity", QString(), QString::number(state.entityIndex) + '/' + QString::number(state.entityCount), 0.);
}
pushMain(lang(lng_export_state_chats));
if (state.itemCount > 0) {
push("item", QString(), QString::number(state.itemIndex) + '/' + QString::number(state.itemCount), 0.);
}
if (state.bytesCount > 0) {
push("bytes", QString(), QString::number(state.bytesLoaded) + '/' + QString::number(state.bytesCount), 0.);
push(
"chat" + QString::number(state.entityIndex),
(state.entityName.isEmpty()
? 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;
default: Unexpected("Step in ContentFromState.");
}

View File

@ -27,14 +27,14 @@ struct Content {
Content ContentFromState(const ProcessingState &state);
inline auto ContentFromState(rpl::producer<State> state) {
return std::move(
return rpl::single(Content()) | rpl::then(std::move(
state
) | rpl::filter([](const State &state) {
return state.template is<ProcessingState>();
}) | rpl::map([](const State &state) {
return ContentFromState(
state.template get_unchecked<ProcessingState>());
});
}));
}
} // namespace View

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/vertical_layout.h"
#include "platform/platform_specific.h"
#include "styles/style_widgets.h"
@ -24,6 +25,8 @@ DoneWidget::DoneWidget(QWidget *parent)
}
void DoneWidget::setupContent() {
initFooter();
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto label = content->add(
@ -47,5 +50,38 @@ rpl::producer<> DoneWidget::showClicks() const {
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 Export

View File

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
namespace Ui {
class RoundButton;
} // namespace Ui
namespace Export {
namespace View {
@ -17,12 +21,16 @@ public:
DoneWidget(QWidget *parent);
rpl::producer<> showClicks() const;
rpl::producer<> closeClicks() const;
private:
void initFooter();
void setupContent();
rpl::event_stream<> _showClicks;
QPointer<Ui::RoundButton> _close;
};
} // namespace View

View File

@ -46,9 +46,7 @@ void PanelController::showSettings() {
settings->startClicks(
) | rpl::start_with_next([=](const Settings &settings) {
_panel->showInner(base::make_unique_q<ProgressWidget>(
_panel.get(),
ContentFromState(_process->state())));
showProgress();
_process->startExport(settings);
}, settings->lifetime());
@ -88,6 +86,36 @@ void PanelController::showError(const QString &text) {
_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 {
return _panelCloseEvents.events(
) | rpl::flatten_latest(
@ -106,17 +134,7 @@ void PanelController::updateState(State &&state) {
} else if (const auto error = base::get_if<OutputErrorState>(&_state)) {
showError(*error);
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
const auto path = 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));
showDone(finished->path);
}
}

View File

@ -31,6 +31,8 @@ private:
void createPanel();
void updateState(State &&state);
void showSettings();
void showProgress();
void showDone(const QString &path);
void showError(const ApiErrorState &error);
void showError(const OutputErrorState &error);
void showError(const QString &text);

View File

@ -8,36 +8,298 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "export/view/export_view_progress.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_export.h"
namespace Export {
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(
QWidget *parent,
rpl::producer<Content> content)
: RpWidget(parent) {
const auto label = Ui::CreateChild<Ui::FlatLabel>(this, st::boxLabel);
sizeValue(
) | rpl::start_with_next([=](QSize size) {
label->setGeometry(QRect(QPoint(), size));
}, label->lifetime());
: RpWidget(parent)
, _body(this) {
initFooter();
widthValue(
) | rpl::start_with_next([=](int width) {
_body->resizeToWidth(width);
_body->moveToLeft(0, 0);
}, _body->lifetime());
std::move(
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));
}, 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) {
_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;

View File

@ -10,6 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "export/view/export_view_content.h"
namespace Ui {
class VerticalLayout;
class RoundButton;
} // namespace Ui
namespace Export {
namespace View {
@ -19,15 +24,21 @@ public:
QWidget *parent,
rpl::producer<Content> content);
rpl::producer<> cancelClicks() const;
~ProgressWidget();
private:
void initFooter();
void updateState(Content &&content);
Content _content;
class Row;
std::vector<base::unique_qptr<Row>> _rows;
object_ptr<Ui::VerticalLayout> _body;
std::vector<not_null<Row*>> _rows;
QPointer<Ui::RoundButton> _cancel;
};