#include "launcher_app.h" #include "ui/fonts.h" #include "ui/icons.h" #include "ui/widgets.h" namespace roro { void LauncherApp::onEnter() { int count = static_cast(manager_->visibleApps().size()); list_.setCount(count); grid_.setCount(count); badges_ = badges(); } bool LauncherApp::onKey(const KeyEvent& e) { bool list = asList(); switch (e.key) { case Key::Up: list ? list_.up() : grid_.up(); break; case Key::Down: list ? list_.down() : grid_.down(); break; case Key::Left: if (list) return false; grid_.left(); break; case Key::Right: if (list) return false; grid_.right(); break; case Key::Select: if (list_.selected() >= 0) manager_->open(manager_->visibleApps()[list_.selected()]->id); return true; default: return false; } // The view that didn't move follows. if (list) grid_.select(list_.selected()); else list_.select(grid_.selected()); requestRedraw(); return true; } uint32_t LauncherApp::badges() const { uint32_t bits = 0; auto apps = manager_->visibleApps(); for (size_t i = 0; i < apps.size() && i < 32; ++i) if (apps[i]->app->badge()) bits |= 1u << i; return bits; } void LauncherApp::update(uint32_t) { uint32_t now = badges(); if (now == badges_) return; badges_ = now; requestRedraw(); } void LauncherApp::draw(Canvas& c) { if (!asList()) return drawGrid(c); auto apps = manager_->visibleApps(); widgets::list(c, list_, theme::kContent, [&](int i) { return std::string(apps[i]->title); }, [&](int i) { return std::string(badges_ & (1u << i) ? "*" : ""); }); } void LauncherApp::drawGrid(Canvas& c) { using namespace theme; auto apps = manager_->visibleApps(); int selected = grid_.selected(); for (int row = 0; row < kRows; ++row) { for (int col = 0; col < kCols; ++col) { int i = (grid_.firstVisibleRow() + row) * kCols + col; if (i >= static_cast(apps.size())) break; int x = kContent.x + col * kTileW, y = kContent.y + row * kTileH; int ix = x + (kTileW - icons::kSize) / 2, iy = y + (kTileH - icons::kSize) / 2; // As on the website: cyan on black, and the chosen one dark on a cyan tile with notched corners. uint16_t ink = i == selected ? kOnAccent : kAccent; if (i == selected) { int tx = ix - 6, tw = icons::kSize + 12; c.fillRect(tx + 4, y, tw - 8, kTileH, kAccent); c.fillRect(tx + 2, y + 2, tw - 4, kTileH - 4, kAccent); c.fillRect(tx, y + 4, tw, kTileH - 8, kAccent); } if (apps[i]->icon) { c.drawBitmap(ix, iy, apps[i]->icon, icons::kSize, icons::kSize, ink); } else { // an App without a picture: its first letter, framed c.drawRect(ix + 2, iy + 2, icons::kSize - 4, icons::kSize - 4, ink); c.setFont(&fonts::large); c.setTextColor(ink); c.setTextDatum(middle_center); c.drawString(std::string(1, apps[i]->title[0]).c_str(), ix + icons::kSize / 2, iy + icons::kSize / 2); } if (badges_ & (1u << i)) { // a square dot on the icon's corner, with a margin to stand out from it c.fillRect(ix + icons::kSize - 6, iy - 1, 10, 10, i == selected ? kAccent : kBackground); c.fillRect(ix + icons::kSize - 4, iy + 1, 6, 6, kWarning); } } } // More rows than fit: where these are among them. if (grid_.rows() > kRows) { int h = kRows * kTileH, barH = h * kRows / grid_.rows(); c.fillRect(kContent.x + kContent.w - 2, kContent.y + (h - barH) * grid_.firstVisibleRow() / (grid_.rows() - kRows), 2, barH, kMuted); } if (selected >= 0) { c.setFont(&fonts::bold); c.setTextColor(kAccent); c.setTextDatum(top_center); c.drawString(apps[selected]->title, kContent.x + kContent.w / 2, kContent.y + kRows * kTileH + 1); } c.setTextDatum(top_left); } } // namespace roro