430 lines
14 KiB
C
430 lines
14 KiB
C
#include <hikari/sheet.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <hikari/configuration.h>
|
|
#include <hikari/group.h>
|
|
#include <hikari/layout.h>
|
|
#include <hikari/memory.h>
|
|
#include <hikari/split.h>
|
|
#include <hikari/view.h>
|
|
|
|
void
|
|
hikari_sheet_init(
|
|
struct hikari_sheet *sheet, int nr, struct hikari_workspace *workspace)
|
|
{
|
|
assert(workspace->output != NULL);
|
|
|
|
wl_list_init(&sheet->views);
|
|
|
|
sheet->nr = nr;
|
|
|
|
sheet->workspace = workspace;
|
|
sheet->layout = NULL;
|
|
}
|
|
|
|
static struct hikari_view *
|
|
scan_next_tileable_view(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct wl_list *next = view->sheet_views.next;
|
|
|
|
while (next != &view->sheet->views) {
|
|
view = wl_container_of(next, view, sheet_views);
|
|
if (hikari_view_is_tileable(view)) {
|
|
assert(!hikari_view_is_hidden(view));
|
|
return view;
|
|
}
|
|
next = view->sheet_views.next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct hikari_view *
|
|
hikari_sheet_first_tileable_view(struct hikari_sheet *sheet)
|
|
{
|
|
assert(sheet != NULL);
|
|
|
|
struct wl_list *next = sheet->views.next;
|
|
struct hikari_view *view = NULL;
|
|
|
|
while (next != &sheet->views) {
|
|
view = wl_container_of(next, view, sheet_views);
|
|
if (hikari_view_is_tileable(view)) {
|
|
if (hikari_view_is_hidden(view)) {
|
|
hikari_view_show(view);
|
|
}
|
|
return view;
|
|
}
|
|
next = view->sheet_views.next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
tileable_views(struct hikari_view *view)
|
|
{
|
|
int result;
|
|
if (hikari_view_is_tileable(view)) {
|
|
result = 1;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
|
|
struct wl_list *next = view->sheet_views.next;
|
|
|
|
while (next != &view->sheet->views) {
|
|
view = wl_container_of(next, view, sheet_views);
|
|
if (hikari_view_is_tileable(view)) {
|
|
result++;
|
|
}
|
|
next = view->sheet_views.next;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static struct hikari_view *
|
|
single_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views)
|
|
{
|
|
hikari_view_tile(first, frame);
|
|
return scan_next_tileable_view(first);
|
|
}
|
|
|
|
static struct hikari_view *
|
|
empty_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views)
|
|
{
|
|
return first;
|
|
}
|
|
|
|
static struct hikari_view *
|
|
full_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views)
|
|
{
|
|
struct hikari_view *view = first;
|
|
for (int i = 0; i < nr_of_views && view != NULL; i++) {
|
|
if (hikari_view_is_tileable(view)) {
|
|
if (hikari_view_is_hidden(view)) {
|
|
hikari_view_show(view);
|
|
}
|
|
hikari_view_tile(view, frame);
|
|
}
|
|
view = scan_next_tileable_view(view);
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
#define LAYOUT_VIEWS(nr_of_views, view, frame) \
|
|
if (nr_of_views == 0) { \
|
|
return NULL; \
|
|
} else if (nr_of_views == 1) { \
|
|
hikari_view_tile(view, frame); \
|
|
} else
|
|
|
|
static struct hikari_view *
|
|
grid_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views)
|
|
{
|
|
int nr_of_rows = 1;
|
|
int nr_of_cols = 1;
|
|
|
|
for (int i = 1; i <= nr_of_views; i++) {
|
|
if (i > nr_of_rows * nr_of_cols) {
|
|
if (nr_of_cols > nr_of_rows) {
|
|
assert(nr_of_cols == nr_of_rows + 1);
|
|
nr_of_rows++;
|
|
} else {
|
|
nr_of_cols++;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct hikari_view *view = first;
|
|
LAYOUT_VIEWS(nr_of_views, first, frame)
|
|
{
|
|
int border_width = hikari_configuration->border;
|
|
int gap = hikari_configuration->gap;
|
|
int border = 2 * border_width;
|
|
int row_gaps = nr_of_rows - 1;
|
|
int col_gaps = nr_of_cols - 1;
|
|
int gaps_height = gap * row_gaps;
|
|
int gaps_width = gap * col_gaps;
|
|
int views_height = frame->height - border * nr_of_rows - gaps_height;
|
|
int views_width = frame->width - border * nr_of_cols - gaps_width;
|
|
|
|
int width = views_width / nr_of_cols;
|
|
int height = views_height / nr_of_rows;
|
|
|
|
int rest_width =
|
|
frame->width - border * col_gaps - gaps_width - width * nr_of_cols;
|
|
|
|
int rest_height =
|
|
frame->height - border * row_gaps - gaps_height - height * nr_of_rows;
|
|
|
|
struct wlr_box geometry = { .y = frame->y, .x = frame->x };
|
|
|
|
geometry.height = height + rest_height;
|
|
for (int g_y = 0; g_y < nr_of_rows; g_y++) {
|
|
if (g_y == 1) {
|
|
geometry.height = height;
|
|
}
|
|
geometry.width = width + rest_width;
|
|
for (int g_x = 0; g_x < nr_of_cols; g_x++) {
|
|
if (g_x == 1) {
|
|
geometry.width = width;
|
|
}
|
|
hikari_view_tile(view, &geometry);
|
|
|
|
view = scan_next_tileable_view(view);
|
|
if (view == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
geometry.x += gap + border + geometry.width;
|
|
}
|
|
geometry.x = frame->x;
|
|
geometry.y += gap + border + geometry.height;
|
|
}
|
|
}
|
|
|
|
return scan_next_tileable_view(view);
|
|
}
|
|
|
|
#define SPLIT_LAYOUT(name, x, y, width, height) \
|
|
static struct hikari_view *name##_layout( \
|
|
struct wlr_box *frame, struct hikari_view *first, int nr_of_views) \
|
|
{ \
|
|
struct hikari_view *view = first; \
|
|
int border_width = hikari_configuration->border; \
|
|
int gap = hikari_configuration->gap; \
|
|
int border = 2 * border_width; \
|
|
int gaps = nr_of_views - 1; \
|
|
int gaps_##width = gap * gaps; \
|
|
\
|
|
LAYOUT_VIEWS(nr_of_views, first, frame) \
|
|
{ \
|
|
int views_width = frame->width - border * gaps - gaps_##width; \
|
|
int width = views_width / nr_of_views; \
|
|
int rest = views_width - width * nr_of_views; \
|
|
\
|
|
struct wlr_box geometry = { .x = frame->x, \
|
|
.y = frame->y, \
|
|
.width = width + rest, \
|
|
.height = frame->height }; \
|
|
\
|
|
hikari_view_tile(first, &geometry); \
|
|
\
|
|
geometry.x += gap + border + width + rest; \
|
|
geometry.width = width; \
|
|
for (int n = 1; n < nr_of_views; n++) { \
|
|
view = scan_next_tileable_view(view); \
|
|
hikari_view_tile(view, &geometry); \
|
|
geometry.x += gap + border + width; \
|
|
} \
|
|
} \
|
|
\
|
|
return scan_next_tileable_view(view); \
|
|
}
|
|
|
|
SPLIT_LAYOUT(queue, x, y, width, height)
|
|
SPLIT_LAYOUT(stack, y, x, height, width)
|
|
#undef SPLIT_LAYOUT
|
|
|
|
#define LAYOUT(name) \
|
|
struct hikari_view *hikari_sheet_##name##_layout(struct hikari_sheet *sheet, \
|
|
struct hikari_view *first, \
|
|
struct wlr_box *frame, \
|
|
int max) \
|
|
{ \
|
|
int nr_of_views = tileable_views(first); \
|
|
if (nr_of_views > max) { \
|
|
nr_of_views = max; \
|
|
} \
|
|
if (nr_of_views == 0 && max != 0) { \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
return name##_layout(frame, first, nr_of_views); \
|
|
}
|
|
|
|
LAYOUT(queue)
|
|
LAYOUT(stack)
|
|
LAYOUT(grid)
|
|
LAYOUT(full)
|
|
LAYOUT(single)
|
|
LAYOUT(empty)
|
|
#undef LAYOUT
|
|
|
|
#undef LAYOUT_WINDOWS
|
|
|
|
#define SHEET_VIEW(name, link) \
|
|
struct hikari_view *hikari_sheet_##name##_view(struct hikari_sheet *sheet) \
|
|
{ \
|
|
struct wl_list *link = sheet->views.link; \
|
|
struct hikari_view *view; \
|
|
\
|
|
while (link != &sheet->views) { \
|
|
view = wl_container_of(link, view, sheet_views); \
|
|
if (!hikari_view_is_hidden(view)) { \
|
|
return view; \
|
|
} \
|
|
link = view->sheet_views.link; \
|
|
} \
|
|
\
|
|
return NULL; \
|
|
}
|
|
|
|
SHEET_VIEW(first, next)
|
|
SHEET_VIEW(last, prev)
|
|
#undef SHEET_VIEW
|
|
|
|
#define SHEET_VIEW(link, fallback) \
|
|
struct hikari_view *hikari_sheet_##link##_view( \
|
|
struct hikari_sheet *sheet, struct hikari_view *view) \
|
|
{ \
|
|
struct wl_list *link = view->sheet_views.link; \
|
|
\
|
|
while (link != &view->sheet->views) { \
|
|
view = wl_container_of(link, view, sheet_views); \
|
|
if (!hikari_view_is_hidden(view)) { \
|
|
return view; \
|
|
} \
|
|
link = view->sheet_views.link; \
|
|
} \
|
|
\
|
|
return hikari_sheet_##fallback##_view(sheet); \
|
|
}
|
|
|
|
SHEET_VIEW(next, first)
|
|
SHEET_VIEW(prev, last)
|
|
#undef SHEET_VIEW
|
|
|
|
int
|
|
hikari_sheet_tileable_views(struct hikari_sheet *sheet)
|
|
{
|
|
int nr_of_views = 0;
|
|
|
|
struct hikari_view *view;
|
|
wl_list_for_each (view, &sheet->views, sheet_views) {
|
|
if (hikari_view_is_tileable(view)) {
|
|
nr_of_views++;
|
|
}
|
|
}
|
|
|
|
return nr_of_views;
|
|
}
|
|
|
|
struct hikari_sheet *
|
|
hikari_sheet_next(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_sheet *sheets = sheet->workspace->sheets;
|
|
|
|
if (sheet->nr == 9) {
|
|
return &sheets[1];
|
|
}
|
|
|
|
return &sheets[sheet->nr + 1];
|
|
}
|
|
|
|
struct hikari_sheet *
|
|
hikari_sheet_prev(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_sheet *sheets = sheet->workspace->sheets;
|
|
|
|
if (sheet->nr == 0 || sheet->nr == 1) {
|
|
return &sheets[9];
|
|
}
|
|
|
|
return &sheets[sheet->nr - 1];
|
|
}
|
|
|
|
struct hikari_sheet *
|
|
hikari_sheet_next_inhabited(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_sheet *sheets = sheet->workspace->sheets;
|
|
|
|
if (sheet->nr == 0 || sheet->nr == 9) {
|
|
return sheet;
|
|
}
|
|
|
|
for (uint8_t i = sheet->nr + 1; i < HIKARI_NR_OF_SHEETS; i++) {
|
|
if (!wl_list_empty(&sheets[i].views)) {
|
|
return &sheets[i];
|
|
}
|
|
}
|
|
|
|
return sheet;
|
|
}
|
|
|
|
struct hikari_sheet *
|
|
hikari_sheet_prev_inhabited(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_sheet *sheets = sheet->workspace->sheets;
|
|
|
|
if (sheet->nr <= 1) {
|
|
return sheet;
|
|
}
|
|
|
|
for (uint8_t i = sheet->nr - 1; i > 0; i--) {
|
|
if (!wl_list_empty(&sheets[i].views)) {
|
|
return &sheets[i];
|
|
}
|
|
}
|
|
|
|
return sheet;
|
|
}
|
|
|
|
static void
|
|
raise_floating(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_view *view, *view_temp, *first = NULL;
|
|
wl_list_for_each_reverse_safe (view, view_temp, &sheet->views, sheet_views) {
|
|
if (hikari_view_is_floating(view)) {
|
|
if (first == view) {
|
|
break;
|
|
} else if (first == NULL) {
|
|
first = view;
|
|
}
|
|
|
|
hikari_view_raise(view);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_sheet_apply_split(struct hikari_sheet *sheet, struct hikari_split *split)
|
|
{
|
|
if (sheet->layout != NULL) {
|
|
struct hikari_tile *tile;
|
|
wl_list_for_each (tile, &sheet->layout->tiles, layout_tiles) {
|
|
if (hikari_view_is_dirty(tile->view)) {
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
sheet->layout = hikari_malloc(sizeof(struct hikari_layout));
|
|
hikari_layout_init(sheet->layout, split, sheet);
|
|
}
|
|
|
|
struct hikari_output *output = sheet->workspace->output;
|
|
struct wlr_box geometry = output->usable_area;
|
|
struct hikari_view *first = hikari_sheet_first_tileable_view(sheet);
|
|
|
|
sheet->layout->split = split;
|
|
|
|
hikari_split_apply(split, &geometry, first);
|
|
|
|
raise_floating(sheet);
|
|
}
|
|
|
|
bool
|
|
hikari_sheet_is_visible(struct hikari_sheet *sheet)
|
|
{
|
|
struct hikari_sheet *sheets = sheet->workspace->sheets;
|
|
|
|
return sheet == sheet->workspace->sheet || sheet == &sheets[0];
|
|
}
|