summaryrefslogtreecommitdiff
path: root/iup/src/iup_layout.c
blob: 9387135fa8069b9f01d1a381f73bbc32e2a7932a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/** \file
 * \brief Abstract Layout Management
 *
 * See Copyright Notice in "iup.h"
 */

#include <stdlib.h>  
#include <stdio.h>  
#include <stdarg.h>  

#include "iup.h"

#include "iup_object.h"
#include "iup_drv.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_layout.h"
#include "iup_assert.h" 

 
void IupRefresh(Ihandle* ih)
{
  Ihandle* dialog;

  iupASSERT(iupObjectCheck(ih));
  if (!iupObjectCheck(ih))
    return;

  dialog = IupGetDialog(ih);
  if (dialog)
  {
    iupLayoutCompute(dialog);
    if (dialog->handle)
      iupLayoutUpdate(dialog);
  }
}

static void iLayoutDisplayUpdateChildren(Ihandle *ih)
{
  Ihandle* child;
  for (child = ih->firstchild; child; child = child->brother)
  {
    iLayoutDisplayUpdateChildren(child);

    if (child->handle && child->iclass->nativetype != IUP_TYPEVOID)
      iupdrvPostRedraw(child);
  }
}

void IupUpdate(Ihandle* ih)
{
  iupASSERT(iupObjectCheck(ih));
  if (!iupObjectCheck(ih))
    return;

  if (ih->handle && ih->iclass->nativetype != IUP_TYPEVOID)
    iupdrvPostRedraw(ih);
}

void IupUpdateChildren(Ihandle* ih)
{
  iupASSERT(iupObjectCheck(ih));
  if (!iupObjectCheck(ih))
    return;

  iLayoutDisplayUpdateChildren(ih);
}

static void iLayoutDisplayRedrawChildren(Ihandle *ih)
{
  Ihandle* child;
  for (child = ih->firstchild; child; child = child->brother)
  {
    iLayoutDisplayRedrawChildren(child);

    if (child->handle && child->iclass->nativetype != IUP_TYPEVOID)
      iupdrvRedrawNow(child);
  }
}

void IupRedraw(Ihandle* ih, int children)
{
  iupASSERT(iupObjectCheck(ih));
  if (!iupObjectCheck(ih))
    return;

  if (ih->handle && ih->iclass->nativetype != IUP_TYPEVOID)
    iupdrvRedrawNow(ih);

  if (children)
    iLayoutDisplayRedrawChildren(ih);
}

void iupLayoutUpdate(Ihandle* ih)
{
  Ihandle* child;

  /* update size and position of the native control */
  iupClassObjectLayoutUpdate(ih);

  /* update its children */
  for (child = ih->firstchild; child; child = child->brother)
  {
    if (child->handle)
      iupLayoutUpdate(child);
  }
}

void iupLayoutCompute(Ihandle* ih)
{
  int shrink = iupAttribGetBoolean(ih, "SHRINK");

  /* Compute the natural size for all elements in the dialog,   
     using the minimum visible size and the defined user size.
     The minimum visible size is the size where all the controls can display
     all their contents.
     The defined user size is used to increase the value of the minimum visible size for containers,
     for standard controls will replace the minimum visible size.
     So the native size will be the maximum value between 
     minimum visible size and defined user size.
     Also calculates the expand configuration for each element, but expand is used only in SetChildrenCurrentSize.
     SEQUENCE: will first calculate the native size for the children, then for the element. */
  iupBaseComputeNaturalSize(ih);

  /* Set the current size (not reflected in the native element yet) based on
     the natural size and the expand configuration. 
     If shrink is 0 (default) the current size of containers can be only larger than the natural size,
     the result will depend on the EXPAND attribute.
     If shrink is 1 the containers can be resized to sizes smaller than the natural size.
     SEQUENCE: will first calculate the current size of the element, then for the children. */
  iupBaseSetCurrentSize(ih, 0, 0, shrink);

  /* Now that the current size is known, set the position of the elements 
     relative to the parent.
     SEQUENCE: will first set the position of the element, then for the children. */
  iupBaseSetPosition(ih, 0, 0);
}

void iupLayoutSetMinMaxSize(Ihandle* ih, int *w, int *h)
{
  if (ih->has_minsize)
  {
    char* value = iupAttribGet(ih, "MINSIZE");
    int min_w = 0, min_h = 0;          /* MINSIZE default value */
    iupStrToIntInt(value, &min_w, &min_h, 'x');
    if (w && *w < min_w) *w = min_w;
    if (h && *h < min_h) *h = min_h;
  }

  if (ih->has_maxsize)
  {
    char* value = iupAttribGet(ih, "MAXSIZE");
    int max_w = 65535, max_h = 65535;  /* MAXSIZE default value */
    iupStrToIntInt(value, &max_w, &max_h, 'x');
    if (w && *w > max_w) *w = max_w;
    if (h && *h > max_h) *h = max_h;
  }
}

