| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mash/catalog_viewer/catalog_viewer.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "services/catalog/public/mojom/catalog.mojom.h" |
| #include "services/catalog/public/mojom/constants.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "ui/base/models/table_model.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/resources/grit/ui_resources.h" |
| #include "ui/views/background.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/table/table_view.h" |
| #include "ui/views/controls/table/table_view_observer.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #include "ui/views/controls/textfield/textfield_controller.h" |
| #include "ui/views/layout/grid_layout.h" |
| #include "ui/views/mus/aura_init.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| namespace mash { |
| namespace catalog_viewer { |
| namespace { |
| |
| class CatalogViewerContents : public views::WidgetDelegateView, |
| public ui::TableModel, |
| public views::TextfieldController { |
| public: |
| CatalogViewerContents(CatalogViewer* catalog_viewer, |
| catalog::mojom::CatalogPtr catalog) |
| : catalog_viewer_(catalog_viewer), |
| catalog_(std::move(catalog)), |
| table_view_(nullptr), |
| table_view_parent_(nullptr), |
| observer_(nullptr), |
| capability_(new views::Textfield) { |
| constexpr int kPadding = 5; |
| SetBorder(views::CreateEmptyBorder(gfx::Insets(kPadding))); |
| SetBackground(views::CreateStandardPanelBackground()); |
| |
| views::GridLayout* layout = |
| SetLayoutManager(std::make_unique<views::GridLayout>(this)); |
| |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddPaddingColumn(0, kPadding); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| views::GridLayout::USE_PREF, 0, 0); |
| |
| layout->StartRow(0, 0); |
| layout->AddView(new views::Label(base::WideToUTF16(L"Capability:"))); |
| layout->AddView(capability_); |
| capability_->set_controller(this); |
| |
| layout->StartRowWithPadding(1, 0, 0, kPadding); |
| table_view_ = |
| new views::TableView(this, GetColumns(), views::TEXT_ONLY, false); |
| table_view_parent_ = table_view_->CreateParentIfNecessary(); |
| layout->AddView(table_view_parent_, 3, 1, views::GridLayout::FILL, |
| views::GridLayout::FILL); |
| |
| GetAllEntries(); |
| } |
| ~CatalogViewerContents() override { |
| table_view_->SetModel(nullptr); |
| catalog_viewer_->RemoveWindow(GetWidget()); |
| } |
| |
| private: |
| struct Entry { |
| Entry(const std::string& name, const std::string& url) |
| : name(name), url(url) {} |
| std::string name; |
| std::string url; |
| }; |
| |
| |
| // Overridden from views::WidgetDelegate: |
| base::string16 GetWindowTitle() const override { |
| // TODO(beng): use resources. |
| return base::ASCIIToUTF16("Applications"); |
| } |
| bool CanResize() const override { return true; } |
| bool CanMaximize() const override { return true; } |
| bool CanMinimize() const override { return true; } |
| |
| gfx::ImageSkia GetWindowAppIcon() override { |
| // TODO(jamescook): Create a new .pak file for this app and make a custom |
| // icon, perhaps one that looks like the Chrome OS task viewer icon. |
| ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_SETTINGS); |
| } |
| |
| // Overridden from ui::TableModel: |
| int RowCount() override { |
| return static_cast<int>(entries_.size()); |
| } |
| base::string16 GetText(int row, int column_id) override { |
| switch (column_id) { |
| case 0: |
| DCHECK(row < static_cast<int>(entries_.size())); |
| return base::UTF8ToUTF16(entries_[row].name); |
| case 1: |
| DCHECK(row < static_cast<int>(entries_.size())); |
| return base::UTF8ToUTF16(entries_[row].url); |
| default: |
| NOTREACHED(); |
| break; |
| } |
| return base::string16(); |
| } |
| |
| // Overriden from views::TextFieldController: |
| bool HandleKeyEvent(views::Textfield* sender, |
| const ui::KeyEvent& key_event) override { |
| if (key_event.type() != ui::ET_KEY_PRESSED || |
| key_event.key_code() != ui::VKEY_RETURN) |
| return false; |
| |
| if (sender->text().length()) { |
| catalog_->GetEntriesProvidingCapability( |
| base::UTF16ToUTF8(sender->text()), |
| base::Bind(&CatalogViewerContents::OnReceivedEntries, |
| base::Unretained(this))); |
| } else { |
| GetAllEntries(); |
| } |
| |
| return true; |
| } |
| |
| void GetAllEntries() { |
| // We don't want to show an empty UI so we just block until we have all the |
| // data. GetEntries is a sync call. |
| std::vector<catalog::mojom::EntryPtr> entries; |
| if (catalog_->GetEntries(base::nullopt, &entries)) |
| UpdateEntries(entries); |
| } |
| |
| void OnReceivedEntries(std::vector<catalog::mojom::EntryPtr> entries) { |
| UpdateEntries(entries); |
| } |
| |
| void UpdateEntries(const std::vector<catalog::mojom::EntryPtr>& entries) { |
| entries_.clear(); |
| for (auto& entry : entries) |
| entries_.push_back(Entry(entry->display_name, entry->name)); |
| observer_->OnModelChanged(); |
| } |
| |
| void SetObserver(ui::TableModelObserver* observer) override { |
| observer_ = observer; |
| } |
| |
| static std::vector<ui::TableColumn> GetColumns() { |
| std::vector<ui::TableColumn> columns; |
| |
| ui::TableColumn name_column; |
| name_column.id = 0; |
| // TODO(beng): use resources. |
| name_column.title = base::ASCIIToUTF16("Name"); |
| name_column.width = -1; |
| name_column.percent = 0.4f; |
| name_column.sortable = true; |
| columns.push_back(name_column); |
| |
| ui::TableColumn url_column; |
| url_column.id = 1; |
| // TODO(beng): use resources. |
| url_column.title = base::ASCIIToUTF16("URL"); |
| url_column.width = -1; |
| url_column.percent = 0.4f; |
| url_column.sortable = true; |
| columns.push_back(url_column); |
| |
| return columns; |
| } |
| |
| CatalogViewer* catalog_viewer_; |
| catalog::mojom::CatalogPtr catalog_; |
| |
| views::TableView* table_view_; |
| views::View* table_view_parent_; |
| ui::TableModelObserver* observer_; |
| views::Textfield* capability_; |
| |
| std::vector<Entry> entries_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CatalogViewerContents); |
| }; |
| |
| } // namespace |
| |
| CatalogViewer::CatalogViewer(service_manager::mojom::ServiceRequest request) |
| : service_binding_(this, std::move(request)) { |
| registry_.AddInterface<mojom::Launchable>( |
| base::Bind(&CatalogViewer::Create, base::Unretained(this))); |
| } |
| CatalogViewer::~CatalogViewer() = default; |
| |
| void CatalogViewer::RemoveWindow(views::Widget* window) { |
| auto it = std::find(windows_.begin(), windows_.end(), window); |
| DCHECK(it != windows_.end()); |
| windows_.erase(it); |
| if (windows_.empty()) |
| Terminate(); |
| } |
| |
| void CatalogViewer::OnStart() { |
| views::AuraInit::InitParams params; |
| params.connector = service_binding_.GetConnector(); |
| params.identity = service_binding_.identity(); |
| aura_init_ = views::AuraInit::Create(params); |
| if (!aura_init_) |
| Terminate(); |
| } |
| |
| void CatalogViewer::OnBindInterface( |
| const service_manager::BindSourceInfo& source_info, |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle interface_pipe) { |
| registry_.BindInterface(interface_name, std::move(interface_pipe)); |
| } |
| |
| void CatalogViewer::Launch(uint32_t what, mojom::LaunchMode how) { |
| bool reuse = how == mojom::LaunchMode::REUSE || |
| how == mojom::LaunchMode::DEFAULT; |
| if (reuse && !windows_.empty()) { |
| windows_.back()->Activate(); |
| return; |
| } |
| catalog::mojom::CatalogPtr catalog; |
| service_binding_.GetConnector()->BindInterface(catalog::mojom::kServiceName, |
| &catalog); |
| |
| views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( |
| new CatalogViewerContents(this, std::move(catalog)), nullptr, |
| gfx::Rect(25, 25, 500, 600)); |
| window->Show(); |
| windows_.push_back(window); |
| } |
| |
| void CatalogViewer::Create(mojom::LaunchableRequest request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| } // namespace catalog_viewer |
| } // namespace mash |