summaryrefslogtreecommitdiff
path: root/examples/dte/frame.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2023-11-09 23:19:53 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2023-11-09 23:19:53 +0100
commit1566b6faa8534118c3566188181367cd0868468f (patch)
tree1de8d4b369efb5e592685a31088f798a6b63ffa1 /examples/dte/frame.c
parent349991bf6efe473ab9a5cbdae0a8114d72b997e3 (diff)
downloadcrep-1566b6faa8534118c3566188181367cd0868468f.tar.gz
Added partial matching and introduced threads
Diffstat (limited to 'examples/dte/frame.c')
-rw-r--r--examples/dte/frame.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/examples/dte/frame.c b/examples/dte/frame.c
new file mode 100644
index 0000000..c06b2d8
--- /dev/null
+++ b/examples/dte/frame.c
@@ -0,0 +1,496 @@
+#include "frame.h"
+#include "editor.h"
+#include "util/debug.h"
+#include "util/xmalloc.h"
+#include "window.h"
+
+enum {
+ WINDOW_MIN_WIDTH = 8,
+ WINDOW_MIN_HEIGHT = 3,
+};
+
+static void sanity_check_frame(const Frame *frame)
+{
+ bool has_window = !!frame->window;
+ bool has_frames = frame->frames.count > 0;
+ if (has_window == has_frames) {
+ BUG("frames must contain a window or subframe(s), but never both");
+ }
+ BUG_ON(has_window && frame != frame->window->frame);
+}
+
+static int get_min_w(const Frame *frame)
+{
+ if (frame->window) {
+ return WINDOW_MIN_WIDTH;
+ }
+
+ const PointerArray *subframes = &frame->frames;
+ const size_t count = subframes->count;
+ if (!frame->vertical) {
+ int w = count - 1; // Separators
+ for (size_t i = 0; i < count; i++) {
+ w += get_min_w(subframes->ptrs[i]);
+ }
+ return w;
+ }
+
+ int max = 0;
+ for (size_t i = 0; i < count; i++) {
+ int w = get_min_w(subframes->ptrs[i]);
+ max = MAX(w, max);
+ }
+ return max;
+}
+
+static int get_min_h(const Frame *frame)
+{
+ if (frame->window) {
+ return WINDOW_MIN_HEIGHT;
+ }
+
+ const PointerArray *subframes = &frame->frames;
+ const size_t count = subframes->count;
+ if (frame->vertical) {
+ int h = 0;
+ for (size_t i = 0; i < count; i++) {
+ h += get_min_h(subframes->ptrs[i]);
+ }
+ return h;
+ }
+
+ int max = 0;
+ for (size_t i = 0; i < count; i++) {
+ int h = get_min_h(subframes->ptrs[i]);
+ max = MAX(h, max);
+ }
+ return max;
+}
+
+static int get_min(const Frame *frame)
+{
+ return frame->parent->vertical ? get_min_h(frame) : get_min_w(frame);
+}
+
+static int get_size(const Frame *frame)
+{
+ return frame->parent->vertical ? frame->h : frame->w;
+}
+
+static int get_container_size(const Frame *frame)
+{
+ return frame->vertical ? frame->h : frame->w;
+}
+
+static void set_size(Frame *frame, int size)
+{
+ bool vertical = frame->parent->vertical;
+ int w = vertical ? frame->parent->w : size;
+ int h = vertical ? size : frame->parent->h;
+ set_frame_size(frame, w, h);
+}
+
+static void divide_equally(const Frame *frame)
+{
+ size_t count = frame->frames.count;
+ BUG_ON(count == 0);
+
+ int *min = xnew(int, count);
+ for (size_t i = 0; i < count; i++) {
+ min[i] = get_min(frame->frames.ptrs[i]);
+ }
+
+ int *size = xnew0(int, count);
+ int s = get_container_size(frame);
+ int q, r, used;
+ size_t n = count;
+
+ // Consume q and r as equally as possible
+ do {
+ used = 0;
+ q = s / n;
+ r = s % n;
+ for (size_t i = 0; i < count; i++) {
+ if (size[i] == 0 && min[i] > q) {
+ size[i] = min[i];
+ used += min[i];
+ n--;
+ }
+ }
+ s -= used;
+ } while (used && n > 0);
+
+ for (size_t i = 0; i < count; i++) {
+ Frame *c = frame->frames.ptrs[i];
+ if (size[i] == 0) {
+ size[i] = q + (r-- > 0);
+ }
+ set_size(c, size[i]);
+ }
+
+ free(size);
+ free(min);
+}
+
+static void fix_size(const Frame *frame)
+{
+ size_t count = frame->frames.count;
+ int *size = xnew(int, count);
+ int *min = xnew(int, count);
+ int total = 0;
+ for (size_t i = 0; i < count; i++) {
+ const Frame *c = frame->frames.ptrs[i];
+ min[i] = get_min(c);
+ size[i] = MAX(get_size(c), min[i]);
+ total += size[i];
+ }
+
+ int s = get_container_size(frame);
+ if (total > s) {
+ int n = total - s;
+ for (ssize_t i = count - 1; n > 0 && i >= 0; i--) {
+ int new_size = MAX(size[i] - n, min[i]);
+ n -= size[i] - new_size;
+ size[i] = new_size;
+ }
+ } else {
+ size[count - 1] += s - total;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ set_size(frame->frames.ptrs[i], size[i]);
+ }
+
+ free(size);
+ free(min);
+}
+
+static void add_to_sibling_size(Frame *frame, int count)
+{
+ const Frame *parent = frame->parent;
+ size_t idx = ptr_array_idx(&parent->frames, frame);
+ BUG_ON(idx >= parent->frames.count);
+ if (idx == parent->frames.count - 1) {
+ frame = parent->frames.ptrs[idx - 1];
+ } else {
+ frame = parent->frames.ptrs[idx + 1];
+ }
+ set_size(frame, get_size(frame) + count);
+}
+
+static int sub(Frame *frame, int count)
+{
+ int min = get_min(frame);
+ int old = get_size(frame);
+ int new = MAX(min, old - count);
+ if (new != old) {
+ set_size(frame, new);
+ }
+ return count - (old - new);
+}
+
+static void subtract_from_sibling_size(const Frame *frame, int count)
+{
+ const Frame *parent = frame->parent;
+ size_t idx = ptr_array_idx(&parent->frames, frame);
+ BUG_ON(idx >= parent->frames.count);
+
+ for (size_t i = idx + 1, n = parent->frames.count; i < n; i++) {
+ count = sub(parent->frames.ptrs[i], count);
+ if (count == 0) {
+ return;
+ }
+ }
+
+ for (size_t i = idx; i > 0; i--) {
+ count = sub(parent->frames.ptrs[i - 1], count);
+ if (count == 0) {
+ return;
+ }
+ }
+}
+
+static void resize_to(Frame *frame, int size)
+{
+ const Frame *parent = frame->parent;
+ int total = parent->vertical ? parent->h : parent->w;
+ int count = parent->frames.count;
+ int min = get_min(frame);
+ int max = total - (count - 1) * min;
+ max = MAX(min, max);
+ size = CLAMP(size, min, max);
+
+ int change = size - get_size(frame);
+ if (change == 0) {
+ return;
+ }
+
+ set_size(frame, size);
+ if (change < 0) {
+ add_to_sibling_size(frame, -change);
+ } else {
+ subtract_from_sibling_size(frame, change);
+ }
+}
+
+static bool rightmost_frame(const Frame *frame)
+{
+ const Frame *parent = frame->parent;
+ if (!parent) {
+ return true;
+ }
+ if (!parent->vertical) {
+ if (frame != parent->frames.ptrs[parent->frames.count - 1]) {
+ return false;
+ }
+ }
+ return rightmost_frame(parent);
+}
+
+static Frame *new_frame(void)
+{
+ Frame *frame = xnew0(Frame, 1);
+ frame->equal_size = true;
+ return frame;
+}
+
+static Frame *add_frame(Frame *parent, Window *window, size_t idx)
+{
+ Frame *frame = new_frame();
+ frame->parent = parent;
+ frame->window = window;
+ window->frame = frame;
+ if (parent) {
+ BUG_ON(idx > parent->frames.count);
+ ptr_array_insert(&parent->frames, frame, idx);
+ parent->window = NULL;
+ }
+ return frame;
+}
+
+Frame *new_root_frame(Window *window)
+{
+ return add_frame(NULL, window, 0);
+}
+
+static Frame *find_resizable(Frame *frame, ResizeDirection dir)
+{
+ if (dir == RESIZE_DIRECTION_AUTO) {
+ return frame;
+ }
+
+ while (frame->parent) {
+ if (dir == RESIZE_DIRECTION_VERTICAL && frame->parent->vertical) {
+ return frame;
+ }
+ if (dir == RESIZE_DIRECTION_HORIZONTAL && !frame->parent->vertical) {
+ return frame;
+ }
+ frame = frame->parent;
+ }
+ return NULL;
+}
+
+void set_frame_size(Frame *frame, int w, int h)
+{
+ int min_w = get_min_w(frame);
+ int min_h = get_min_h(frame);
+ w = MAX(w, min_w);
+ h = MAX(h, min_h);
+ frame->w = w;
+ frame->h = h;
+
+ if (frame->window) {
+ w -= rightmost_frame(frame) ? 0 : 1; // Separator
+ set_window_size(frame->window, w, h);
+ return;
+ }
+
+ if (frame->equal_size) {
+ divide_equally(frame);
+ } else {
+ fix_size(frame);
+ }
+}
+
+void equalize_frame_sizes(Frame *parent)
+{
+ parent->equal_size = true;
+ divide_equally(parent);
+ update_window_coordinates(parent);
+}
+
+void resize_frame(Frame *frame, ResizeDirection dir, int size)
+{
+ frame = find_resizable(frame, dir);
+ if (!frame) {
+ return;
+ }
+
+ Frame *parent = frame->parent;
+ parent->equal_size = false;
+ resize_to(frame, size);
+ update_window_coordinates(parent);
+}
+
+void add_to_frame_size(Frame *frame, ResizeDirection dir, int amount)
+{
+ resize_frame(frame, dir, get_size(frame) + amount);
+}
+
+static void update_frame_coordinates(const Frame *frame, int x, int y)
+{
+ if (frame->window) {
+ set_window_coordinates(frame->window, x, y);
+ return;
+ }
+
+ for (size_t i = 0, n = frame->frames.count; i < n; i++) {
+ const Frame *c = frame->frames.ptrs[i];
+ update_frame_coordinates(c, x, y);
+ if (frame->vertical) {
+ y += c->h;
+ } else {
+ x += c->w;
+ }
+ }
+}
+
+static Frame *get_root_frame(Frame *frame)
+{
+ BUG_ON(!frame);
+ while (frame->parent) {
+ frame = frame->parent;
+ }
+ return frame;
+}
+
+void update_window_coordinates(Frame *frame)
+{
+ update_frame_coordinates(get_root_frame(frame), 0, 0);
+}
+
+Frame *split_frame(Window *window, bool vertical, bool before)
+{
+ Frame *frame = window->frame;
+ Frame *parent = frame->parent;
+ if (!parent || parent->vertical != vertical) {
+ // Reparent window
+ frame->vertical = vertical;
+ add_frame(frame, window, 0);
+ parent = frame;
+ }
+
+ size_t idx = ptr_array_idx(&parent->frames, window->frame);
+ BUG_ON(idx >= parent->frames.count);
+ idx += before ? 0 : 1;
+ frame = add_frame(parent, new_window(window->editor), idx);
+ parent->equal_size = true;
+
+ // Recalculate
+ set_frame_size(parent, parent->w, parent->h);
+ update_window_coordinates(parent);
+ return frame;
+}
+
+// Doesn't really split root but adds new frame between root and its contents
+Frame *split_root_frame(EditorState *e, bool vertical, bool before)
+{
+ Frame *old_root = e->root_frame;
+ Frame *new_root = new_frame();
+ ptr_array_append(&new_root->frames, old_root);
+ old_root->parent = new_root;
+ new_root->vertical = vertical;
+ e->root_frame = new_root;
+
+ Frame *frame = add_frame(new_root, new_window(e), before ? 0 : 1);
+ set_frame_size(new_root, old_root->w, old_root->h);
+ update_window_coordinates(new_root);
+ return frame;
+}
+
+// NOTE: does not remove frame from frame->parent->frames
+static void free_frame(Frame *frame)
+{
+ frame->parent = NULL;
+ ptr_array_free_cb(&frame->frames, FREE_FUNC(free_frame));
+
+ if (frame->window) {
+ window_free(frame->window);
+ frame->window = NULL;
+ }
+
+ free(frame);
+}
+
+void remove_frame(EditorState *e, Frame *frame)
+{
+ Frame *parent = frame->parent;
+ if (!parent) {
+ free_frame(frame);
+ return;
+ }
+
+ ptr_array_remove(&parent->frames, frame);
+ free_frame(frame);
+
+ if (parent->frames.count == 1) {
+ // Replace parent with the only child frame
+ Frame *gp = parent->parent;
+ Frame *c = parent->frames.ptrs[0];
+ c->parent = gp;
+ c->w = parent->w;
+ c->h = parent->h;
+ if (gp) {
+ size_t idx = ptr_array_idx(&gp->frames, parent);
+ BUG_ON(idx >= gp->frames.count);
+ gp->frames.ptrs[idx] = c;
+ } else {
+ e->root_frame = c;
+ }
+ free(parent->frames.ptrs);
+ free(parent);
+ parent = c;
+ }
+
+ // Recalculate
+ set_frame_size(parent, parent->w, parent->h);
+ update_window_coordinates(parent);
+}
+
+void dump_frame(const Frame *frame, size_t level, String *str)
+{
+ sanity_check_frame(frame);
+ string_append_memset(str, ' ', level * 4);
+ string_sprintf(str, "%dx%d", frame->w, frame->h);
+
+ const Window *win = frame->window;
+ if (win) {
+ string_append_byte(str, '\n');
+ string_append_memset(str, ' ', (level + 1) * 4);
+ string_sprintf(str, "%d,%d %dx%d ", win->x, win->y, win->w, win->h);
+ string_append_cstring(str, buffer_filename(win->view->buffer));
+ string_append_byte(str, '\n');
+ return;
+ }
+
+ string_append_cstring(str, frame->vertical ? " V" : " H");
+ string_append_cstring(str, frame->equal_size ? "\n" : " !\n");
+
+ for (size_t i = 0, n = frame->frames.count; i < n; i++) {
+ const Frame *c = frame->frames.ptrs[i];
+ dump_frame(c, level + 1, str);
+ }
+}
+
+#if DEBUG >= 1
+void debug_frame(const Frame *frame)
+{
+ sanity_check_frame(frame);
+ for (size_t i = 0, n = frame->frames.count; i < n; i++) {
+ const Frame *c = frame->frames.ptrs[i];
+ BUG_ON(c->parent != frame);
+ debug_frame(c);
+ }
+}
+#endif