Squash commits for public release
This commit is contained in:
24
userland/system/applist/AppDelegate.cpp
Normal file
24
userland/system/applist/AppDelegate.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "AppListView.h"
|
||||
#include "AppListViewController.h"
|
||||
#include "AppListWindow.h"
|
||||
#include <libui/AppDelegate.h>
|
||||
|
||||
class AppDelegate : public UI::AppDelegate {
|
||||
public:
|
||||
AppDelegate() = default;
|
||||
virtual ~AppDelegate() = default;
|
||||
|
||||
LG::Size preferred_desktop_window_size() const override { return LG::Size(320, 400); }
|
||||
|
||||
bool application() override
|
||||
{
|
||||
auto& window = std::xos::construct<AppListWindow>(window_size());
|
||||
window.set_bitmap_format(LG::PixelBitmapFormat::RGBA);
|
||||
auto& dock_view = window.create_superview<AppListView, AppListViewController>();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
SET_APP_DELEGATE(AppDelegate);
|
||||
27
userland/system/applist/AppEntity.h
Normal file
27
userland/system/applist/AppEntity.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class AppEntity {
|
||||
public:
|
||||
AppEntity() = default;
|
||||
|
||||
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
|
||||
const LG::PixelBitmap& icon() const { return m_icon; }
|
||||
|
||||
void set_title(const std::string& title) { m_title = title; }
|
||||
const std::string& title() const { return m_title; }
|
||||
|
||||
void set_path_to_exec(const std::string& path) { m_path_to_exec = path; }
|
||||
const std::string& path_to_exec() const { return m_path_to_exec; }
|
||||
|
||||
void set_bundle_id(const std::string& bid) { m_bundle_id = bid; }
|
||||
const std::string& bundle_id() const { return m_bundle_id; }
|
||||
|
||||
private:
|
||||
LG::PixelBitmap m_icon;
|
||||
std::string m_title {};
|
||||
std::string m_path_to_exec {};
|
||||
std::string m_bundle_id {};
|
||||
};
|
||||
99
userland/system/applist/AppListView.cpp
Normal file
99
userland/system/applist/AppListView.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "AppListView.h"
|
||||
#include "IconView.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/KeyboardMapping.h>
|
||||
#include <libg/Color.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/CollectionView.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/StackView.h>
|
||||
#include <libui/TextField.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static AppListView* this_view;
|
||||
|
||||
AppListView::AppListView(UI::View* superview, const LG::Rect& frame)
|
||||
: UI::View(superview, frame)
|
||||
{
|
||||
// Not used any more, reinit
|
||||
// auto& dock_stack_view = add_subview<UI::StackView>(bounds());
|
||||
// dock_stack_view.set_spacing(padding());
|
||||
// dock_stack_view.set_background_color(LG::Color::Opaque);
|
||||
// dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
|
||||
// dock_stack_view.set_distribution(UI::StackView::Distribution::EqualCentering);
|
||||
// m_dock_stackview = &dock_stack_view;
|
||||
}
|
||||
|
||||
AppListView::AppListView(UI::View* superview, UI::Window* window, const LG::Rect& frame)
|
||||
: UI::View(superview, window, frame)
|
||||
{
|
||||
// Use manual layouting since layout is really simple here.
|
||||
auto& header = add_subview<UI::View>(LG::Rect(0, 0, bounds().width(), 30 + 2 * padding()));
|
||||
header.set_background_color(LG::Color::White);
|
||||
header.layer().set_corner_mask(LG::CornerMask(8, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
|
||||
|
||||
auto& searchbar = add_subview<UI::TextField>(LG::Rect(padding(), padding(), bounds().width() - padding() * 2, 30));
|
||||
searchbar.layer().set_corner_mask(LG::CornerMask(8));
|
||||
searchbar.set_background_color(LG::Color(0xE7F0FA));
|
||||
searchbar.set_placeholder_text("SEARCH");
|
||||
|
||||
auto& applist_grid_view = add_subview<UI::CollectionView>(LG::Rect(0, 2 * padding() + 30, bounds().width(), bounds().height() - (2 * padding() + 30)));
|
||||
applist_grid_view.set_data_source([this](int id) -> View* {
|
||||
return this->view_streamer(id);
|
||||
});
|
||||
m_applist_grid_view = &applist_grid_view;
|
||||
}
|
||||
|
||||
void AppListView::display(const LG::Rect& rect)
|
||||
{
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
ctx.set_fill_color(background_color());
|
||||
ctx.fill_rounded(bounds(), layer().corner_mask());
|
||||
|
||||
ctx.set_fill_color(LG::Color(120, 120, 120, 50));
|
||||
ctx.draw_shading(LG::Rect(0, 30 + 2 * padding(), bounds().width(), 4), LG::Shading(LG::Shading::Type::TopToBottom));
|
||||
}
|
||||
|
||||
UI::View* AppListView::view_streamer(int id)
|
||||
{
|
||||
if (id * items_per_row() >= m_app_entities.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t calc_padding = (m_applist_grid_view->bounds().width() - 2 * padding() - (icon_view_size() * items_per_row())) / (items_per_row() - 1);
|
||||
UI::StackView& dock_stack_view = m_applist_grid_view->add_subview<UI::StackView>(LG::Rect(padding(), 0, m_applist_grid_view->bounds().width() - 2 * padding(), icon_view_size() + calc_padding));
|
||||
dock_stack_view.set_spacing(calc_padding);
|
||||
dock_stack_view.set_background_color(LG::Color::Opaque);
|
||||
dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
|
||||
dock_stack_view.set_distribution(UI::StackView::Distribution::Standard);
|
||||
|
||||
size_t rem = std::min(items_per_row(), (int)m_app_entities.size() - id * items_per_row());
|
||||
for (int i = 0; i < rem; i++) {
|
||||
auto& icon_view = dock_stack_view.add_arranged_subview<IconView>();
|
||||
icon_view.set_title(m_app_entities[id * items_per_row() + i].title());
|
||||
icon_view.entity() = m_app_entities[id * items_per_row() + i];
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
}
|
||||
|
||||
return &dock_stack_view;
|
||||
}
|
||||
|
||||
void AppListView::register_entity(const AppEntity& ent)
|
||||
{
|
||||
m_app_entities.push_back(ent);
|
||||
m_applist_grid_view->reload_data();
|
||||
m_applist_grid_view->invalidate_row((m_app_entities.size() - 1) / items_per_row());
|
||||
}
|
||||
|
||||
void AppListView::register_entity(AppEntity&& ent)
|
||||
{
|
||||
m_app_entities.push_back(std::move(ent));
|
||||
m_applist_grid_view->reload_data();
|
||||
m_applist_grid_view->invalidate_row((m_app_entities.size() - 1) / items_per_row());
|
||||
}
|
||||
32
userland/system/applist/AppListView.h
Normal file
32
userland/system/applist/AppListView.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "AppEntity.h"
|
||||
#include "IconView.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/CollectionView.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class AppListView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
AppListView(UI::View* superview, const LG::Rect& frame);
|
||||
AppListView(UI::View* superview, UI::Window* window, const LG::Rect& frame);
|
||||
|
||||
static constexpr size_t padding() { return 16; }
|
||||
static constexpr int icon_size() { return 32; }
|
||||
static constexpr int icon_view_size() { return (int)64; }
|
||||
static constexpr int items_per_row() { return (int)4; }
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
|
||||
void register_entity(const AppEntity& ent);
|
||||
void register_entity(AppEntity&& ent);
|
||||
|
||||
private:
|
||||
UI::View* view_streamer(int id);
|
||||
|
||||
UI::CollectionView* m_applist_grid_view {};
|
||||
std::vector<AppEntity> m_app_entities {};
|
||||
};
|
||||
82
userland/system/applist/AppListViewController.h
Normal file
82
userland/system/applist/AppListViewController.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "AppListView.h"
|
||||
#include <libfoundation/FileManager.h>
|
||||
#include <libfoundation/json/Parser.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/Button.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/View.h>
|
||||
#include <libui/ViewController.h>
|
||||
#include <libui/Window.h>
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
|
||||
char contentdir[256];
|
||||
|
||||
class AppListViewController : public UI::ViewController<AppListView> {
|
||||
public:
|
||||
AppListViewController(AppListView& view)
|
||||
: UI::ViewController<AppListView>(view)
|
||||
{
|
||||
}
|
||||
virtual ~AppListViewController() = default;
|
||||
|
||||
void load_application(const std::string& content_dir)
|
||||
{
|
||||
auto json_parser = LFoundation::Json::Parser(content_dir + "info.json");
|
||||
LFoundation::Json::Object* jobj_root = json_parser.object();
|
||||
if (jobj_root->invalid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* jdict_root = jobj_root->cast_to<LFoundation::Json::DictObject>();
|
||||
const std::string& bundle_id = jdict_root->data()["bundle_id"]->cast_to<LFoundation::Json::StringObject>()->data();
|
||||
|
||||
AppEntity new_ent;
|
||||
LG::PNG::PNGLoader loader;
|
||||
|
||||
std::string icon_path = jdict_root->data()["icon_path"]->cast_to<LFoundation::Json::StringObject>()->data();
|
||||
new_ent.set_icon(loader.load_from_file(icon_path + "/32x32.png"));
|
||||
|
||||
std::string rel_exec_path = jdict_root->data()["exec_rel_path"]->cast_to<LFoundation::Json::StringObject>()->data();
|
||||
new_ent.set_path_to_exec(content_dir + rel_exec_path);
|
||||
|
||||
new_ent.set_title(jdict_root->data()["name"]->cast_to<LFoundation::Json::StringObject>()->data());
|
||||
new_ent.set_bundle_id(jdict_root->data()["bundle_id"]->cast_to<LFoundation::Json::StringObject>()->data());
|
||||
view().register_entity(std::move(new_ent));
|
||||
|
||||
delete jdict_root;
|
||||
}
|
||||
|
||||
void load_application_list()
|
||||
{
|
||||
auto local_fm = LFoundation::FileManager();
|
||||
local_fm.foreach_object("/Applications", [this](const char* name) {
|
||||
sprintf(contentdir, "/Applications/%s/Content/", name);
|
||||
load_application(contentdir);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void view_did_load() override
|
||||
{
|
||||
view().set_background_color(LG::Color::LightSystemOpaque);
|
||||
view().layer().set_corner_mask(LG::CornerMask(4, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
|
||||
load_application_list();
|
||||
#if 0
|
||||
for (int i = 0; i < 32; i++) {
|
||||
AppEntity new_ent;
|
||||
LG::PNG::PNGLoader loader;
|
||||
|
||||
new_ent.set_icon(loader.load_from_file("/res/icons/apps/about.icon/32x32.png"));
|
||||
new_ent.set_path_to_exec("/Applications/about.app/Content/about");
|
||||
new_ent.set_title("TestApp");
|
||||
new_ent.set_bundle_id("com.x.test");
|
||||
view().register_entity(std::move(new_ent));
|
||||
}
|
||||
#endif
|
||||
view().set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
7
userland/system/applist/AppListWindow.cpp
Normal file
7
userland/system/applist/AppListWindow.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "AppListWindow.h"
|
||||
#include "AppListView.h"
|
||||
|
||||
void AppListWindow::receive_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
Window::receive_event(std::move(event));
|
||||
}
|
||||
15
userland/system/applist/AppListWindow.h
Normal file
15
userland/system/applist/AppListWindow.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <libg/Size.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
class AppListWindow : public UI::Window {
|
||||
public:
|
||||
AppListWindow(const LG::Size& size)
|
||||
: UI::Window("AppList", size, UI::WindowType::AppList)
|
||||
{
|
||||
}
|
||||
|
||||
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
|
||||
};
|
||||
18
userland/system/applist/BUILD.gn
Normal file
18
userland/system/applist/BUILD.gn
Normal file
@@ -0,0 +1,18 @@
|
||||
import("//build/userland/TEMPLATE.gni")
|
||||
|
||||
xOS_executable("applist") {
|
||||
install_path = "System/"
|
||||
sources = [
|
||||
"AppDelegate.cpp",
|
||||
"AppListView.cpp",
|
||||
"AppListWindow.cpp",
|
||||
"IconView.cpp",
|
||||
]
|
||||
configs = [ "//build/userland:userland_flags" ]
|
||||
deplibs = [
|
||||
"libcxx",
|
||||
"libfoundation",
|
||||
"libg",
|
||||
"libui",
|
||||
]
|
||||
}
|
||||
38
userland/system/applist/IconView.cpp
Normal file
38
userland/system/applist/IconView.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "IconView.h"
|
||||
#include "AppListView.h"
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
IconView::IconView(View* superview, const LG::Rect& frame)
|
||||
: View(superview, frame)
|
||||
{
|
||||
m_label = &add_subview<UI::Label>(LG::Rect(0, AppListView::icon_view_size() - 12, AppListView::icon_view_size(), 12));
|
||||
m_label->set_alignment(UI::Text::Alignment::Center);
|
||||
}
|
||||
|
||||
void IconView::display(const LG::Rect& rect)
|
||||
{
|
||||
const int padding = 4;
|
||||
const int offset_x = (AppListView::icon_view_size() - AppListView::icon_size()) / 2;
|
||||
const int offset_y = (AppListView::icon_view_size() - AppListView::icon_size()) / 2;
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
auto icon_rect = LG::Rect(offset_x, offset_y, AppListView::icon_size(), AppListView::icon_size());
|
||||
ctx.draw(icon_rect.origin(), m_launch_entity.icon());
|
||||
if (is_hovered()) {
|
||||
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
|
||||
ctx.fill(icon_rect);
|
||||
}
|
||||
|
||||
ctx.set_fill_color(LG::Color(120, 129, 133, 40));
|
||||
ctx.draw_box_shading(icon_rect, LG::Shading(LG::Shading::Type::Box, 0, 3), LG::CornerMask(6));
|
||||
}
|
||||
|
||||
void IconView::on_click()
|
||||
{
|
||||
launch();
|
||||
}
|
||||
60
userland/system/applist/IconView.h
Normal file
60
userland/system/applist/IconView.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "AppEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/PopupMenu.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
class IconView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
IconView(View* superview, const LG::Rect& frame);
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
void mouse_up() override { on_click(); }
|
||||
|
||||
static constexpr size_t underline_height() { return 4; }
|
||||
|
||||
void set_title(const std::string& title)
|
||||
{
|
||||
if (!m_label) {
|
||||
return;
|
||||
}
|
||||
m_label->set_text(title);
|
||||
}
|
||||
|
||||
AppEntity& entity() { return m_launch_entity; }
|
||||
const AppEntity& entity() const { return m_launch_entity; }
|
||||
|
||||
virtual void mouse_entered(const LG::Point<int>& location) override
|
||||
{
|
||||
View::mouse_entered(location);
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
virtual void mouse_exited() override
|
||||
{
|
||||
View::mouse_exited();
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
void on_click();
|
||||
void launch()
|
||||
{
|
||||
if (fork() == 0) {
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
execlp(m_launch_entity.path_to_exec().c_str(), m_launch_entity.path_to_exec().c_str(), NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
UI::Label* m_label;
|
||||
AppEntity m_launch_entity;
|
||||
};
|
||||
5
userland/system/dock/.info.mk
Normal file
5
userland/system/dock/.info.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
APPS += DOCK
|
||||
|
||||
DOCK_NAME = dock
|
||||
DOCK_LIBS = cxx ui
|
||||
DOCK_INSTALL_PATH = bin/
|
||||
24
userland/system/dock/AppDelegate.cpp
Normal file
24
userland/system/dock/AppDelegate.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "DockView.h"
|
||||
#include "DockViewController.h"
|
||||
#include "DockWindow.h"
|
||||
#include <libui/AppDelegate.h>
|
||||
|
||||
class AppDelegate : public UI::AppDelegate {
|
||||
public:
|
||||
AppDelegate() = default;
|
||||
virtual ~AppDelegate() = default;
|
||||
|
||||
LG::Size preferred_desktop_window_size() const override { return LG::Size(400, 300); }
|
||||
|
||||
bool application() override
|
||||
{
|
||||
auto& window = std::xos::construct<DockWindow>();
|
||||
window.set_bitmap_format(LG::PixelBitmapFormat::RGBA);
|
||||
auto& dock_view = window.create_superview<DockView, DockViewController>();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
SET_APP_DELEGATE(AppDelegate);
|
||||
44
userland/system/dock/AppListView.cpp
Normal file
44
userland/system/dock/AppListView.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "AppListView.h"
|
||||
#include "DockView.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
AppListView::AppListView(View* superview, const LG::Rect& frame)
|
||||
: View(superview, frame)
|
||||
{
|
||||
LG::PNG::PNGLoader loader;
|
||||
m_icon = loader.load_from_file("/res/system/app_list_32.png");
|
||||
}
|
||||
|
||||
void AppListView::display(const LG::Rect& rect)
|
||||
{
|
||||
const int padding = 4;
|
||||
const int offset_x = (DockView::icon_view_size() - m_icon.width()) / 2;
|
||||
const int offset_y = (DockView::icon_view_size() - m_icon.height()) / 2;
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
auto icon_rect = LG::Rect(offset_x, offset_y, DockView::icon_size(), DockView::icon_size());
|
||||
ctx.draw(icon_rect.origin(), m_icon);
|
||||
if (is_hovered()) {
|
||||
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
|
||||
ctx.fill(icon_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void AppListView::on_click()
|
||||
{
|
||||
int this_window_id = window()->id();
|
||||
|
||||
if (m_target_window_id == INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& app = UI::App::the();
|
||||
AskBringToFrontMessage msg(app.connection().key(), this_window_id, m_target_window_id);
|
||||
app.connection().send_async_message(msg);
|
||||
}
|
||||
41
userland/system/dock/AppListView.h
Normal file
41
userland/system/dock/AppListView.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "DockEntity.h"
|
||||
#include "WindowEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/PopupMenu.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
class AppListView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
static constexpr int INVALID = -1;
|
||||
|
||||
public:
|
||||
AppListView(View* superview, const LG::Rect& frame);
|
||||
|
||||
void set_target_window_id(int winid) { m_target_window_id = winid; }
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
void mouse_up() override { on_click(); }
|
||||
virtual void mouse_entered(const LG::Point<int>& location) override
|
||||
{
|
||||
View::mouse_entered(location);
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
virtual void mouse_exited() override
|
||||
{
|
||||
View::mouse_exited();
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
void on_click();
|
||||
|
||||
int m_target_window_id { INVALID };
|
||||
LG::PixelBitmap m_icon;
|
||||
};
|
||||
19
userland/system/dock/BUILD.gn
Normal file
19
userland/system/dock/BUILD.gn
Normal file
@@ -0,0 +1,19 @@
|
||||
import("//build/userland/TEMPLATE.gni")
|
||||
|
||||
xOS_executable("dock") {
|
||||
install_path = "System/"
|
||||
sources = [
|
||||
"AppDelegate.cpp",
|
||||
"AppListView.cpp",
|
||||
"DockView.cpp",
|
||||
"DockWindow.cpp",
|
||||
"IconView.cpp",
|
||||
]
|
||||
configs = [ "//build/userland:userland_flags" ]
|
||||
deplibs = [
|
||||
"libcxx",
|
||||
"libfoundation",
|
||||
"libg",
|
||||
"libui",
|
||||
]
|
||||
}
|
||||
29
userland/system/dock/DockEntity.h
Normal file
29
userland/system/dock/DockEntity.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "WindowEntity.h"
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class DockEntity {
|
||||
public:
|
||||
DockEntity() = default;
|
||||
|
||||
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
|
||||
const LG::PixelBitmap& icon() const { return m_icon; }
|
||||
|
||||
void set_path_to_exec(const std::string& path) { m_path_to_exec = path; }
|
||||
const std::string& path_to_exec() const { return m_path_to_exec; }
|
||||
|
||||
void set_bundle_id(const std::string& bid) { m_bundle_id = bid; }
|
||||
const std::string& bundle_id() const { return m_bundle_id; }
|
||||
|
||||
void add_window(const WindowEntity& went) { m_windows.push_front(went); }
|
||||
const std::list<WindowEntity>& windows() const { return m_windows; }
|
||||
std::list<WindowEntity>& windows() { return m_windows; }
|
||||
|
||||
private:
|
||||
LG::PixelBitmap m_icon;
|
||||
std::string m_path_to_exec {};
|
||||
std::string m_bundle_id {};
|
||||
std::list<WindowEntity> m_windows {};
|
||||
};
|
||||
162
userland/system/dock/DockView.cpp
Normal file
162
userland/system/dock/DockView.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "DockView.h"
|
||||
#include "AppListView.h"
|
||||
#include "IconView.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/KeyboardMapping.h>
|
||||
#include <libg/Color.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static DockView* this_view;
|
||||
|
||||
DockView::DockView(UI::View* superview, const LG::Rect& frame)
|
||||
: UI::View(superview, frame)
|
||||
{
|
||||
init_fill_bounds();
|
||||
init_subviews();
|
||||
}
|
||||
|
||||
DockView::DockView(UI::View* superview, UI::Window* window, const LG::Rect& frame)
|
||||
: UI::View(superview, window, frame)
|
||||
{
|
||||
init_fill_bounds();
|
||||
init_subviews();
|
||||
}
|
||||
|
||||
void DockView::init_fill_bounds()
|
||||
{
|
||||
m_fill_bounds = bounds();
|
||||
m_fill_bounds.set_x(bounds().mid_x());
|
||||
m_fill_bounds.set_width(0);
|
||||
fill_bounds_expand(2 * padding());
|
||||
}
|
||||
|
||||
void DockView::init_subviews()
|
||||
{
|
||||
auto& dock_stack_view = add_subview<UI::StackView>(bounds());
|
||||
dock_stack_view.set_spacing(padding());
|
||||
dock_stack_view.set_background_color(LG::Color::Opaque);
|
||||
dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
|
||||
dock_stack_view.set_distribution(UI::StackView::Distribution::EqualCentering);
|
||||
m_dock_stackview = &dock_stack_view;
|
||||
add_system_buttons();
|
||||
}
|
||||
|
||||
void DockView::add_system_buttons()
|
||||
{
|
||||
fill_bounds_expand(icon_view_size() + padding());
|
||||
auto& icon_view = m_dock_stackview->add_arranged_subview<AppListView>();
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
m_applist_view = &icon_view;
|
||||
}
|
||||
|
||||
void DockView::display(const LG::Rect& rect)
|
||||
{
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
ctx.set_fill_color(background_color());
|
||||
ctx.fill_rounded(fill_bounds(), LG::CornerMask(4, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
|
||||
}
|
||||
|
||||
void DockView::new_dock_entity(const std::string& exec_path, const std::string& icon_path, const std::string& bundle_id)
|
||||
{
|
||||
LG::PNG::PNGLoader loader;
|
||||
|
||||
fill_bounds_expand(icon_view_size() + padding());
|
||||
auto& icon_view = m_dock_stackview->add_arranged_subview<IconView>();
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.entity().set_icon(loader.load_from_file(icon_path + "/32x32.png"));
|
||||
icon_view.entity().set_path_to_exec(std::move(exec_path));
|
||||
icon_view.entity().set_bundle_id(std::move(bundle_id));
|
||||
m_icon_views.push_back(&icon_view);
|
||||
set_needs_layout();
|
||||
}
|
||||
|
||||
WindowEntity* DockView::find_window_entry(int window_id)
|
||||
{
|
||||
for (auto* view : m_icon_views) {
|
||||
for (auto& wins : view->entity().windows()) {
|
||||
if (wins.window_id() == window_id) {
|
||||
return &wins;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DockView::on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type)
|
||||
{
|
||||
// Don't add an icon of dock (self).
|
||||
if (window()->id() == window_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window_type == WindowType::AppList) {
|
||||
if (m_applist_view) {
|
||||
m_applist_view->set_target_window_id(window_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto* view : m_icon_views) {
|
||||
if (view->entity().bundle_id() == bundle_id) {
|
||||
view->entity().add_window(WindowEntity(window_id));
|
||||
set_needs_display();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
new_dock_entity("", icon_path, bundle_id);
|
||||
for (auto* view : m_icon_views) {
|
||||
if (view->entity().bundle_id() == bundle_id) {
|
||||
view->entity().add_window(WindowEntity(window_id));
|
||||
set_needs_display();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockView::on_window_minimize(int window_id)
|
||||
{
|
||||
auto* ent = find_window_entry(window_id);
|
||||
if (!ent) {
|
||||
return;
|
||||
}
|
||||
ent->set_minimized(true);
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
void DockView::on_window_remove(int window_id)
|
||||
{
|
||||
// TODO: Currently icons are not properly deleted.
|
||||
for (auto* view : m_icon_views) {
|
||||
for (auto& wins : view->entity().windows()) {
|
||||
if (wins.window_id() == window_id) {
|
||||
auto& win = view->entity().windows();
|
||||
win.erase(std::find(win.begin(), win.end(), WindowEntity(window_id)));
|
||||
set_needs_display();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockView::set_icon(int window_id, const std::string& path)
|
||||
{
|
||||
}
|
||||
|
||||
void DockView::set_title(int window_id, const std::string& title)
|
||||
{
|
||||
auto* ent = find_window_entry(window_id);
|
||||
if (!ent) {
|
||||
return;
|
||||
}
|
||||
ent->set_title(title);
|
||||
}
|
||||
49
userland/system/dock/DockView.h
Normal file
49
userland/system/dock/DockView.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "AppListView.h"
|
||||
#include "DockEntity.h"
|
||||
#include "IconView.h"
|
||||
#include "WindowEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/StackView.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class DockView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
DockView(UI::View* superview, const LG::Rect& frame);
|
||||
DockView(UI::View* superview, UI::Window* window, const LG::Rect& frame);
|
||||
|
||||
static constexpr size_t padding() { return 4; }
|
||||
static constexpr size_t dock_view_height() { return 50; }
|
||||
static constexpr int icon_size() { return 32; }
|
||||
static constexpr int icon_view_size() { return (int)dock_view_height(); }
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
|
||||
WindowEntity* find_window_entry(int window_id);
|
||||
void on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type);
|
||||
void on_window_remove(int window_id);
|
||||
void on_window_minimize(int window_id);
|
||||
void set_icon(int window_id, const std::string& path);
|
||||
void set_title(int window_id, const std::string& title);
|
||||
|
||||
void add_system_buttons();
|
||||
|
||||
void new_dock_entity(const std::string& exec_path, const std::string& icon_path, const std::string& bundle_id);
|
||||
|
||||
private:
|
||||
void init_subviews();
|
||||
void init_fill_bounds();
|
||||
void fill_bounds_expand(size_t l) { m_fill_bounds.set_width(m_fill_bounds.width() + l), m_fill_bounds.set_x(m_fill_bounds.min_x() - l / 2); }
|
||||
const LG::Rect& fill_bounds() const { return m_fill_bounds; }
|
||||
|
||||
void launch(const DockEntity& ent);
|
||||
|
||||
LG::Rect m_fill_bounds {};
|
||||
UI::StackView* m_dock_stackview {};
|
||||
AppListView* m_applist_view {};
|
||||
std::list<IconView*> m_icon_views {};
|
||||
};
|
||||
31
userland/system/dock/DockViewController.h
Normal file
31
userland/system/dock/DockViewController.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "DockView.h"
|
||||
#include <libui/App.h>
|
||||
#include <libui/Button.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/View.h>
|
||||
#include <libui/ViewController.h>
|
||||
#include <libui/Window.h>
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
|
||||
class DockViewController : public UI::ViewController<DockView> {
|
||||
public:
|
||||
DockViewController(DockView& view)
|
||||
: UI::ViewController<DockView>(view)
|
||||
{
|
||||
}
|
||||
virtual ~DockViewController() = default;
|
||||
|
||||
virtual void view_did_load() override
|
||||
{
|
||||
view().set_background_color(LG::Color::LightSystemOpaque);
|
||||
view().new_dock_entity("/Applications/about.app/Content/about", "/res/icons/apps/about.icon", "com.x.about");
|
||||
view().new_dock_entity("/Applications/terminal.app/Content/terminal", "/res/icons/apps/terminal.icon", "com.x.terminal");
|
||||
view().new_dock_entity("/Applications/activity_monitor.app/Content/activity_monitor", "/res/icons/apps/activity_monitor.icon", "com.x.activity_monitor");
|
||||
view().new_dock_entity("/Applications/calculator.app/Content/calculator", "/res/icons/apps/calculator.icon", "com.x.calculator");
|
||||
view().set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
42
userland/system/dock/DockWindow.cpp
Normal file
42
userland/system/dock/DockWindow.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "DockWindow.h"
|
||||
#include "DockView.h"
|
||||
|
||||
void DockWindow::receive_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case UI::Event::Type::NotifyWindowCreateEvent: {
|
||||
UI::NotifyWindowCreateEvent& own_event = *(UI::NotifyWindowCreateEvent*)event.get();
|
||||
DockView* it = (DockView*)superview();
|
||||
it->on_window_create(own_event.bundle_id(), own_event.icon_path(), own_event.window_id(), own_event.window_type());
|
||||
break;
|
||||
}
|
||||
|
||||
case UI::Event::Type::NotifyWindowStatusChangedEvent: {
|
||||
UI::NotifyWindowStatusChangedEvent& own_event = *(UI::NotifyWindowStatusChangedEvent*)event.get();
|
||||
DockView* it = (DockView*)superview();
|
||||
if (own_event.type() == UI::WindowStatusUpdateType::Removed) {
|
||||
it->on_window_remove(own_event.changed_window_id());
|
||||
}
|
||||
if (own_event.type() == UI::WindowStatusUpdateType::Minimized) {
|
||||
it->on_window_minimize(own_event.changed_window_id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UI::Event::Type::NotifyWindowIconChangedEvent: {
|
||||
UI::NotifyWindowIconChangedEvent& own_event = *(UI::NotifyWindowIconChangedEvent*)event.get();
|
||||
DockView* it = (DockView*)superview();
|
||||
it->set_icon(own_event.changed_window_id(), own_event.icon_path());
|
||||
break;
|
||||
}
|
||||
|
||||
case UI::Event::Type::NotifyWindowTitleChangedEvent: {
|
||||
UI::NotifyWindowTitleChangedEvent& own_event = *(UI::NotifyWindowTitleChangedEvent*)event.get();
|
||||
DockView* it = (DockView*)superview();
|
||||
it->set_title(own_event.changed_window_id(), own_event.title());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Window::receive_event(std::move(event));
|
||||
}
|
||||
22
userland/system/dock/DockWindow.h
Normal file
22
userland/system/dock/DockWindow.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <libg/Size.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
class DockWindow : public UI::Window {
|
||||
public:
|
||||
DockWindow()
|
||||
: UI::Window("Dock", LG::Size(UI::Screen::main().bounds().width(), 50), UI::WindowType::Homescreen)
|
||||
{
|
||||
if (fork() == 0) {
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
execlp("/System/applist", "/System/applist", NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
|
||||
};
|
||||
74
userland/system/dock/IconView.cpp
Normal file
74
userland/system/dock/IconView.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "IconView.h"
|
||||
#include "DockView.h"
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
IconView::IconView(View* superview, const LG::Rect& frame)
|
||||
: View(superview, frame)
|
||||
{
|
||||
}
|
||||
|
||||
void IconView::display(const LG::Rect& rect)
|
||||
{
|
||||
const int padding = 4;
|
||||
const int offset_x = (DockView::icon_view_size() - DockView::icon_size()) / 2;
|
||||
const int offset_y = (DockView::icon_view_size() - DockView::icon_size()) / 2;
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
auto icon_rect = LG::Rect(offset_x, offset_y, DockView::icon_size(), DockView::icon_size());
|
||||
ctx.draw(icon_rect.origin(), m_launch_entity.icon());
|
||||
if (is_hovered()) {
|
||||
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
|
||||
ctx.fill(icon_rect);
|
||||
}
|
||||
|
||||
ctx.set_fill_color(LG::Color(120, 129, 133, 40));
|
||||
ctx.draw_box_shading(icon_rect, LG::Shading(LG::Shading::Type::Box, 0, 3), LG::CornerMask(6));
|
||||
|
||||
ctx.set_fill_color(LG::Color(163, 174, 190));
|
||||
const int underline_y = DockView::icon_view_size() - underline_height() - padding;
|
||||
if (entity().windows().size() > 0) {
|
||||
const int len = 8;
|
||||
ctx.fill({ (DockView::icon_view_size() - len) / 2, underline_y, len, underline_height() });
|
||||
}
|
||||
}
|
||||
|
||||
void IconView::on_click()
|
||||
{
|
||||
if (entity().windows().empty()) {
|
||||
launch();
|
||||
return;
|
||||
} else {
|
||||
auto demo_menu = UI::Menu();
|
||||
for (auto& win : entity().windows()) {
|
||||
auto title = win.title();
|
||||
int this_window_id = window()->id();
|
||||
int target_window_id = win.window_id();
|
||||
|
||||
if (win.is_minimized()) {
|
||||
title += " - minimized";
|
||||
}
|
||||
|
||||
demo_menu.add_item(UI::MenuItem(title, [this, this_window_id, target_window_id] {
|
||||
for (auto& win : entity().windows()) {
|
||||
if (win.window_id() == target_window_id) {
|
||||
win.set_minimized(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto& app = UI::App::the();
|
||||
AskBringToFrontMessage msg(app.connection().key(), this_window_id, target_window_id);
|
||||
app.connection().send_async_message(msg);
|
||||
}));
|
||||
}
|
||||
demo_menu.add_item(UI::MenuItem("New window", [this] {
|
||||
launch();
|
||||
}));
|
||||
window()->popup_manager().show({ frame().min_x(), (int)UI::Screen::main().bounds().height() - (int)DockView::dock_view_height() - 4 }, demo_menu);
|
||||
}
|
||||
}
|
||||
61
userland/system/dock/IconView.h
Normal file
61
userland/system/dock/IconView.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "DockEntity.h"
|
||||
#include "WindowEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/PopupMenu.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
class IconView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
IconView(View* superview, const LG::Rect& frame);
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
void mouse_up() override { on_click(); }
|
||||
|
||||
static constexpr size_t underline_height() { return 2; }
|
||||
|
||||
void set_title(const std::string& title)
|
||||
{
|
||||
if (!m_label) {
|
||||
return;
|
||||
}
|
||||
m_label->set_text(title);
|
||||
}
|
||||
|
||||
DockEntity& entity() { return m_launch_entity; }
|
||||
const DockEntity& entity() const { return m_launch_entity; }
|
||||
|
||||
virtual void mouse_entered(const LG::Point<int>& location) override
|
||||
{
|
||||
View::mouse_entered(location);
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
virtual void mouse_exited() override
|
||||
{
|
||||
View::mouse_exited();
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
void on_click();
|
||||
void launch()
|
||||
{
|
||||
if (fork() == 0) {
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
execlp(m_launch_entity.path_to_exec().c_str(), m_launch_entity.path_to_exec().c_str(), NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
UI::Label* m_label;
|
||||
DockEntity m_launch_entity;
|
||||
};
|
||||
43
userland/system/dock/WindowEntity.h
Normal file
43
userland/system/dock/WindowEntity.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <string>
|
||||
|
||||
class WindowEntity {
|
||||
public:
|
||||
enum Status : uint32_t {
|
||||
Minimized = (1 << 0),
|
||||
};
|
||||
|
||||
WindowEntity()
|
||||
: m_window_id(0)
|
||||
, m_title()
|
||||
{
|
||||
}
|
||||
|
||||
WindowEntity(int window_id)
|
||||
: m_window_id(window_id)
|
||||
, m_title()
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const WindowEntity& other) const { return m_window_id == other.m_window_id; }
|
||||
bool operator!=(const WindowEntity& other) const { return m_window_id != other.m_window_id; }
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
bool is_minimized() const { return has_attr(Status::Minimized); }
|
||||
void set_minimized(bool b) { b ? set_attr(Status::Minimized) : rem_attr(Status::Minimized); }
|
||||
|
||||
const std::string& title() const { return m_title; }
|
||||
void set_title(const std::string& title) { m_title = title; }
|
||||
void set_title(std::string&& title) { m_title = std::move(title); }
|
||||
|
||||
private:
|
||||
inline bool has_attr(Status mode) const { return ((m_window_status & (uint32_t)mode) == (uint32_t)mode); }
|
||||
inline void set_attr(Status mode) { m_window_status |= (uint32_t)mode; }
|
||||
inline void rem_attr(Status mode) { m_window_status = m_window_status & (~(uint32_t)mode); }
|
||||
|
||||
int m_window_id { 0 };
|
||||
uint32_t m_window_status { 0 };
|
||||
std::string m_title {};
|
||||
};
|
||||
24
userland/system/homescreen/AppDelegate.cpp
Normal file
24
userland/system/homescreen/AppDelegate.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "HomeScreenView.h"
|
||||
#include "HomeScreenViewController.h"
|
||||
#include "HomeScreenWindow.h"
|
||||
#include <libui/AppDelegate.h>
|
||||
|
||||
class AppDelegate : public UI::AppDelegate {
|
||||
public:
|
||||
AppDelegate() = default;
|
||||
virtual ~AppDelegate() = default;
|
||||
|
||||
LG::Size preferred_desktop_window_size() const override { return LG::Size(400, 300); }
|
||||
|
||||
bool application() override
|
||||
{
|
||||
auto& window = std::xos::construct<HomeScreenWindow>(window_size());
|
||||
window.set_bitmap_format(LG::PixelBitmapFormat::RGBA); // Turning on Alpha channel
|
||||
auto& dock_view = window.create_superview<HomeScreenView, HomeScreenViewController>();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
SET_APP_DELEGATE(AppDelegate);
|
||||
33
userland/system/homescreen/AppListView.cpp
Normal file
33
userland/system/homescreen/AppListView.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "AppListView.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/Screen.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
AppListView::AppListView(View* superview, const LG::Rect& frame)
|
||||
: View(superview, frame)
|
||||
{
|
||||
// TODO: Drag-like animation is needed here.
|
||||
auto& label = add_subview<UI::Label>(bounds());
|
||||
label.set_text("Swipe up to open AppList");
|
||||
label.set_alignment(UI::Text::Alignment::Center);
|
||||
}
|
||||
|
||||
void AppListView::display(const LG::Rect& rect)
|
||||
{
|
||||
}
|
||||
|
||||
void AppListView::on_click()
|
||||
{
|
||||
int this_window_id = window()->id();
|
||||
|
||||
if (m_target_window_id == INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& app = UI::App::the();
|
||||
AskBringToFrontMessage msg(app.connection().key(), this_window_id, m_target_window_id);
|
||||
app.connection().send_async_message(msg);
|
||||
}
|
||||
28
userland/system/homescreen/AppListView.h
Normal file
28
userland/system/homescreen/AppListView.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <libg/Font.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/PopupMenu.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
class AppListView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
static constexpr int INVALID = -1;
|
||||
|
||||
public:
|
||||
AppListView(View* superview, const LG::Rect& frame);
|
||||
|
||||
void set_target_window_id(int winid) { m_target_window_id = winid; }
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
void mouse_up() override { on_click(); }
|
||||
|
||||
void on_click();
|
||||
|
||||
private:
|
||||
int m_target_window_id { INVALID };
|
||||
LG::PixelBitmap m_icon;
|
||||
};
|
||||
18
userland/system/homescreen/BUILD.gn
Normal file
18
userland/system/homescreen/BUILD.gn
Normal file
@@ -0,0 +1,18 @@
|
||||
import("//build/userland/TEMPLATE.gni")
|
||||
|
||||
xOS_executable("homescreen") {
|
||||
install_path = "System/"
|
||||
sources = [
|
||||
"AppDelegate.cpp",
|
||||
"AppListView.cpp",
|
||||
"HomeScreenView.cpp",
|
||||
"IconView.cpp",
|
||||
]
|
||||
configs = [ "//build/userland:userland_flags" ]
|
||||
deplibs = [
|
||||
"libcxx",
|
||||
"libfoundation",
|
||||
"libg",
|
||||
"libui",
|
||||
]
|
||||
}
|
||||
18
userland/system/homescreen/FastLaunchEntity.h
Normal file
18
userland/system/homescreen/FastLaunchEntity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <string>
|
||||
|
||||
class FastLaunchEntity {
|
||||
public:
|
||||
FastLaunchEntity() = default;
|
||||
|
||||
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
|
||||
const LG::PixelBitmap& icon() const { return m_icon; }
|
||||
|
||||
void set_path_to_exec(std::string&& path) { m_path_to_exec = std::move(path); }
|
||||
const std::string& path_to_exec() const { return m_path_to_exec; }
|
||||
|
||||
private:
|
||||
LG::PixelBitmap m_icon;
|
||||
std::string m_path_to_exec {};
|
||||
};
|
||||
24
userland/system/homescreen/HomeScreenEntity.h
Normal file
24
userland/system/homescreen/HomeScreenEntity.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <libg/PixelBitmap.h>
|
||||
|
||||
class HomeScreenEntity {
|
||||
public:
|
||||
HomeScreenEntity() = default;
|
||||
|
||||
HomeScreenEntity(int window_id)
|
||||
: m_window_id(window_id)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const HomeScreenEntity& other) const { return m_window_id == other.m_window_id; }
|
||||
bool operator!=(const HomeScreenEntity& other) const { return m_window_id != other.m_window_id; }
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
|
||||
const LG::PixelBitmap& icon() const { return m_icon; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
LG::PixelBitmap m_icon;
|
||||
int m_window_status { 0 };
|
||||
};
|
||||
127
userland/system/homescreen/HomeScreenView.cpp
Normal file
127
userland/system/homescreen/HomeScreenView.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "HomeScreenView.h"
|
||||
#include "AppListView.h"
|
||||
#include "IconView.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/KeyboardMapping.h>
|
||||
#include <libg/Color.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libui/App.h>
|
||||
#include <libui/Context.h>
|
||||
#include <libui/StackView.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static HomeScreenView* this_view;
|
||||
|
||||
HomeScreenView::HomeScreenView(UI::View* superview, const LG::Rect& frame)
|
||||
: UI::View(superview, frame)
|
||||
{
|
||||
}
|
||||
|
||||
HomeScreenView::HomeScreenView(UI::View* superview, UI::Window* window, const LG::Rect& frame)
|
||||
: UI::View(superview, window, frame)
|
||||
{
|
||||
LG::Rect homegrid_frame = LG::Rect(0, grid_padding(), bounds().width(), bounds().height() - grid_padding() - dock_height_with_padding());
|
||||
auto& vstack_view = add_subview<UI::StackView>(homegrid_frame);
|
||||
vstack_view.set_background_color(LG::Color::Opaque);
|
||||
vstack_view.set_axis(UI::LayoutConstraints::Axis::Vertical);
|
||||
vstack_view.set_distribution(UI::StackView::Distribution::EqualSpacing);
|
||||
vstack_view.set_alignment(UI::StackView::Alignment::Center);
|
||||
|
||||
for (int i = 0; i < grid_entities_per_column(); i++) {
|
||||
auto& hstack_view = vstack_view.add_arranged_subview<UI::StackView>();
|
||||
hstack_view.set_background_color(LG::Color::Opaque);
|
||||
hstack_view.set_distribution(UI::StackView::Distribution::EqualSpacing);
|
||||
vstack_view.add_constraint(UI::Constraint(hstack_view, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, vstack_view, UI::Constraint::Attribute::Left, 1, grid_padding()));
|
||||
vstack_view.add_constraint(UI::Constraint(hstack_view, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, vstack_view, UI::Constraint::Attribute::Right, 1, -grid_padding()));
|
||||
vstack_view.add_constraint(UI::Constraint(hstack_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
m_grid_stackviews.push_back(&hstack_view);
|
||||
}
|
||||
|
||||
int dock_offsety = bounds().height() - dock_height_with_padding();
|
||||
auto& dock_subview = add_subview<UI::View>(LG::Rect(0, dock_offsety, bounds().width(), dock_height_with_padding()));
|
||||
dock_subview.layer().set_corner_mask(LG::CornerMask(16, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
|
||||
dock_subview.set_background_color(LG::Color::LightSystemOpaque128);
|
||||
dock_subview.add_gesture_recognizer<UI::SwipeGestureRecognizer>([this](const UI::GestureRecognizer* recon) {
|
||||
if (recon->state() == UI::GestureRecognizer::State::Ended) {
|
||||
if (m_applist_view) {
|
||||
m_applist_view->on_click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LG::Rect applist_frame = homegrid_frame;
|
||||
applist_frame.set_x(grid_padding());
|
||||
applist_frame.set_width(bounds().width() - 2 * grid_padding());
|
||||
applist_frame.set_y(bounds().height() - dock_height_with_padding() + grid_padding());
|
||||
applist_frame.set_height(applist_height());
|
||||
auto& applist_view = add_subview<AppListView>(applist_frame);
|
||||
m_applist_view = &applist_view;
|
||||
|
||||
LG::Rect dock_frame = homegrid_frame;
|
||||
dock_frame.set_y(applist_frame.max_y() + (dock_height() - icon_size()) / 2);
|
||||
dock_frame.set_height(dock_height());
|
||||
auto& dock_stack_view = add_subview<UI::StackView>(dock_frame);
|
||||
dock_stack_view.set_spacing(12); // TODO: Set spacing which depends on screen width.
|
||||
dock_stack_view.set_background_color(LG::Color::Opaque);
|
||||
dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
|
||||
dock_stack_view.set_distribution(UI::StackView::Distribution::EqualCentering);
|
||||
m_dock_stackview = &dock_stack_view;
|
||||
|
||||
vstack_view.set_needs_layout();
|
||||
}
|
||||
|
||||
void HomeScreenView::display(const LG::Rect& rect)
|
||||
{
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
|
||||
ctx.set_fill_color(LG::Color(0, 0, 0, 0));
|
||||
ctx.fill(bounds());
|
||||
}
|
||||
|
||||
void HomeScreenView::new_grid_entity(const std::string& title, const std::string& icon_path, std::string&& exec_path)
|
||||
{
|
||||
// TODO: Add pages.
|
||||
LG::PNG::PNGLoader loader;
|
||||
int row_to_put_to = 0;
|
||||
for (int i = 0; i < grid_entities_per_column(); i++) {
|
||||
if (m_grid_stackviews[i]->subviews().size() < grid_entities_per_row()) {
|
||||
row_to_put_to = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto& icon_view = m_grid_stackviews[row_to_put_to]->add_arranged_subview<IconView>();
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.set_title(title);
|
||||
icon_view.entity().set_icon(loader.load_from_file(icon_path + "/48x48.png"));
|
||||
icon_view.entity().set_path_to_exec(std::move(exec_path));
|
||||
set_needs_layout();
|
||||
}
|
||||
|
||||
void HomeScreenView::new_fast_launch_entity(const std::string& title, const std::string& icon_path, std::string&& exec_path)
|
||||
{
|
||||
if (m_dock_stackview->subviews().size() >= grid_entities_per_row()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LG::PNG::PNGLoader loader;
|
||||
auto& icon_view = m_dock_stackview->add_arranged_subview<IconView>();
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
|
||||
icon_view.entity().set_icon(loader.load_from_file(icon_path + "/48x48.png"));
|
||||
icon_view.entity().set_path_to_exec(std::move(exec_path));
|
||||
set_needs_layout();
|
||||
}
|
||||
|
||||
void HomeScreenView::on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type)
|
||||
{
|
||||
if (window_type == WindowType::AppList) {
|
||||
if (m_applist_view) {
|
||||
m_applist_view->set_target_window_id(window_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
42
userland/system/homescreen/HomeScreenView.h
Normal file
42
userland/system/homescreen/HomeScreenView.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include "AppListView.h"
|
||||
#include "FastLaunchEntity.h"
|
||||
#include "HomeScreenEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/StackView.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class HomeScreenView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
HomeScreenView(UI::View* superview, const LG::Rect& frame);
|
||||
HomeScreenView(UI::View* superview, UI::Window* window, const LG::Rect& frame);
|
||||
|
||||
static constexpr int dock_height() { return 70; }
|
||||
static constexpr int applist_height() { return 22; }
|
||||
static constexpr int dock_height_with_padding() { return dock_height() + applist_height() + 2 * grid_padding() + 4; }
|
||||
static constexpr int grid_entities_size() { return 48; }
|
||||
static constexpr int icon_size() { return 48; }
|
||||
static constexpr int icon_view_size() { return 62; }
|
||||
static constexpr int grid_padding() { return 16; }
|
||||
static constexpr int grid_entities_per_row() { return 4; }
|
||||
static constexpr int grid_entities_per_column() { return 5; }
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
|
||||
void on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type);
|
||||
|
||||
void new_grid_entity(const std::string& title, const std::string& icon_path, std::string&& exec_path);
|
||||
void new_fast_launch_entity(const std::string& title, const std::string& icon_path, std::string&& exec_path);
|
||||
|
||||
private:
|
||||
std::vector<UI::StackView*> m_grid_stackviews;
|
||||
UI::StackView* m_dock_stackview {};
|
||||
AppListView* m_applist_view {};
|
||||
std::list<FastLaunchEntity> m_grid_entites {};
|
||||
std::list<FastLaunchEntity> m_fast_launch_entites {};
|
||||
};
|
||||
40
userland/system/homescreen/HomeScreenViewController.h
Normal file
40
userland/system/homescreen/HomeScreenViewController.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "HomeScreenView.h"
|
||||
#include <libui/App.h>
|
||||
#include <libui/Button.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/StackView.h>
|
||||
#include <libui/View.h>
|
||||
#include <libui/ViewController.h>
|
||||
#include <libui/Window.h>
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
|
||||
class HomeScreenViewController : public UI::ViewController<HomeScreenView> {
|
||||
public:
|
||||
HomeScreenViewController(HomeScreenView& view)
|
||||
: UI::ViewController<HomeScreenView>(view)
|
||||
{
|
||||
}
|
||||
virtual ~HomeScreenViewController() = default;
|
||||
|
||||
void init_data()
|
||||
{
|
||||
view().new_fast_launch_entity("About", "/res/icons/apps/about.icon", "/Applications/about.app/Content/about"); // FIXME: Parse some init file
|
||||
view().new_fast_launch_entity("Terminal", "/res/icons/apps/terminal.icon", "/Applications/terminal.app/Content/terminal");
|
||||
view().new_fast_launch_entity("Calculator", "/res/icons/apps/calculator.icon", "/Applications/calculator.app/Content/calculator");
|
||||
view().new_grid_entity("About", "/res/icons/apps/about.icon", "/Applications/about.app/Content/about"); // FIXME: Parse some init file
|
||||
view().new_grid_entity("Terminal", "/res/icons/apps/terminal.icon", "/Applications/terminal.app/Content/terminal");
|
||||
view().new_grid_entity("Monitor", "/res/icons/apps/activity_monitor.icon", "/Applications/activity_monitor.app/Content/activity_monitor");
|
||||
view().new_grid_entity("Calculator", "/res/icons/apps/calculator.icon", "/Applications/calculator.app/Content/calculator");
|
||||
}
|
||||
|
||||
virtual void view_did_load() override
|
||||
{
|
||||
view().set_background_color(LG::Color(255, 255, 255, 135));
|
||||
init_data();
|
||||
view().set_needs_display();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
34
userland/system/homescreen/HomeScreenWindow.h
Normal file
34
userland/system/homescreen/HomeScreenWindow.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "HomeScreenView.h"
|
||||
#include <libg/Size.h>
|
||||
#include <libui/Window.h>
|
||||
|
||||
class HomeScreenWindow : public UI::Window {
|
||||
public:
|
||||
HomeScreenWindow(const LG::Size& size)
|
||||
: UI::Window("Homescreen", size, UI::WindowType::Homescreen)
|
||||
{
|
||||
if (fork() == 0) {
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
execlp("/System/applist", "/System/applist", NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void receive_event(std::unique_ptr<LFoundation::Event> event) override
|
||||
{
|
||||
switch (event->type()) {
|
||||
case UI::Event::Type::NotifyWindowCreateEvent: {
|
||||
UI::NotifyWindowCreateEvent& own_event = *(UI::NotifyWindowCreateEvent*)event.get();
|
||||
HomeScreenView* it = (HomeScreenView*)superview();
|
||||
it->on_window_create(own_event.bundle_id(), own_event.icon_path(), own_event.window_id(), own_event.window_type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Window::receive_event(std::move(event));
|
||||
}
|
||||
};
|
||||
19
userland/system/homescreen/IconView.cpp
Normal file
19
userland/system/homescreen/IconView.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "IconView.h"
|
||||
#include "HomeScreenView.h"
|
||||
#include <libui/Context.h>
|
||||
#include <libui/Label.h>
|
||||
|
||||
IconView::IconView(View* superview, const LG::Rect& frame)
|
||||
: View(superview, frame)
|
||||
{
|
||||
m_label = &add_subview<UI::Label>(LG::Rect(0, HomeScreenView::icon_view_size() - 12, HomeScreenView::icon_view_size(), 12));
|
||||
m_label->set_alignment(UI::Text::Alignment::Center);
|
||||
}
|
||||
|
||||
void IconView::display(const LG::Rect& rect)
|
||||
{
|
||||
const int offset_x = (HomeScreenView::icon_view_size() - HomeScreenView::icon_size()) / 2;
|
||||
LG::Context ctx = UI::graphics_current_context();
|
||||
ctx.add_clip(rect);
|
||||
ctx.draw({ offset_x, 0 }, m_launch_entity.icon());
|
||||
}
|
||||
45
userland/system/homescreen/IconView.h
Normal file
45
userland/system/homescreen/IconView.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "FastLaunchEntity.h"
|
||||
#include "HomeScreenEntity.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libui/Label.h>
|
||||
#include <libui/View.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
class IconView : public UI::View {
|
||||
UI_OBJECT();
|
||||
|
||||
public:
|
||||
IconView(View* superview, const LG::Rect& frame);
|
||||
|
||||
void display(const LG::Rect& rect) override;
|
||||
void mouse_up() override { launch(m_launch_entity.path_to_exec()); }
|
||||
|
||||
void set_title(const std::string& title)
|
||||
{
|
||||
if (!m_label) {
|
||||
return;
|
||||
}
|
||||
m_label->set_text(title);
|
||||
}
|
||||
|
||||
FastLaunchEntity& entity() { return m_launch_entity; }
|
||||
const FastLaunchEntity& entity() const { return m_launch_entity; }
|
||||
|
||||
private:
|
||||
void launch(const std::string& path_to_exec)
|
||||
{
|
||||
if (fork() == 0) {
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
execlp(path_to_exec.c_str(), path_to_exec.c_str(), NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
UI::Label* m_label;
|
||||
FastLaunchEntity m_launch_entity;
|
||||
};
|
||||
Reference in New Issue
Block a user