summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_Grid.H21
-rw-r--r--fluid/Fl_Grid_Type.cxx263
-rw-r--r--fluid/Fl_Grid_Type.h15
-rw-r--r--fluid/alignment_panel.fl32
-rw-r--r--fluid/documentation/src-dev/page_introduction.dox19
5 files changed, 307 insertions, 43 deletions
diff --git a/FL/Fl_Grid.H b/FL/Fl_Grid.H
index b240ca95b..403c6c0f3 100644
--- a/FL/Fl_Grid.H
+++ b/FL/Fl_Grid.H
@@ -159,10 +159,10 @@ public:
public:
- Cell(int row, int col) {
+ void Cell_() {
next_ = NULL;
- row_ = row;
- col_ = col;
+ row_ = 0;
+ col_ = 0;
rowspan_ = 1;
colspan_ = 1;
widget_ = NULL;
@@ -171,6 +171,19 @@ public:
align_ = 0;
}
+ Cell(int row, int col) {
+ Cell_();
+ row_ = row;
+ col_ = col;
+ }
+
+ Cell(Fl_Widget *w, int row, int col) {
+ Cell_();
+ widget_ = w;
+ row_ = row;
+ col_ = col;
+ }
+
~Cell() {}
Fl_Widget *widget() { return widget_; }
@@ -211,7 +224,7 @@ protected:
Fl_Color grid_color; // color for drawing the grid lines (design helper)
bool draw_grid_; // draw the grid for testing / design
-private:
+protected:
void init();
Cell *add_cell(int row, int col);
void remove_cell(int row, int col);
diff --git a/fluid/Fl_Grid_Type.cxx b/fluid/Fl_Grid_Type.cxx
index 33c061291..493565b6a 100644
--- a/fluid/Fl_Grid_Type.cxx
+++ b/fluid/Fl_Grid_Type.cxx
@@ -32,9 +32,34 @@
#include <stdio.h>
#include <stdlib.h>
+#include <assert.h>
// ---- Fl_Grid_Proxy --------------------------------------------------- MARK: -
+/**
+ An implementation of the Fl_Grid widget with additional functionality.
+
+ Fl_Grid_Proxy add a list of transient children, i.e. children that are
+ temporarily assigned to a cell that is already taken by another child.
+ */
+Fl_Grid_Proxy::Fl_Grid_Proxy(int X,int Y,int W,int H)
+: Fl_Grid(X,Y,W,H),
+ transient_(NULL),
+ num_transient_(0),
+ cap_transient_(0)
+{
+}
+
+Fl_Grid_Proxy::~Fl_Grid_Proxy() {
+ int i;
+ if (transient_) {
+ for (i=0; i<num_transient_; i++) {
+ if (transient_[i].cell) ::free(transient_[i].cell);
+ }
+ ::free(transient_);
+ }
+}
+
// Override group's resize behavior to do nothing to children:
void Fl_Grid_Proxy::resize(int X, int Y, int W, int H) {
if (Fl_Type::allow_layout > 0) {
@@ -65,6 +90,183 @@ void Fl_Grid_Proxy::draw_overlay() {
fl_color(grid_color);
}
+/**
+ Move a cell into the grid or within the grid.
+
+ \param[in] in_child must already be a child of grid
+ \param[in] to_row, to_col move the child into this cell
+ \param[in] how 0: replace occupant, 1: don't replace, 2 = make transient
+ if occupied
+
+ \todo If the target cell is already taken, this function will remove the old
+ child from the cell and leave it in limbo (a child of grid, but with no
+ assigned cell). As this function is used to move children around with arrow
+ keys, it must be possible to move a widget past a cell that is already taken
+ without disturbing the existing system.
+
+ One solution would be to make the widget "hover", meaning, it will be registered
+ in Fl_Grid_Proxy as using the same cell as another widget. I am not sure at all
+ how to implement that yet. We could return the original cell, but that may have
+ different minsize, etc. . It would be better to have a temporary cell to store
+ all the information.
+
+ We must make sure that a hovering cell (or multiple cells) are a very temporary
+ solution, because all this information will be skewed if Grid::layout is
+ called, and all information is lost when we save the file for compilation.
+
+ We must show a conflict grid under or around the hovering cell!
+
+ Also mind the rowspan and colspan!
+ */
+void Fl_Grid_Proxy::move_cell(Fl_Widget *in_child, int to_row, int to_col, int how) {
+ // the child must already be a true child of grid
+ assert(find(in_child)<children());
+
+ short rowspan = 1, colspan = 1;
+ Fl_Grid_Align align = FL_GRID_FILL;
+ int w = 20, h = 20;
+ const Fl_Grid::Cell *old_cell = cell(in_child);
+ if (old_cell) {
+ rowspan = old_cell->rowspan();
+ colspan = old_cell->colspan();
+ align = old_cell->align();
+ old_cell->minimum_size(&w, &h);
+ }
+ if ((to_row < 0) || (to_row+rowspan > rows())) return;
+ if ((to_col < 0) || (to_col+colspan > cols())) return;
+ Fl_Grid::Cell *new_cell = NULL;
+ if (how == 0) { // replace old occupant in cell, making that one homeless
+ new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align);
+ } else if (how == 1) { // don't replace an old occupant, making ourselves homeless
+ // todo: colspan, rowspan?
+ if (cell(to_row, to_col) == NULL) {
+ new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align);
+ } else {
+ if (old_cell) remove_cell(old_cell->row(), old_cell->col());
+ }
+ } else if (how == 2) {
+ Cell *current = cell(to_row, to_col);
+ if (current == NULL) {
+ new_cell = widget(in_child, to_row, to_col, rowspan, colspan, align);
+ } else {
+ if (old_cell) remove_cell(old_cell->row(), old_cell->col());
+ new_cell = transient_widget(in_child, to_row, to_col, rowspan, colspan, align);
+ Fl_Widget *w = current->widget();
+ Fl_Type::allow_layout++;
+ in_child->resize(w->x(), w->y(), w->w(), w->h());
+ Fl_Type::allow_layout--;
+ }
+ }
+ if (new_cell) new_cell->minimum_size(w, h);
+}
+
+/**
+ Generate or replace a transient widget entry.
+ If the widget is in the cell list, it will be removed there.
+ If the widget is already transient, the cell will be replaced.
+ \param[in] wi a child of this Fl_Grid_Proxy, that may be linked to a cell or transient cell
+ \param[in] row, col, row_span, col_span, align cell parameters
+ */
+Fl_Grid::Cell* Fl_Grid_Proxy::transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align) {
+ int i;
+ bool remove_old_cell = false;
+ Cell *old_cell = cell(wi);
+ if (old_cell) {
+ remove_old_cell = true;
+ } else {
+ for (i=0; i<num_transient_; i++) {
+ if (transient_[i].widget == wi) {
+ old_cell = transient_[i].cell;
+ break;
+ }
+ }
+ }
+ Cell *new_cell = new Cell(wi, row, col);
+ new_cell->rowspan(row_span);
+ new_cell->colspan(col_span);
+ new_cell->align(align);
+ if (old_cell) {
+ int mw, mh;
+ old_cell->minimum_size(&mw, &mh);
+ new_cell->minimum_size(mw, mh);
+ ::free(old_cell);
+ }
+ if (i == num_transient_) {
+ transient_make_room_(num_transient_ + 1);
+ transient_[i].widget = wi;
+ num_transient_++;
+ }
+ transient_[i].cell = new_cell;
+ if (remove_old_cell) {
+ remove_cell(old_cell->row(), old_cell->col());
+ }
+ return new_cell;
+}
+
+/**
+ Make room for at least n transient widgets in the array.
+ \param[in] n minimum number of entries
+ */
+void Fl_Grid_Proxy::transient_make_room_(int n) {
+ if (n > cap_transient_) {
+ cap_transient_ = n + 10;
+ transient_ = (Cell_Widget_Pair*)::realloc(transient_, cap_transient_ * sizeof(Cell_Widget_Pair));
+ }
+}
+
+/**
+ Remove a widget form the list and deallocate the transient cell.
+ \param[in] w remove the transient cell for this widget
+ */
+void Fl_Grid_Proxy::transient_remove_(Fl_Widget *w) {
+ for (int i=0; i<num_transient_; i++) {
+ if (transient_[i].widget==w) {
+ if (transient_[i].cell) {
+ ::free(transient_[i].cell);
+ ::memmove(transient_+i, transient_+i+1, sizeof(Cell_Widget_Pair)*(num_transient_-i-1));
+ num_transient_--;
+ return;
+ }
+ }
+ }
+}
+
+/**
+ Find a cell in the grid or in the transient cell list.
+ \param[in] widget must be a child of the grid.
+ \return the cell, the transient cell, or NULL if neither was found.
+ */
+Fl_Grid_Proxy::Cell *Fl_Grid_Proxy::any_cell(Fl_Widget *widget) const {
+ Cell *c = cell(widget);
+ if (c) return c;
+ for (int i=0; i<num_transient_; i++) {
+ if (transient_[i].widget == widget)
+ return transient_[i].cell;
+ }
+ return NULL;
+}
+
+/**
+ Forwarding the call.
+ \param[in] wi generate a cell for this widget
+ \param[in] row, col, align cell parameters
+ */
+Fl_Grid::Cell *Fl_Grid_Proxy::widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align) {
+ return widget(wi, row, col, 1, 1, align);
+}
+
+/**
+ Just like the Fl_Grid original, but removes potential transient cell.
+ \param[in] wi generate a cell for this widget
+ \param[in] row, col, rowspan, colspan, align cell parameters
+ */
+Fl_Grid::Cell *Fl_Grid_Proxy::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align) {
+ transient_remove_(wi);
+ return Fl_Grid::widget(wi, row, col, rowspan, colspan, align);
+}
+
+
+
// ---- Fl_Grid_Type --------------------------------------------------- MARK: -
const char grid_type_name[] = "Fl_Grid";
@@ -432,29 +634,8 @@ Fl_Grid *Fl_Grid_Type::selected() {
return NULL;
}
-extern Fluid_Coord_Input *widget_grid_row_input, *widget_grid_col_input,
- *widget_grid_rowspan_input, *widget_grid_colspan_input;
-extern Fl_Group *widget_tab_grid_child;
-
-static void move_cell(Fl_Grid *grid, Fl_Widget *child, int to_row, int to_col) {
- short rowspan = 1, colspan = 1;
- Fl_Grid_Align align = FL_GRID_FILL;
- int w = 20, h = 20;
- const Fl_Grid::Cell *old_cell = grid->cell(child);
- if (old_cell) {
- rowspan = old_cell->rowspan();
- colspan = old_cell->colspan();
- align = old_cell->align();
- old_cell->minimum_size(&w, &h);
- }
- if ((to_row<0) || (to_row+rowspan>grid->rows())) return;
- if ((to_col<0) || (to_col+colspan>grid->cols())) return;
- Fl_Grid::Cell *new_cell = grid->widget(child, to_row, to_col, rowspan, colspan, align);
- 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;
+ Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)o;
int row = -1, col = -1, ml, mt, grg, gcg;
grid->margin(&ml, &mt, NULL, NULL);
grid->gap(&grg, &gcg);
@@ -475,22 +656,24 @@ void Fl_Grid_Type::insert_child_at(Fl_Widget *child, int x, int y) {
x0 += gap;
}
- move_cell(grid, child, row, col);
+ grid->move_cell(child, row, col);
}
-/** Insert a child window into the first new cell we can find .
+/** Insert a child widget 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?
+
+ /param[in] child
*/
void Fl_Grid_Type::insert_child(Fl_Widget *child) {
- Fl_Grid *grid = (Fl_Grid*)o;
+ Fl_Grid_Proxy *grid = (Fl_Grid_Proxy*)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);
+ grid->move_cell(child, r, c);
return;
}
}
@@ -506,17 +689,17 @@ void Fl_Grid_Type::insert_child(Fl_Widget *child) {
\param[in] key code of the last keypress when handling a FL_KEYBOARD event.
*/
void Fl_Grid_Type::keyboard_move_child(Fl_Widget_Type *child, int key) {
- Fl_Grid *grid = ((Fl_Grid*)o);
- Fl_Grid::Cell *cell = grid->cell(child->o);
+ Fl_Grid_Proxy *grid = ((Fl_Grid_Proxy*)o);
+ Fl_Grid::Cell *cell = grid->any_cell(child->o);
if (!cell) return;
if (key == FL_Right) {
- move_cell(grid, child->o, cell->row(), cell->col()+1);
+ grid->move_cell(child->o, cell->row(), cell->col()+1, 2);
} else if (key == FL_Left) {
- move_cell(grid, child->o, cell->row(), cell->col()-1);
+ grid->move_cell(child->o, cell->row(), cell->col()-1, 2);
} else if (key == FL_Up) {
- move_cell(grid, child->o, cell->row()-1, cell->col());
+ grid->move_cell(child->o, cell->row()-1, cell->col(), 2);
} else if (key == FL_Down) {
- move_cell(grid, child->o, cell->row()+1, cell->col());
+ grid->move_cell(child->o, cell->row()+1, cell->col(), 2);
}
}
@@ -544,6 +727,12 @@ void Fl_Grid_Type::layout_widget() {
// 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: we must set undo checkpoints in all callbacks!
+
+extern Fluid_Coord_Input *widget_grid_row_input, *widget_grid_col_input,
+*widget_grid_rowspan_input, *widget_grid_colspan_input;
+extern Fl_Group *widget_tab_grid_child;
+
+
void grid_child_cb(Fluid_Coord_Input* i, void* v, int what) {
static Fl_Widget *prev_widget = NULL;
if ( !current_widget
@@ -553,7 +742,7 @@ void grid_child_cb(Fluid_Coord_Input* i, void* v, int what) {
return;
}
Fl_Widget *child = ((Fl_Widget_Type*)current_widget)->o;
- Fl_Grid *g = ((Fl_Grid*)((Fl_Widget_Type*)current_widget->parent)->o);
+ Fl_Grid_Proxy *g = ((Fl_Grid_Proxy*)((Fl_Widget_Type*)current_widget->parent)->o);
Fl_Grid::Cell *cell = g->cell(child);
bool freeze_row_col = (!cell && prev_widget==child && ((what&0x00ff)==8 || (what&0x00ff)==9));
if (v == LOAD) {
@@ -593,11 +782,11 @@ void grid_child_cb(Fluid_Coord_Input* i, void* v, int what) {
}
if (old_v != v) {
switch (what & 0x00ff) {
- case 8: if (v>=0 && v2>=0) move_cell(g, current_widget->o, v, v2);
- if (freeze_row_col) i->value(v);
+ case 8: if (v>=0 && v2>=0) g->move_cell(current_widget->o, v, v2);
+ if (freeze_row_col) i->value(v);
break;
- case 9: if (v>=0 && v2>=0) move_cell(g, current_widget->o, v2, v);
- if (freeze_row_col) i->value(v);
+ case 9: if (v>=0 && v2>=0) g->move_cell(current_widget->o, v2, v);
+ if (freeze_row_col) i->value(v);
break;
case 10: if (cell && cell->row()+v<=g->rows()) cell->rowspan(v);
break;
diff --git a/fluid/Fl_Grid_Type.h b/fluid/Fl_Grid_Type.h
index 5ab87b8a5..9780ace76 100644
--- a/fluid/Fl_Grid_Type.h
+++ b/fluid/Fl_Grid_Type.h
@@ -25,11 +25,24 @@
extern const char grid_type_name[];
class Fl_Grid_Proxy : public Fl_Grid {
+protected:
+ typedef struct { Fl_Widget *widget; Cell *cell; } Cell_Widget_Pair;
+ Cell_Widget_Pair *transient_;
+ int num_transient_;
+ int cap_transient_;
+ void transient_make_room_(int n);
+ void transient_remove_(Fl_Widget *w);
public:
- Fl_Grid_Proxy(int X,int Y,int W,int H) : Fl_Grid(X,Y,W,H) {}
+ Fl_Grid_Proxy(int X,int Y,int W,int H);
+ ~Fl_Grid_Proxy();
void resize(int,int,int,int) FL_OVERRIDE;
void draw() FL_OVERRIDE;
void draw_overlay();
+ void move_cell(Fl_Widget *child, int to_row, int to_col, int how = 0);
+ Cell* any_cell(Fl_Widget *widget) const;
+ Cell* transient_widget(Fl_Widget *wi, int row, int col, int row_span, int col_span, Fl_Grid_Align align = FL_GRID_FILL);
+ Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL);
+ Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL);
};
class Fl_Grid_Type : public Fl_Group_Type
diff --git a/fluid/alignment_panel.fl b/fluid/alignment_panel.fl
index 464970348..66335f095 100644
--- a/fluid/alignment_panel.fl
+++ b/fluid/alignment_panel.fl
@@ -282,7 +282,7 @@ Examples:
}
Fl_Check_Button ghosted_outline_button {
label {Show Ghosted Group Outlines}
- callback toggle_ghosted_outline_cb selected
+ callback toggle_ghosted_outline_cb
tooltip {groups with no box type or flat boxtypes without contrast will be rendered with a dim outline in the editing window only} xywh {120 340 200 20} down_box DOWN_BOX labelsize 11
code0 {o->value(show_ghosted_outline);}
}
@@ -1565,6 +1565,36 @@ settings_window->hide();}
}
}
}
+ Fl_Window {} {open
+ xywh {646 417 480 320} type Double visible
+ } {
+ Fl_Grid {} {open
+ xywh {25 25 240 160}
+ dimensions {3 3}
+ } {
+ Fl_Button {} {
+ label Button
+ xywh {25 25 80 66}
+ parent_properties {
+ location {0 0}
+ }
+ }
+ Fl_Light_Button {} {
+ label Button
+ xywh {105 25 80 66}
+ parent_properties {
+ location {0 1}
+ }
+ }
+ Fl_Button {} {
+ label Button selected
+ xywh {185 25 80 66}
+ parent_properties {
+ location {0 2}
+ }
+ }
+ }
+ }
code {w_settings_tabs->do_callback(w_settings_tabs, LOAD);} {}
}
diff --git a/fluid/documentation/src-dev/page_introduction.dox b/fluid/documentation/src-dev/page_introduction.dox
index 1db902d44..5684eeeda 100644
--- a/fluid/documentation/src-dev/page_introduction.dox
+++ b/fluid/documentation/src-dev/page_introduction.dox
@@ -64,4 +64,23 @@
...
+ ## Wish List ##
+
+ I call this a wish list because I write my wishes down as I go. There is no
+ verification yet, or alternative ideas.
+
+ - group source files into subdirectories
+ - all panels should have their own folder
+ - all custom widgets
+ - all file and stream operations
+ - all utilities and tools
+ - move to C++17 and use std::string, vector, map, ...
+ - Fl_Type::write#() could go into a single write function with an enum to
+ describe what needs to be written to make thing logical and expandable
+ - better error handling in all file classes
+ - separate FLUID generated files form the core and build a shell-only version
+ - individual event handling for types
+ - individual overlay drawing for types
+ - plug-ins for new types
+
*/