Skip to content

Commit

Permalink
feat: draw arrow between fks, alow drag gesture on graph
Browse files Browse the repository at this point in the history
  • Loading branch information
ppvan committed Apr 17, 2024
1 parent 22030bd commit 7ce76d6
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 14 deletions.
79 changes: 72 additions & 7 deletions src/ui/widgets/Shape.vala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public interface Shape : Object {

public sealed class TextBox : Object, Shape {
public static int DEFAULT_PAD = 8;
public static int DEFAULT_LINE_HEIGHT = 20;

private string text;
private Gdk.Rectangle boundary;
Expand Down Expand Up @@ -97,6 +98,10 @@ public sealed class TableBox : Object, Shape {

public TableBox(Table table) {
this.table = table;

// this.boundary.height = (table.columns.length + 1) * (TextBox.DEFAULT_LINE_HEIGHT + 2 * TextBox.DEFAULT_PAD);
// this.boundary.width = 0;

}

public void update(UIContext ctx) {
Expand Down Expand Up @@ -134,12 +139,11 @@ public sealed class TableBox : Object, Shape {
index++;
}

int spacing = TextBox.DEFAULT_PAD * 16;
int next_y = -(table.foreign_keys.length * row_height + (table.foreign_keys.length - 1) * spacing / 2);
int next_x = width / 2 - boundary.width / 2 - TextBox.DEFAULT_PAD * 8;
int spacing = (height - table.foreign_keys.length * 2 * row_height) / (table.foreign_keys.length + 1);
int next_y = -(table.foreign_keys.length * row_height + (table.foreign_keys.length - 1) * spacing / 2);
int next_x = width / 2 - boundary.width / 2 - TextBox.DEFAULT_PAD * 8;
foreach (var fk in table.foreign_keys)
{
debug("name = %s: %s -> %s", fk.name, fk.table, fk.fk_table);
var fk_header = new TextBox(fk.fk_table, { next_x + TextBox.DEFAULT_PAD, next_y, boundary.width / 2, row_height });
fk_header.custom_font.set_weight(Pango.Weight.BOLD);
fk_header.bg_color = { 64 / 255f, 64 / 255f, 64 / 255f, 1 };
Expand All @@ -153,18 +157,79 @@ public sealed class TableBox : Object, Shape {

fk_compose_box.draw(cr, width, height);

string col_compose = string.joinv(", ", fk.columns);
var col_index = table.columns.find((_col) => {
return _col.name == col_compose;
});

if (col_index != -1) {
var arrow = new Arrow({ boundary.x + boundary.width, boundary.y + (col_index + 1) * row_height + row_height / 2 }, { next_x + TextBox.DEFAULT_PAD, next_y + row_height });
arrow.draw(cr, width, height);
} else {
// TODO: handle compose foreign key case (2 or more column in 1 fk)
}

next_y += 2 * row_height + spacing;
}
}
}

public class Arrow: Object, Shape {
public struct Vec2D
{
double x;
double y;

public Vec2D add(Vec2D other) {
return({ this.x + other.x, this.y + other.y });
}

public Vec2D substract(Vec2D other) {
return({ this.x - other.x, this.y - other.y });
}

public Vec2D orthogonal() {
return({ -this.y, this.x });
}

public Vec2D divide(double d) {
return({ this.x / d, this.y / d });
}

public Vec2D normalize() {
var length = GLib.Math.hypot(this.x, this.y);
return({ this.x / length, this.y / length });
}

public string to_str() {
return("(%.2f, %.2f)".printf(x, y));
}
}

public class Arrow : Object, Shape {
private Vec2D tail;
private Vec2D head;

public Arrow (int x1, int y1, int x2, int y2) {

public Arrow(Vec2D tail, Vec2D head) {
this.tail = tail;
this.head = head;
}

public void draw(Cairo.Context cr, int width, int height) {
assert_not_reached();
var orthogonal = tail.substract(head).orthogonal().normalize();
var mid = tail.add(head).divide(2);
var p2 = mid.add(orthogonal.divide(1 / 64.0));
var p1 = mid.substract(orthogonal.divide(1 / 64.0));

cr.move_to(tail.x, tail.y);

if (tail.y < head.y) {
cr.curve_to(p2.x, p2.y, p1.x, p1.y, head.x, head.y);
} else {
cr.curve_to(p1.x, p1.y, p2.x, p2.y, head.x, head.y);
}

cr.stroke();
}
}

Expand Down
40 changes: 36 additions & 4 deletions src/ui/widgets/TableGraph.vala
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public class TableGraph : Gtk.Box {
this.viewmodel = autowire <TableViewModel>();

this.viewmodel.notify["selected-table"].connect(() => {
debug("Test: %s", this.viewmodel.selected_table.name);
var table = this.viewmodel.selected_table;
this.current_table = new TableBox(table);
this.ctx = new UIContext();

area.queue_draw();
});
Expand All @@ -31,8 +31,14 @@ public class TableGraph : Gtk.Box {
var scrollEvent = new Gtk.EventControllerScroll(Gtk.EventControllerScrollFlags.VERTICAL);
scrollEvent.scroll.connect(this.handle_scroll);

var dragEvent = new Gtk.GestureDrag();
dragEvent.drag_update.connect(this.drag_update);
dragEvent.drag_end.connect(this.drag_end);



area.add_controller(scrollEvent);
area.add_controller(dragEvent);
area.set_draw_func(redraw);
});
}
Expand All @@ -58,14 +64,34 @@ public class TableGraph : Gtk.Box {
return(true);
}

private void drag_end(Gtk.GestureDrag drag, double x, double y) {
this.ctx.last_x += x;
this.ctx.last_y += y;
this.ctx.offset_x = 0;
this.ctx.offset_y = 0;
area.queue_draw();
}

private void drag_update(Gtk.GestureDrag drag, double x, double y) {
drag.get_offset(out x, out y);
this.ctx.offset_x = x;
this.ctx.offset_y = y;
area.queue_draw();
}



private void redraw(Gtk.DrawingArea area, Cairo.Context cr, int width, int height) {
cr.translate(width / 2, height / 2);


cr.translate(width / 2 + ctx.last_x + ctx.offset_x, height / 2 + ctx.last_y + ctx.offset_y);
cr.scale(ctx.zoom, ctx.zoom);

cr.set_source_rgb(30 / 255.0, 30 / 255.0, 30 / 255.0);
cr.paint();

var text_h = line_height(cr);
var text_h = line_height(cr);

var cur_color = this.get_color();


Expand All @@ -82,7 +108,7 @@ public class TableGraph : Gtk.Box {

private int line_height(Cairo.Context cr) {
var layout = Pango.cairo_create_layout(cr);
layout.set_font_description(Pango.FontDescription.from_string("Roboto 16"));
layout.set_font_description(Pango.FontDescription.from_string("Roboto 12"));
layout.set_text("jjjjjjjjjj", -1); // j is the highest character, good for line height measure.

int text_w = 0, text_h = 0;
Expand All @@ -98,6 +124,12 @@ public class TableGraph : Gtk.Box {
public class UIContext : Object {
public double mouse_x { get; set; }
public double mouse_y { get; set; }
public double offset_x { get; set; default = 0; }
public double offset_y { get; set; default = 0; }

public double last_x { get; set; default = 0; }
public double last_y { get; set; default = 0; }

public double zoom { get; set; default = 1.0; }

public UIContext() {
Expand Down
7 changes: 4 additions & 3 deletions src/viewmodels/TableViewModel.vala
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,7 @@ public class TableViewModel : BaseViewModel {
JOIN pg_class rel_cls ON idx.indrelid = rel_cls.oid
JOIN pg_namespace nsp ON cls.relnamespace = nsp.oid
JOIN pg_am am ON am.oid = cls.relam
WHERE nsp.nspname = $1 AND cls.relkind = 'i';
WHERE nsp.nspname = $1 AND cls.relkind = 'i' AND NOT indisprimary;
""";

public const string PK_SQL = """
Expand All @@ -225,7 +224,8 @@ public class TableViewModel : BaseViewModel {
cls1.relname AS src_table,
ARRAY_AGG(attr1.attname) AS src_columns,
cls2.relname AS dest_table,
ARRAY_AGG(attr2.attname) AS dest_columns
ARRAY_AGG(attr2.attname) AS dest_columns,
ARRAY_AGG(attr1.attnum) AS src_columns_num
FROM pg_catalog.pg_constraint con
JOIN pg_catalog.pg_class cls1 ON con.conrelid = cls1.oid
JOIN pg_catalog.pg_class cls2 ON con.confrelid = cls2.oid
Expand All @@ -239,6 +239,7 @@ public class TableViewModel : BaseViewModel {
AND attr1.attnum = ANY(con.conkey)
AND attr2.attnum = ANY(con.confkey)
GROUP BY nsp.nspname, con.oid, cls1.relname, cls2.relname
ORDER BY src_table, src_columns_num;
""";
}
}

0 comments on commit 7ce76d6

Please sign in to comment.