void iupBaseComputeNaturalSize(Ihandle* ih)
{
  /* always initialize the natural size using the user size */
  ih->naturalwidth = ih->userwidth;
  ih->naturalheight = ih->userheight;

  if (ih->iclass->childtype!=IUP_CHILDNONE || 
      ih->iclass->nativetype == IUP_TYPEDIALOG)  /* pre-defined dialogs can restrict the number of children */
  {
    int w=0, h=0, children_expand;

    /* if a container then update the "expand" member from the EXPAND attribute */
    iupBaseContainerUpdateExpand(ih);
    children_expand = ih->expand; /* use it as default value */

    iupClassObjectComputeNaturalSize(ih, &w, &h, &children_expand);

    if (ih->iclass->nativetype == IUP_TYPEDIALOG)
    {
      /* only update the natural size if user size is not defined. */
      /* IupDialog is the only container where this must be done */ 
      /* if the natural size is bigger than the actual dialog size then
         the dialog will be resized, if smaller then the dialog remains with the same size. */
      ih->expand |= children_expand;
      if (ih->naturalwidth <= 0) ih->naturalwidth = iupMAX(ih->currentwidth, w);
      if (ih->naturalheight <= 0) ih->naturalheight = iupMAX(ih->currentheight, h);
    }
    else
    {
      ih->expand &= children_expand; /* combine but only expand where the element can expand */
      ih->naturalwidth = iupMAX(ih->naturalwidth, w);
      ih->naturalheight = iupMAX(ih->naturalheight, h);

      /* crop the natural size */
      iupLayoutSetMinMaxSize(ih, &(ih->naturalwidth), &(ih->naturalheight));
    }
  }
  else 
  {
    /* for non-container only compute if user size is not defined */
    if (ih->naturalwidth <= 0 || ih->naturalheight <= 0)
    {
      int w=0, h=0, children_expand;
      iupClassObjectComputeNaturalSize(ih, &w, &h, &children_expand);

      if (ih->naturalwidth <= 0) ih->naturalwidth = w;
      if (ih->naturalheight <= 0) ih->naturalheight = h;
    }

    /* crop the natural size */
    iupLayoutSetMinMaxSize(ih, &(ih->naturalwidth), &(ih->naturalheight));
  }
}

void iupBaseSetCurrentSize(Ihandle* ih, int w, int h, int shrink)
{
  if (ih->iclass->nativetype == IUP_TYPEDIALOG)
  {
    /* w and h parameters here are ignored, because they are always 0 for the dialog. */

    /* current size is zero before map and when reset by the application */
    /* after that the current size must follow the actual size of the dialog */
    if (!ih->currentwidth)  ih->currentwidth  = ih->naturalwidth;
    if (!ih->currentheight) ih->currentheight = ih->naturalheight;

    if (ih->firstchild)
      iupClassObjectSetChildrenCurrentSize(ih, shrink);
  }
  else
  {
    if (ih->iclass->childtype!=IUP_CHILDNONE)
    {
      if (shrink)
      {
        /* if expand then use the given size, else use the natural size */
        /* this expand is a combination of the expand defined for the element and its children */
        ih->currentwidth  = (ih->expand & IUP_EXPAND_WIDTH)?  w: ih->naturalwidth;
        ih->currentheight = (ih->expand & IUP_EXPAND_HEIGHT)? h: ih->naturalheight;
      }
      else
      {
        /* if expand then use the given size (if greater than natural size), else use the natural size */
        /* this expand is a combination of the expand defined for the element and its children */
        ih->currentwidth  = (ih->expand & IUP_EXPAND_WIDTH)?  iupMAX(ih->naturalwidth, w):  ih->naturalwidth;
        ih->currentheight = (ih->expand & IUP_EXPAND_HEIGHT)? iupMAX(ih->naturalheight, h): ih->naturalheight;
      }

      if (ih->firstchild)
        iupClassObjectSetChildrenCurrentSize(ih, shrink);
    }
    else
    {
      /* shrink is only used by containers, usually is 0 */
      /* for non containers is always 1, so they always can be smaller than the natural size */

      /* if expand use the given size, else use the natural size */
      /* this expand is the defined for the element */
      ih->currentwidth = (ih->expand & IUP_EXPAND_WIDTH)? w: ih->naturalwidth;
      ih->currentheight = (ih->expand & IUP_EXPAND_HEIGHT)? h: ih->naturalheight;
    }

    /* crop the current size if expanded */
    if (ih->expand & IUP_EXPAND_WIDTH || ih->expand & IUP_EXPAND_HEIGHT)
      iupLayoutSetMinMaxSize(ih, &(ih->currentwidth), &(ih->currentheight));
  }
}

void iupBaseSetPosition(Ihandle* ih, int x, int y)
{
  ih->x = x;
  ih->y = y;

  if (ih->firstchild)
    iupClassObjectSetChildrenPosition(ih, x, y);
}