mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 11:18:11 +00:00
FileManager: Port to GTableModel/GTableView.
Replace the custom DirectoryView widget with a GTableView subclass. This was pleasantly straightforward and it's so cool seeing the huge increase in app quality from GTableView. :^)
This commit is contained in:
parent
b5dcad932e
commit
ac8fb5da4c
8 changed files with 308 additions and 236 deletions
180
Applications/FileManager/DirectoryTableModel.cpp
Normal file
180
Applications/FileManager/DirectoryTableModel.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
#include "DirectoryTableModel.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <AK/FileSystemPath.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
|
|
||||||
|
DirectoryTableModel::DirectoryTableModel()
|
||||||
|
{
|
||||||
|
m_directory_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/folder16.rgb", { 16, 16 });
|
||||||
|
m_file_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/file16.rgb", { 16, 16 });
|
||||||
|
m_symlink_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/link16.rgb", { 16, 16 });
|
||||||
|
m_socket_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/socket16.rgb", { 16, 16 });
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryTableModel::~DirectoryTableModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryTableModel::row_count() const
|
||||||
|
{
|
||||||
|
return m_directories.size() + m_files.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryTableModel::column_count() const
|
||||||
|
{
|
||||||
|
return Column::__Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirectoryTableModel::column_name(int column) const
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case Column::Icon: return "";
|
||||||
|
case Column::Name: return "Name";
|
||||||
|
case Column::Size: return "Size";
|
||||||
|
case Column::UID: return "UID";
|
||||||
|
case Column::GID: return "GID";
|
||||||
|
case Column::Permissions: return "Mode";
|
||||||
|
case Column::Inode: return "Inode";
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
GTableModel::ColumnMetadata DirectoryTableModel::column_metadata(int column) const
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case Column::Icon: return { 16, TextAlignment::Center };
|
||||||
|
case Column::Name: return { 120, TextAlignment::CenterLeft };
|
||||||
|
case Column::Size: return { 80, TextAlignment::CenterRight };
|
||||||
|
case Column::UID: return { 80, TextAlignment::CenterRight };
|
||||||
|
case Column::GID: return { 80, TextAlignment::CenterRight };
|
||||||
|
case Column::Permissions: return { 100, TextAlignment::CenterLeft };
|
||||||
|
case Column::Inode: return { 80, TextAlignment::CenterRight };
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
const GraphicsBitmap& DirectoryTableModel::icon_for(const Entry& entry) const
|
||||||
|
{
|
||||||
|
if (S_ISDIR(entry.mode))
|
||||||
|
return *m_directory_icon;
|
||||||
|
if (S_ISLNK(entry.mode))
|
||||||
|
return *m_symlink_icon;
|
||||||
|
if (S_ISSOCK(entry.mode))
|
||||||
|
return *m_socket_icon;
|
||||||
|
return *m_file_icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static String permission_string(mode_t mode)
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
builder.append("d");
|
||||||
|
else if (S_ISLNK(mode))
|
||||||
|
builder.append("l");
|
||||||
|
else if (S_ISBLK(mode))
|
||||||
|
builder.append("b");
|
||||||
|
else if (S_ISCHR(mode))
|
||||||
|
builder.append("c");
|
||||||
|
else if (S_ISFIFO(mode))
|
||||||
|
builder.append("f");
|
||||||
|
else if (S_ISSOCK(mode))
|
||||||
|
builder.append("s");
|
||||||
|
else if (S_ISREG(mode))
|
||||||
|
builder.append("-");
|
||||||
|
else
|
||||||
|
builder.append("?");
|
||||||
|
|
||||||
|
builder.appendf("%c%c%c%c%c%c%c%c",
|
||||||
|
mode & S_IRUSR ? 'r' : '-',
|
||||||
|
mode & S_IWUSR ? 'w' : '-',
|
||||||
|
mode & S_ISUID ? 's' : (mode & S_IXUSR ? 'x' : '-'),
|
||||||
|
mode & S_IRGRP ? 'r' : '-',
|
||||||
|
mode & S_IWGRP ? 'w' : '-',
|
||||||
|
mode & S_ISGID ? 's' : (mode & S_IXGRP ? 'x' : '-'),
|
||||||
|
mode & S_IROTH ? 'r' : '-',
|
||||||
|
mode & S_IWOTH ? 'w' : '-'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mode & S_ISVTX)
|
||||||
|
builder.append("t");
|
||||||
|
else
|
||||||
|
builder.appendf("%c", mode & S_IXOTH ? 'x' : '-');
|
||||||
|
return builder.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
GVariant DirectoryTableModel::data(int row, int column) const
|
||||||
|
{
|
||||||
|
auto& entry = this->entry(row);
|
||||||
|
switch (column) {
|
||||||
|
case Column::Icon: return icon_for(entry);
|
||||||
|
case Column::Name: return entry.name;
|
||||||
|
case Column::Size: return (int)entry.size;
|
||||||
|
case Column::UID: return (int)entry.uid;
|
||||||
|
case Column::GID: return (int)entry.gid;
|
||||||
|
case Column::Permissions: return permission_string(entry.mode);
|
||||||
|
case Column::Inode: return (int)entry.inode;
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableModel::update()
|
||||||
|
{
|
||||||
|
DIR* dirp = opendir(m_path.characters());
|
||||||
|
if (!dirp) {
|
||||||
|
perror("opendir");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
m_directories.clear();
|
||||||
|
m_files.clear();
|
||||||
|
|
||||||
|
m_bytes_in_files = 0;
|
||||||
|
while (auto* de = readdir(dirp)) {
|
||||||
|
Entry entry;
|
||||||
|
entry.name = de->d_name;
|
||||||
|
struct stat st;
|
||||||
|
int rc = lstat(String::format("%s/%s", m_path.characters(), de->d_name).characters(), &st);
|
||||||
|
if (rc < 0) {
|
||||||
|
perror("lstat");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry.size = st.st_size;
|
||||||
|
entry.mode = st.st_mode;
|
||||||
|
entry.uid = st.st_uid;
|
||||||
|
entry.gid = st.st_gid;
|
||||||
|
entry.inode = st.st_ino;
|
||||||
|
auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files;
|
||||||
|
entries.append(move(entry));
|
||||||
|
|
||||||
|
if (S_ISREG(entry.mode))
|
||||||
|
m_bytes_in_files += st.st_size;
|
||||||
|
}
|
||||||
|
closedir(dirp);
|
||||||
|
|
||||||
|
did_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableModel::open(const String& path)
|
||||||
|
{
|
||||||
|
if (m_path == path)
|
||||||
|
return;
|
||||||
|
DIR* dirp = opendir(path.characters());
|
||||||
|
if (!dirp)
|
||||||
|
return;
|
||||||
|
closedir(dirp);
|
||||||
|
m_path = path;
|
||||||
|
update();
|
||||||
|
set_selected_index({ 0, 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableModel::activate(const GModelIndex& index)
|
||||||
|
{
|
||||||
|
auto& entry = this->entry(index.row());
|
||||||
|
if (entry.is_directory()) {
|
||||||
|
FileSystemPath new_path(String::format("%s/%s", m_path.characters(), entry.name.characters()));
|
||||||
|
open(new_path.string());
|
||||||
|
}
|
||||||
|
}
|
62
Applications/FileManager/DirectoryTableModel.h
Normal file
62
Applications/FileManager/DirectoryTableModel.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/GTableModel.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
class DirectoryTableModel final : public GTableModel {
|
||||||
|
public:
|
||||||
|
DirectoryTableModel();
|
||||||
|
virtual ~DirectoryTableModel() override;
|
||||||
|
|
||||||
|
enum Column {
|
||||||
|
Icon = 0,
|
||||||
|
Name,
|
||||||
|
Size,
|
||||||
|
UID,
|
||||||
|
GID,
|
||||||
|
Permissions,
|
||||||
|
Inode,
|
||||||
|
__Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual int row_count() const override;
|
||||||
|
virtual int column_count() const override;
|
||||||
|
virtual String column_name(int column) const override;
|
||||||
|
virtual ColumnMetadata column_metadata(int column) const override;
|
||||||
|
virtual GVariant data(int row, int column) const override;
|
||||||
|
virtual void update() override;
|
||||||
|
virtual void activate(const GModelIndex&) override;
|
||||||
|
|
||||||
|
String path() const { return m_path; }
|
||||||
|
void open(const String& path);
|
||||||
|
size_t bytes_in_files() const { return m_bytes_in_files; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
String name;
|
||||||
|
size_t size { 0 };
|
||||||
|
mode_t mode { 0 };
|
||||||
|
uid_t uid { 0 };
|
||||||
|
uid_t gid { 0 };
|
||||||
|
ino_t inode { 0 };
|
||||||
|
bool is_directory() const { return S_ISDIR(mode); }
|
||||||
|
};
|
||||||
|
|
||||||
|
const Entry& entry(int index) const
|
||||||
|
{
|
||||||
|
if (index < m_directories.size())
|
||||||
|
return m_directories[index];
|
||||||
|
return m_files[index - m_directories.size()];
|
||||||
|
}
|
||||||
|
const GraphicsBitmap& icon_for(const Entry& entry) const;
|
||||||
|
|
||||||
|
String m_path;
|
||||||
|
Vector<Entry> m_files;
|
||||||
|
Vector<Entry> m_directories;
|
||||||
|
size_t m_bytes_in_files;
|
||||||
|
|
||||||
|
RetainPtr<GraphicsBitmap> m_directory_icon;
|
||||||
|
RetainPtr<GraphicsBitmap> m_file_icon;
|
||||||
|
RetainPtr<GraphicsBitmap> m_symlink_icon;
|
||||||
|
RetainPtr<GraphicsBitmap> m_socket_icon;
|
||||||
|
};
|
33
Applications/FileManager/DirectoryTableView.cpp
Normal file
33
Applications/FileManager/DirectoryTableView.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include "DirectoryTableView.h"
|
||||||
|
|
||||||
|
DirectoryTableView::DirectoryTableView(GWidget* parent)
|
||||||
|
: GTableView(parent)
|
||||||
|
{
|
||||||
|
set_model(make<DirectoryTableModel>());
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryTableView::~DirectoryTableView()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableView::open(const String& path)
|
||||||
|
{
|
||||||
|
model().open(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableView::model_notification(const GModelNotification& notification)
|
||||||
|
{
|
||||||
|
if (notification.type() == GModelNotification::Type::ModelUpdated) {
|
||||||
|
set_status_message(String::format("%d item%s (%u byte%s)",
|
||||||
|
model().row_count(),
|
||||||
|
model().row_count() != 1 ? "s" : "",
|
||||||
|
model().bytes_in_files(),
|
||||||
|
model().bytes_in_files() != 1 ? "s" : ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryTableView::set_status_message(const String& message)
|
||||||
|
{
|
||||||
|
if (on_status_message)
|
||||||
|
on_status_message(message);
|
||||||
|
}
|
25
Applications/FileManager/DirectoryTableView.h
Normal file
25
Applications/FileManager/DirectoryTableView.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/GTableView.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "DirectoryTableModel.h"
|
||||||
|
|
||||||
|
class DirectoryTableView final : public GTableView {
|
||||||
|
public:
|
||||||
|
explicit DirectoryTableView(GWidget* parent);
|
||||||
|
virtual ~DirectoryTableView() override;
|
||||||
|
|
||||||
|
void open(const String& path);
|
||||||
|
String path() const { return model().path(); }
|
||||||
|
|
||||||
|
Function<void(const String&)> on_path_change;
|
||||||
|
Function<void(String)> on_status_message;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void model_notification(const GModelNotification&) override;
|
||||||
|
|
||||||
|
DirectoryTableModel& model() { return static_cast<DirectoryTableModel&>(*GTableView::model()); }
|
||||||
|
const DirectoryTableModel& model() const { return static_cast<const DirectoryTableModel&>(*GTableView::model()); }
|
||||||
|
|
||||||
|
void set_status_message(const String&);
|
||||||
|
};
|
|
@ -1,171 +0,0 @@
|
||||||
#include <dirent.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <SharedGraphics/GraphicsBitmap.h>
|
|
||||||
#include <SharedGraphics/Painter.h>
|
|
||||||
#include <LibGUI/GScrollBar.h>
|
|
||||||
#include <AK/FileSystemPath.h>
|
|
||||||
#include "DirectoryView.h"
|
|
||||||
|
|
||||||
DirectoryView::DirectoryView(GWidget* parent)
|
|
||||||
: GWidget(parent)
|
|
||||||
{
|
|
||||||
m_directory_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/folder16.rgb", { 16, 16 });
|
|
||||||
m_file_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/file16.rgb", { 16, 16 });
|
|
||||||
m_symlink_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/link16.rgb", { 16, 16 });
|
|
||||||
m_socket_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/socket16.rgb", { 16, 16 });
|
|
||||||
|
|
||||||
m_scrollbar = new GScrollBar(Orientation::Vertical, this);
|
|
||||||
m_scrollbar->set_step(4);
|
|
||||||
m_scrollbar->set_big_step(30);
|
|
||||||
m_scrollbar->on_change = [this] (int) {
|
|
||||||
update();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryView::~DirectoryView()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::resize_event(GResizeEvent& event)
|
|
||||||
{
|
|
||||||
m_scrollbar->set_relative_rect(event.size().width() - m_scrollbar->preferred_size().width(), 0, m_scrollbar->preferred_size().width(), event.size().height());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::open(const String& path)
|
|
||||||
{
|
|
||||||
if (m_path == path)
|
|
||||||
return;
|
|
||||||
DIR* dirp = opendir(path.characters());
|
|
||||||
if (!dirp)
|
|
||||||
return;
|
|
||||||
closedir(dirp);
|
|
||||||
m_path = path;
|
|
||||||
reload();
|
|
||||||
if (on_path_change)
|
|
||||||
on_path_change(m_path);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::reload()
|
|
||||||
{
|
|
||||||
DIR* dirp = opendir(m_path.characters());
|
|
||||||
if (!dirp) {
|
|
||||||
perror("opendir");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
m_directories.clear();
|
|
||||||
m_files.clear();
|
|
||||||
|
|
||||||
size_t bytes_in_files = 0;
|
|
||||||
while (auto* de = readdir(dirp)) {
|
|
||||||
Entry entry;
|
|
||||||
entry.name = de->d_name;
|
|
||||||
struct stat st;
|
|
||||||
int rc = lstat(String::format("%s/%s", m_path.characters(), de->d_name).characters(), &st);
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("lstat");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entry.size = st.st_size;
|
|
||||||
entry.mode = st.st_mode;
|
|
||||||
auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files;
|
|
||||||
entries.append(move(entry));
|
|
||||||
|
|
||||||
if (S_ISREG(entry.mode))
|
|
||||||
bytes_in_files += st.st_size;
|
|
||||||
}
|
|
||||||
closedir(dirp);
|
|
||||||
int excess_height = max(0, (item_count() * item_height()) - height());
|
|
||||||
m_scrollbar->set_range(0, excess_height);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set_status_message(String::format("%d item%s (%u byte%s)",
|
|
||||||
item_count(),
|
|
||||||
item_count() != 1 ? "s" : "",
|
|
||||||
bytes_in_files,
|
|
||||||
bytes_in_files != 1 ? "s" : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
const GraphicsBitmap& DirectoryView::icon_for(const Entry& entry) const
|
|
||||||
{
|
|
||||||
if (S_ISDIR(entry.mode))
|
|
||||||
return *m_directory_icon;
|
|
||||||
if (S_ISLNK(entry.mode))
|
|
||||||
return *m_symlink_icon;
|
|
||||||
if (S_ISSOCK(entry.mode))
|
|
||||||
return *m_socket_icon;
|
|
||||||
return *m_file_icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String pretty_byte_size(size_t size)
|
|
||||||
{
|
|
||||||
return String::format("%u", size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DirectoryView::should_show_size_for(const Entry& entry) const
|
|
||||||
{
|
|
||||||
return S_ISREG(entry.mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect DirectoryView::row_rect(int item_index) const
|
|
||||||
{
|
|
||||||
return { 0, item_index * item_height(), width(), item_height() };
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::mousedown_event(GMouseEvent& event)
|
|
||||||
{
|
|
||||||
if (event.button() == GMouseButton::Left) {
|
|
||||||
for (int i = 0; i < item_count(); ++i) {
|
|
||||||
if (!row_rect(i).contains(event.position()))
|
|
||||||
continue;
|
|
||||||
auto& entry = this->entry(i);
|
|
||||||
if (entry.is_directory()) {
|
|
||||||
FileSystemPath new_path(String::format("%s/%s", m_path.characters(), entry.name.characters()));
|
|
||||||
open(new_path.string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::paint_event(GPaintEvent&)
|
|
||||||
{
|
|
||||||
Painter painter(*this);
|
|
||||||
|
|
||||||
painter.translate(0, -m_scrollbar->value());
|
|
||||||
|
|
||||||
int horizontal_padding = 5;
|
|
||||||
int icon_size = 16;
|
|
||||||
int painted_item_index = 0;
|
|
||||||
|
|
||||||
auto process_entries = [&] (const Vector<Entry>& entries) {
|
|
||||||
for (int i = 0; i < entries.size(); ++i, ++painted_item_index) {
|
|
||||||
auto& entry = entries[i];
|
|
||||||
int y = painted_item_index * item_height();
|
|
||||||
Rect icon_rect(horizontal_padding, y, icon_size, item_height());
|
|
||||||
Rect name_rect(icon_rect.right() + horizontal_padding, y, 100, item_height());
|
|
||||||
Rect size_rect(name_rect.right() + horizontal_padding, y, 64, item_height());
|
|
||||||
painter.fill_rect(row_rect(painted_item_index), painted_item_index % 2 ? Color(210, 210, 210) : Color::White);
|
|
||||||
painter.blit(icon_rect.location(), icon_for(entry), { 0, 0, icon_size, icon_size });
|
|
||||||
painter.draw_text(name_rect, entry.name, TextAlignment::CenterLeft, Color::Black);
|
|
||||||
if (should_show_size_for(entry))
|
|
||||||
painter.draw_text(size_rect, pretty_byte_size(entry.size), TextAlignment::CenterRight, Color::Black);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
process_entries(m_directories);
|
|
||||||
process_entries(m_files);
|
|
||||||
|
|
||||||
Rect unpainted_rect(0, painted_item_index * item_height(), width(), height());
|
|
||||||
unpainted_rect.intersect(rect());
|
|
||||||
painter.fill_rect(unpainted_rect, Color::White);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectoryView::set_status_message(String&& message)
|
|
||||||
{
|
|
||||||
if (on_status_message)
|
|
||||||
on_status_message(move(message));
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <LibGUI/GWidget.h>
|
|
||||||
#include <AK/Function.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
class GScrollBar;
|
|
||||||
|
|
||||||
class DirectoryView final : public GWidget {
|
|
||||||
public:
|
|
||||||
DirectoryView(GWidget* parent);
|
|
||||||
virtual ~DirectoryView() override;
|
|
||||||
|
|
||||||
void open(const String& path);
|
|
||||||
void reload();
|
|
||||||
|
|
||||||
Function<void(const String&)> on_path_change;
|
|
||||||
Function<void(String)> on_status_message;
|
|
||||||
|
|
||||||
int item_height() const { return 16; }
|
|
||||||
int item_count() const { return m_directories.size() + m_files.size(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void paint_event(GPaintEvent&) override;
|
|
||||||
virtual void resize_event(GResizeEvent&) override;
|
|
||||||
virtual void mousedown_event(GMouseEvent&) override;
|
|
||||||
|
|
||||||
void set_status_message(String&&);
|
|
||||||
|
|
||||||
Rect row_rect(int item_index) const;
|
|
||||||
|
|
||||||
struct Entry {
|
|
||||||
String name;
|
|
||||||
size_t size { 0 };
|
|
||||||
mode_t mode { 0 };
|
|
||||||
|
|
||||||
bool is_directory() const { return S_ISDIR(mode); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const Entry& entry(int index) const
|
|
||||||
{
|
|
||||||
if (index < m_directories.size())
|
|
||||||
return m_directories[index];
|
|
||||||
return m_files[index - m_directories.size()];
|
|
||||||
}
|
|
||||||
const GraphicsBitmap& icon_for(const Entry&) const;
|
|
||||||
bool should_show_size_for(const Entry&) const;
|
|
||||||
|
|
||||||
Vector<Entry> m_files;
|
|
||||||
Vector<Entry> m_directories;
|
|
||||||
|
|
||||||
String m_path;
|
|
||||||
RetainPtr<GraphicsBitmap> m_directory_icon;
|
|
||||||
RetainPtr<GraphicsBitmap> m_file_icon;
|
|
||||||
RetainPtr<GraphicsBitmap> m_symlink_icon;
|
|
||||||
RetainPtr<GraphicsBitmap> m_socket_icon;
|
|
||||||
|
|
||||||
GScrollBar* m_scrollbar { nullptr };
|
|
||||||
};
|
|
|
@ -1,5 +1,6 @@
|
||||||
OBJS = \
|
OBJS = \
|
||||||
DirectoryView.o \
|
DirectoryTableModel.o \
|
||||||
|
DirectoryTableView.o \
|
||||||
main.o
|
main.o
|
||||||
|
|
||||||
APP = FileManager
|
APP = FileManager
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <LibGUI/GAction.h>
|
#include <LibGUI/GAction.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "DirectoryView.h"
|
#include "DirectoryTableView.h"
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
@ -63,20 +63,21 @@ int main(int argc, char** argv)
|
||||||
toolbar->add_action(copy_action.copy_ref());
|
toolbar->add_action(copy_action.copy_ref());
|
||||||
toolbar->add_action(delete_action.copy_ref());
|
toolbar->add_action(delete_action.copy_ref());
|
||||||
|
|
||||||
auto* directory_view = new DirectoryView(widget);
|
auto* directory_table_view = new DirectoryTableView(widget);
|
||||||
|
|
||||||
auto* statusbar = new GStatusBar(widget);
|
auto* statusbar = new GStatusBar(widget);
|
||||||
statusbar->set_text("Welcome!");
|
statusbar->set_text("Welcome!");
|
||||||
|
|
||||||
directory_view->on_path_change = [window] (const String& new_path) {
|
directory_table_view->on_path_change = [window] (const String& new_path) {
|
||||||
window->set_title(String::format("FileManager: %s", new_path.characters()));
|
window->set_title(String::format("FileManager: %s", new_path.characters()));
|
||||||
};
|
};
|
||||||
|
|
||||||
directory_view->on_status_message = [statusbar] (String message) {
|
directory_table_view->on_status_message = [statusbar] (String message) {
|
||||||
statusbar->set_text(move(message));
|
statusbar->set_text(move(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
directory_view->open("/");
|
directory_table_view->open("/");
|
||||||
|
directory_table_view->set_focus(true);
|
||||||
|
|
||||||
window->set_should_exit_app_on_close(true);
|
window->set_should_exit_app_on_close(true);
|
||||||
window->show();
|
window->show();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue