FLUID: Positioning grid cells intuitively.

User can now drag widgets from the toolbox into the grid
or use the context menu to add them into the corresponding
cell. If no position is indicated, now children are added at the
first free cell.
This commit is contained in:
Matthias Melcher 2023-10-23 01:36:55 +02:00
parent ab6ef9d52f
commit 17baeceb7a
5 changed files with 67 additions and 2 deletions

View File

@ -311,6 +311,9 @@ public:
void row_gap(const int *value, size_t size); void row_gap(const int *value, size_t size);
int row_gap(int row) const; int row_gap(int row) const;
int computed_col_width(int col) const;
int computed_row_height(int row) const;
/** /**
Enable or disable drawing of the grid helper lines for visualization. Enable or disable drawing of the grid helper lines for visualization.

View File

@ -421,18 +421,62 @@ static void move_cell(Fl_Grid *grid, Fl_Widget *child, int to_row, int to_col) {
if (new_cell) new_cell->minimum_size(w, h); if (new_cell) new_cell->minimum_size(w, h);
} }
void Fl_Grid_Type::insert_child_at(Fl_Widget *child, int x, int y) {
Fl_Grid *grid = (Fl_Grid*)o;
int row = -1, col = -1, ml, mt, grg, gcg;
grid->margin(&ml, &mt, NULL, NULL);
grid->gap(&grg, &gcg);
int x0 = grid->x() + Fl::box_dx(grid->box()) + ml;
int y0 = grid->y() + Fl::box_dy(grid->box()) + mt;
for (int r = 0; r < grid->rows(); r++) {
if (y>y0) row = r;
int gap = grid->row_gap(r)>=0 ? grid->row_gap(r) : grg;
y0 += grid->computed_row_height(r);
y0 += gap;
}
for (int c = 0; c < grid->cols(); c++) {
if (x>x0) col = c;
int gap = grid->col_gap(c)>=0 ? grid->col_gap(c) : gcg;
x0 += grid->computed_col_width(c);
x0 += gap;
}
move_cell(grid, child, row, col);
}
/** Insert a child window into the first new cell we can find .
There are many other possible strategies. How about inserting to the right
of the last added child. Also, what happens if the grid is full? Should
we add a new row at the bottom?
*/
void Fl_Grid_Type::insert_child(Fl_Widget *child) {
Fl_Grid *grid = (Fl_Grid*)o;
if (grid->cell(child)) return;
for (int r=0; r<grid->rows(); r++) {
for (int c=0; c<grid->cols(); c++) {
if (!grid->cell(r, c)) {
move_cell(grid, child, r, c);
return;
}
}
}
}
// FIXME: when changing the cell location, and another cell would be overridden, // FIXME: when changing the cell location, and another cell would be overridden,
// don't actually move the cell (hard to implement!) and activate // don't actually move the cell (hard to implement!) and activate
// a red button "replace". If clicked, user gets the option to delete // a red button "replace". If clicked, user gets the option to delete
// the old widget, or just remove the cell, or cancel. // the old widget, or just remove the cell, or cancel.
// TODO: move cells by using the arrow keys? // TODO: move cells by using the arrow keys?
// TODO: move cells via drag'n'drop // TODO: move cells via drag'n'drop
// TODO: insert cells when adding them from the menu or toolbar
// TODO: better grid overlay? // TODO: better grid overlay?
// TODO: grid_child_cb should move all selected cells. // TODO: grid_child_cb should move all selected cells, not just the current_selected.
// TODO: buttons to add and delete rows and columns in the widget dialog // TODO: buttons to add and delete rows and columns in the widget dialog
// TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu? // TODO: ways to resize rows and columns, add and delete them in the project window, pulldown menu?
// TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL? // TODO: alignment can be FL_GRID_LEFT|FL_GRID_VERTICAL?
// TODO: we must set undo checkpoints in all callbacks!
void grid_child_cb(Fluid_Coord_Input* i, void* v, int what) { void grid_child_cb(Fluid_Coord_Input* i, void* v, int what) {
static Fl_Widget *prev_widget = NULL; static Fl_Widget *prev_widget = NULL;
if ( !current_widget if ( !current_widget

View File

@ -45,6 +45,8 @@ public:
void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE; void move_child(Fl_Type*, Fl_Type*) FL_OVERRIDE;
void remove_child(Fl_Type*) FL_OVERRIDE; void remove_child(Fl_Type*) FL_OVERRIDE;
void child_resized(Fl_Widget_Type *child); void child_resized(Fl_Widget_Type *child);
void insert_child_at(Fl_Widget *child, int x, int y);
void insert_child(Fl_Widget *child);
static class Fl_Grid *selected(); static class Fl_Grid *selected();
}; };

View File

@ -26,6 +26,7 @@
#include "fluid.h" #include "fluid.h"
#include "Fl_Group_Type.h" #include "Fl_Group_Type.h"
#include "Fl_Grid_Type.h"
#include "Fl_Menu_Type.h" #include "Fl_Menu_Type.h"
#include "Fd_Snap_Action.h" #include "Fd_Snap_Action.h"
#include "pixmaps.h" #include "pixmaps.h"
@ -1225,6 +1226,13 @@ Fl_Type *add_new_widget_from_user(Fl_Type *inPrototype, Strategy strategy) {
wt->o->size(w, h); wt->o->size(w, h);
} }
} }
if (t->parent && t->parent->is_a(ID_Grid)) {
if (Fl_Window_Type::popupx != 0x7FFFFFFF) {
((Fl_Grid_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy);
} else {
((Fl_Grid_Type*)t->parent)->insert_child(((Fl_Widget_Type*)t)->o);
}
}
} }
if (t->is_a(ID_Window)) { if (t->is_a(ID_Window)) {
int x = 0, y = 0, w = 480, h = 320; int x = 0, y = 0, w = 480, h = 320;

View File

@ -1142,6 +1142,14 @@ int Fl_Grid::row_gap(int row) const {
return 0; return 0;
} }
int Fl_Grid::computed_col_width(int col) const {
return Cols_[col].w_;
}
int Fl_Grid::computed_row_height(int row) const {
return Rows_[row].h_;
}
/** /**
Output layout information of this Fl_Grid to stderr. Output layout information of this Fl_Grid to stderr.