1 #ifndef YLAYOUT_H
2 #define YLAYOUT_H
3 
4 /*
5  * Layout widgets.
6  * Use in dialogs.
7  */
8 
9 #include "yarray.h"
10 
11 class YWindow;
12 
13 /*
14  * Layout base interface.
15  */
16 class Sizeable {
17 public:
~Sizeable()18     virtual ~Sizeable() { }
19     virtual unsigned width() = 0;
20     virtual unsigned height() = 0;
21     virtual int x() = 0;
22     virtual int y() = 0;
23     virtual void setPosition(int x, int y) = 0;
24     virtual void realize() = 0;
25 
26     void layout(YWindow* outside);
27 };
28 
29 /*
30  * Create internal space in a dialog.
31  */
32 class Spacer : public Sizeable {
33 public:
Spacer(unsigned width,unsigned height)34     Spacer(unsigned width, unsigned height) :
35         fW(width), fH(height), fX(0), fY(0) { }
width()36     unsigned width() override { return fW; }
height()37     unsigned height() override { return fH; }
x()38     int x() override { return fX; }
y()39     int y() override { return fY; }
setPosition(int x,int y)40     void setPosition(int x, int y) override {
41         fX = x;
42         fY = y;
43     }
realize()44     void realize() override { }
45 protected:
46     unsigned fW, fH;
47     int fX, fY;
48 };
49 
50 /*
51  * Hold a YWindow.
52  */
53 class Cell : public Sizeable {
54 public:
Cell(YWindow * win)55     Cell(YWindow* win) : fWin(win) { }
~Cell()56     ~Cell() { delete fWin; }
width()57     unsigned width() override { return fWin->width(); }
height()58     unsigned height() override { return fWin->height(); }
x()59     int x() override { return fWin->x(); }
y()60     int y() override { return fWin->y(); }
setPosition(int x,int y)61     void setPosition(int x, int y) override {
62         fWin->setPosition(x, y);
63     }
realize()64     void realize() override { fWin->show(); }
65 protected:
66     YWindow* fWin;
67 };
68 
69 /*
70  * Create space around others.
71  */
72 class Padder : public Sizeable {
73 public:
Padder(Sizeable * win,int hpad,int vpad)74     Padder(Sizeable* win, int hpad, int vpad) :
75         fWin(win), fHori(hpad), fVert(vpad) { }
~Padder()76     ~Padder() { delete fWin; }
width()77     unsigned width() override { return fWin->width() + 2 * fHori; }
height()78     unsigned height() override { return fWin->height() + 2 * fVert; }
x()79     int x() override { return fWin->x() - fHori; }
y()80     int y() override { return fWin->y() - fVert; }
setPosition(int x,int y)81     void setPosition(int x, int y) override {
82         fWin->setPosition(x + fHori, y + fVert);
83     }
realize()84     void realize() override { fWin->realize(); }
85 protected:
86     Sizeable* fWin;
87     int fHori, fVert;
88 };
89 
90 /*
91  * Group sizeables vertically.
92  */
93 class Ladder : public Sizeable {
94 public:
95     Ladder& operator+=(Sizeable* step) {
96         fSteps += step;
97         return *this;
98     }
99     Ladder& operator+=(YWindow* win) {
100         return operator+=(new Cell(win));
101     }
empty()102     bool empty() const {
103         return fSteps.isEmpty();
104     }
width()105     unsigned width() override {
106         unsigned w = 1;
107         for (Sizeable* step : fSteps)
108             w = max(w, step->width());
109         return w;
110     }
height()111     unsigned height() override {
112         unsigned h = 0;
113         for (Sizeable* step : fSteps)
114             h += step->height();
115         return h;
116     }
x()117     int x() override { return empty() ? 0 : fSteps[0]->x(); }
y()118     int y() override { return empty() ? 0 : fSteps[0]->y(); }
setPosition(int x,int y)119     void setPosition(int x, int y) override {
120         for (Sizeable* step : fSteps) {
121             step->setPosition(x, y);
122             y += step->height();
123         }
124     }
realize()125     void realize() override {
126         for (Sizeable* step : fSteps)
127             step->realize();
128     }
129 
130 protected:
131     YObjectArray<Sizeable> fSteps;
132 };
133 
134 /*
135  * Group sizeables horizontally.
136  */
137 class Row : public Sizeable {
138 public:
139     Row(Sizeable* left, Sizeable* right = nullptr) :
fLeft(left)140         fLeft(left), fRight(right),
141         fOffset(fRight ? 20 + left->width() : 0) { }
142     Row(YWindow* left, YWindow* right = nullptr) :
143         Row(new Cell(left), right ? new Cell(right) : nullptr) { }
~Row()144     ~Row() {
145         delete fLeft;
146         delete fRight;
147     }
offset()148     unsigned offset() {
149         return fRight ? max(fOffset, fLeft->width()) : 0;
150     }
setOffset(unsigned newOffset)151     void setOffset(unsigned newOffset) {
152         if (fOffset != newOffset) {
153             fOffset = newOffset;
154             if (fRight)
155                 fRight->setPosition(x() + offset(), y());
156         }
157     }
width()158     unsigned width() override {
159         return fRight ? offset() + fRight->width() : fLeft->width();
160     }
height()161     unsigned height() override {
162         return max(fLeft->height(), fRight ? fRight->height() : 0);
163     }
x()164     int x() override { return fLeft->x(); }
y()165     int y() override { return fLeft->y(); }
setPosition(int x,int y)166     void setPosition(int x, int y) override {
167         fLeft->setPosition(x, y);
168         if (fRight)
169             fRight->setPosition(x + offset(), y);
170     }
realize()171     void realize() override {
172         fLeft->realize();
173         if (fRight)
174             fRight->realize();
175     }
swapColumns()176     void swapColumns() {
177         if (fLeft && fRight)
178             ::swap(fLeft, fRight);
179     }
180 
181 protected:
182     Sizeable* fLeft;
183     Sizeable* fRight;
184     unsigned fOffset;
185 };
186 
187 /*
188  * A grid of rows.
189  * The second column is aligned.
190  */
191 class Table : public Sizeable {
192 public:
Table()193     Table() { }
~Table()194     ~Table() { }
195     Table& operator+=(Row* row) {
196         if (row) fRows += row;
197         return *this;
198     }
width()199     unsigned width() override {
200         unsigned w = 1;
201         for (Sizeable* row : fRows)
202             w = max(w, row->width());
203         return w;
204     }
height()205     unsigned height() override {
206         unsigned h = 1;
207         for (Sizeable* row : fRows)
208             h = max(h, row->y() + row->height());
209         return h - y();
210     }
x()211     int x() override {
212         int x = fRows.nonempty() ? fRows[0]->x() : 0;
213         for (Sizeable* row : fRows)
214             x = min(x, row->x());
215         return x;
216     }
y()217     int y() override {
218         return fRows.nonempty() ? fRows[0]->y() : 0;
219     }
setPosition(int x,int y)220     void setPosition(int x, int y) override {
221         for (Sizeable* row : fRows) {
222             row->setPosition(x, y);
223             y += row->height();
224         }
225     }
realize()226     void realize() override {
227         unsigned offset = 0;
228         for (Row* row : fRows) {
229             offset = max(offset, row->offset());
230         }
231         for (Row* row : fRows) {
232             row->setOffset(offset);
233             row->realize();
234         }
235     }
swapColumns()236     void swapColumns() {
237         for (Row* row : fRows) {
238             row->swapColumns();
239         }
240     }
241 protected:
242     YObjectArray<Row> fRows;
243 };
244 
245 /*
246  * Communicate a layout to the outside world.
247  */
layout(YWindow * outside)248 inline void Sizeable::layout(YWindow* outside) {
249     setPosition(0, 0);
250     realize();
251     outside->setSize(width(), height());
252 }
253 
254 #endif
255 
256 // vim: set sw=4 ts=4 et:
257