00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "../ui_main.h"
00026 #include "../ui_parse.h"
00027 #include "../ui_render.h"
00028 #include "../ui_actions.h"
00029 #include "ui_node_abstractnode.h"
00030 #include "ui_node_panel.h"
00031 #include "../../../common/scripts.h"
00032
00033 #define EXTRADATA_TYPE panelExtraData_t
00034 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
00035
00036 #define CORNER_SIZE 25
00037 #define MID_SIZE 1
00038 #define MARGE 3
00039
00040 static const value_t *propertyLayoutMargin;
00041 static const value_t *propertyLayoutColumns;
00042 static const value_t *propertyPadding;
00043
00044 static const uiBehaviour_t const *localBehaviour;
00045
00049 static void UI_PanelNodeDraw (uiNode_t *node)
00050 {
00051 static const int panelTemplate[] = {
00052 CORNER_SIZE, MID_SIZE, CORNER_SIZE,
00053 CORNER_SIZE, MID_SIZE, CORNER_SIZE,
00054 MARGE
00055 };
00056 const char *image;
00057 vec2_t pos;
00058
00059 UI_GetNodeAbsPos(node, pos);
00060
00061 image = UI_GetReferenceString(node, node->image);
00062 if (image)
00063 UI_DrawPanel(pos, node->size, image, 0, 0, panelTemplate);
00064 }
00065
00074 static void UI_TopDownFlowLayout (uiNode_t *node, int margin)
00075 {
00076 const int width = node->size[0] - node->padding - node->padding;
00077 int positionY = node->padding;
00078 uiNode_t *child = node->firstChild;
00079 vec2_t newSize = {width, 0};
00080
00081 while (child) {
00082 newSize[1] = child->size[1];
00083 UI_NodeSetSize(child, newSize);
00084 child->pos[0] = node->padding;
00085 child->pos[1] = positionY;
00086 positionY += child->size[1] + margin;
00087 child = child->next;
00088 }
00089 }
00090
00102 static void UI_BorderLayout (uiNode_t *node, int margin)
00103 {
00104 uiNode_t *child;
00105 vec2_t newSize;
00106 int minX = node->padding;
00107 int maxX = node->size[0] - node->padding;
00108 int minY = node->padding;
00109 int maxY = node->size[1] - node->padding;
00110
00111
00112 for (child = node->firstChild; child; child = child->next) {
00113 if (child->align != LAYOUTALIGN_TOP)
00114 continue;
00115 if (child->invis)
00116 continue;
00117 newSize[0] = maxX - minX;
00118 newSize[1] = child->size[1];
00119 UI_NodeSetSize(child, newSize);
00120 child->pos[0] = minX;
00121 child->pos[1] = minY;
00122 minY += child->size[1] + margin;
00123 }
00124
00125
00126 for (child = node->firstChild; child; child = child->next) {
00127 if (child->align != LAYOUTALIGN_BOTTOM)
00128 continue;
00129 if (child->invis)
00130 continue;
00131 newSize[0] = maxX - minX;
00132 newSize[1] = child->size[1];
00133 UI_NodeSetSize(child, newSize);
00134 child->pos[0] = minX;
00135 child->pos[1] = maxY - child->size[1];
00136 maxY -= child->size[1] + margin;
00137 }
00138
00139
00140 for (child = node->firstChild; child; child = child->next) {
00141 if (child->align != LAYOUTALIGN_LEFT)
00142 continue;
00143 if (child->invis)
00144 continue;
00145 newSize[0] = child->size[0];
00146 newSize[1] = maxY - minY;
00147 UI_NodeSetSize(child, newSize);
00148 child->pos[0] = minX;
00149 child->pos[1] = minY;
00150 minX += child->size[0] + margin;
00151 }
00152
00153
00154 for (child = node->firstChild; child; child = child->next) {
00155 if (child->align != LAYOUTALIGN_RIGHT)
00156 continue;
00157 if (child->invis)
00158 continue;
00159 newSize[0] = child->size[0];
00160 newSize[1] = maxY - minY;
00161 UI_NodeSetSize(child, newSize);
00162 child->pos[0] = maxX - child->size[0];
00163 child->pos[1] = minY;
00164 maxX -= child->size[0] + margin;
00165 }
00166
00167
00168 for (child = node->firstChild; child; child = child->next) {
00169 if (child->align != LAYOUTALIGN_MIDDLE)
00170 continue;
00171 if (child->invis)
00172 continue;
00173 newSize[0] = maxX - minX;
00174 newSize[1] = maxY - minY;
00175 UI_NodeSetSize(child, newSize);
00176 child->pos[0] = minX;
00177 child->pos[1] = minY;
00178 }
00179 }
00180
00191 static void UI_PackLayout (uiNode_t *node, int margin)
00192 {
00193 uiNode_t *child;
00194 vec2_t newSize;
00195 int minX = node->padding;
00196 int maxX = node->size[0] - node->padding;
00197 int minY = node->padding;
00198 int maxY = node->size[1] - node->padding;
00199
00200
00201 for (child = node->firstChild; child; child = child->next) {
00202 if (child->invis)
00203 continue;
00204 switch (child->align) {
00205 case LAYOUTALIGN_TOP:
00206 newSize[0] = maxX - minX;
00207 newSize[1] = child->size[1];
00208 UI_NodeSetSize(child, newSize);
00209 child->pos[0] = minX;
00210 child->pos[1] = minY;
00211 minY += child->size[1] + margin;
00212 break;
00213 case LAYOUTALIGN_BOTTOM:
00214 newSize[0] = maxX - minX;
00215 newSize[1] = child->size[1];
00216 UI_NodeSetSize(child, newSize);
00217 child->pos[0] = minX;
00218 child->pos[1] = maxY - child->size[1];
00219 maxY -= child->size[1] + margin;
00220 break;
00221 case LAYOUTALIGN_LEFT:
00222 newSize[0] = child->size[0];
00223 newSize[1] = maxY - minY;
00224 UI_NodeSetSize(child, newSize);
00225 child->pos[0] = minX;
00226 child->pos[1] = minY;
00227 minX += child->size[0] + margin;
00228 break;
00229 case LAYOUTALIGN_RIGHT:
00230 newSize[0] = child->size[0];
00231 newSize[1] = maxY - minY;
00232 UI_NodeSetSize(child, newSize);
00233 child->pos[0] = maxX - child->size[0];
00234 child->pos[1] = minY;
00235 maxX -= child->size[0] + margin;
00236 break;
00237 case LAYOUTALIGN_FILL:
00238 newSize[0] = maxX - minX;
00239 newSize[1] = maxY - minY;
00240 UI_NodeSetSize(child, newSize);
00241 child->pos[0] = minX;
00242 child->pos[1] = minY;
00243 break;
00244 default:
00245 break;
00246 }
00247 }
00248 }
00249
00258 void UI_StarLayout (uiNode_t *node)
00259 {
00260 uiNode_t *child;
00261 for (child = node->firstChild; child; child = child->next) {
00262 vec2_t source;
00263 vec2_t destination;
00264
00265 if (child->align <= LAYOUTALIGN_NONE)
00266 continue;
00267
00268 if (child->align == LAYOUTALIGN_FILL) {
00269 child->pos[0] = 0;
00270 child->pos[1] = 0;
00271 UI_NodeSetSize(child, node->size);
00272 child->behaviour->doLayout(child);
00273 } else if (child->align < LAYOUTALIGN_SPECIAL) {
00274 UI_NodeGetPoint(node, destination, child->align);
00275 UI_NodeRelativeToAbsolutePoint(node, destination);
00276 UI_NodeGetPoint(child, source, child->align);
00277 UI_NodeRelativeToAbsolutePoint(child, source);
00278 child->pos[0] += destination[0] - source[0];
00279 child->pos[1] += destination[1] - source[1];
00280 }
00281 }
00282 }
00283
00287 static void UI_ClientLayout (uiNode_t *node)
00288 {
00289 int width = 0;
00290 int height = 0;
00291 uiNode_t *child;
00292 qboolean updated;
00293 for (child = node->firstChild; child; child = child->next) {
00294 int value;
00295 value = child->pos[0] + child->size[0];
00296 if (value > width)
00297 width = value;
00298 value = child->pos[1] + child->size[1];
00299 if (value > height)
00300 height = value;
00301 }
00302
00303 width += node->padding;
00304 height += node->padding;
00305
00306 updated = UI_SetScroll(&EXTRADATA(node).super.scrollX, -1, node->size[0], width);
00307 updated = UI_SetScroll(&EXTRADATA(node).super.scrollY, -1, node->size[1], height) || updated;
00308 if (updated && EXTRADATA(node).super.onViewChange)
00309 UI_ExecuteEventActions(node, EXTRADATA(node).super.onViewChange);
00310 }
00311
00319 static void UI_ColumnLayout (uiNode_t *node)
00320 {
00321 int columnPos[EXTRADATA(node).layoutColumns];
00322 int columnSize[EXTRADATA(node).layoutColumns];
00323 int rowHeight = 0;
00324 int i;
00325 int y;
00326 uiNode_t *child;
00327
00328 if (EXTRADATA(node).layoutColumns <= 0) {
00329 Com_Printf("UI_ColumnLayout: Column number must be positive (%s). Layout ignored.", UI_GetPath(node));
00330 return;
00331 }
00332
00333
00334 child = node->firstChild;
00335 for (i = 0; i < EXTRADATA(node).layoutColumns; i++) {
00336 if (child == NULL)
00337 break;
00338 columnSize[i] = child->size[0];
00339 if (child->size[1] > rowHeight) {
00340 rowHeight = child->size[1];
00341 }
00342 child = child->next;
00343 }
00344
00345
00346 columnPos[0] = node->padding;
00347 for (i = 1; i < EXTRADATA(node).layoutColumns; i++) {
00348 columnPos[i] = columnPos[i - 1] + columnSize[i - 1] + EXTRADATA(node).layoutMargin;
00349 }
00350
00351
00352 i = 0;
00353 y = -1;
00354 for (child = node->firstChild; child; child = child->next) {
00355 const int column = i % EXTRADATA(node).layoutColumns;
00356 if (column == 0) {
00357 if (y < 0) {
00358 y = node->padding;
00359 } else {
00360 y += rowHeight + EXTRADATA(node).layoutMargin;
00361 }
00362 }
00363 child->pos[0] = columnPos[column];
00364 child->pos[1] = y;
00365
00366 child->behaviour->doLayout(child);
00367 i++;
00368 }
00369
00370
00371 {
00372 const int column = EXTRADATA(node).layoutColumns;
00373 int width = columnPos[column - 1] + columnSize[column - 1] + node->padding;
00374 int height = y + rowHeight + node->padding;
00375 qboolean updated;
00376
00377 updated = UI_SetScroll(&EXTRADATA(node).super.scrollX, -1, node->size[0], width);
00378 updated = UI_SetScroll(&EXTRADATA(node).super.scrollY, -1, node->size[1], height) || updated;
00379 if (updated && EXTRADATA(node).super.onViewChange)
00380 UI_ExecuteEventActions(node, EXTRADATA(node).super.onViewChange);
00381 }
00382 }
00383
00384 static void UI_PanelNodeDoLayout (uiNode_t *node)
00385 {
00386 if (!node->invalidated)
00387 return;
00388
00389 switch (EXTRADATA(node).layout) {
00390 case LAYOUT_NONE:
00391 break;
00392 case LAYOUT_TOP_DOWN_FLOW:
00393 UI_TopDownFlowLayout(node, EXTRADATA(node).layoutMargin);
00394 break;
00395 case LAYOUT_BORDER:
00396 UI_BorderLayout(node, EXTRADATA(node).layoutMargin);
00397 break;
00398 case LAYOUT_PACK:
00399 UI_PackLayout(node, EXTRADATA(node).layoutMargin);
00400 break;
00401 case LAYOUT_STAR:
00402 UI_StarLayout(node);
00403 break;
00404 case LAYOUT_CLIENT:
00405 UI_ClientLayout(node);
00406 break;
00407 case LAYOUT_COLUMN:
00408 UI_ColumnLayout(node);
00409 break;
00410 default:
00411 Com_Printf("UI_PanelNodeDoLayout: layout '%d' unsupported.", EXTRADATA(node).layout);
00412 }
00413
00414 localBehaviour->super->doLayout(node);
00415 }
00416
00420 static void UI_PanelNodeLoaded (uiNode_t *node)
00421 {
00422 #ifdef DEBUG
00423 if (node->size[0] < CORNER_SIZE + MID_SIZE + CORNER_SIZE || node->size[1] < CORNER_SIZE + MID_SIZE + CORNER_SIZE)
00424 Com_DPrintf(DEBUG_CLIENT, "Node '%s' too small. It can create graphical glitches\n", UI_GetPath(node));
00425 #endif
00426 if (EXTRADATA(node).layout != LAYOUT_NONE)
00427 UI_Invalidate(node);
00428 }
00429
00430 static void UI_PanelNodeGetClientPosition (uiNode_t *node, vec2_t position)
00431 {
00432 position[0] = -EXTRADATA(node).super.scrollX.viewPos;
00433 position[1] = -EXTRADATA(node).super.scrollY.viewPos;
00434 }
00435
00436 static void UI_PanelPropertyChanged (uiNode_t *node, const value_t *property)
00437 {
00438 if (propertyPadding == NULL) {
00439 propertyPadding = UI_GetPropertyFromBehaviour(node->behaviour, "padding");
00440 }
00441
00442 if (property == propertyLayoutColumns ||property == propertyLayoutMargin || property == propertyPadding) {
00443 UI_Invalidate(node);
00444 return;
00445 }
00446 localBehaviour->super->propertyChanged(node, property);
00447 }
00448
00452 static const value_t properties[] = {
00466 {"layout", V_INT, UI_EXTRADATA_OFFSETOF(panelExtraData_t, layout), MEMBER_SIZEOF(panelExtraData_t, layout)},
00470 {"layoutMargin", V_INT, UI_EXTRADATA_OFFSETOF(panelExtraData_t, layoutMargin), MEMBER_SIZEOF(panelExtraData_t, layoutMargin)},
00474 {"layoutColumns", V_INT, UI_EXTRADATA_OFFSETOF(panelExtraData_t, layoutColumns), MEMBER_SIZEOF(panelExtraData_t, layoutColumns)},
00475
00476 {NULL, V_NULL, 0, 0}
00477 };
00478
00479 void UI_RegisterPanelNode (uiBehaviour_t *behaviour)
00480 {
00481 localBehaviour = behaviour;
00482 behaviour->extends = "abstractscrollable";
00483 behaviour->name = "panel";
00484 behaviour->properties = properties;
00485 behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
00486 behaviour->draw = UI_PanelNodeDraw;
00487 behaviour->loaded = UI_PanelNodeLoaded;
00488 behaviour->doLayout = UI_PanelNodeDoLayout;
00489 behaviour->getClientPosition = UI_PanelNodeGetClientPosition;
00490 behaviour->propertyChanged = UI_PanelPropertyChanged;
00491
00492
00493 propertyLayoutColumns = UI_GetPropertyFromBehaviour(behaviour, "layoutColumns");
00494 propertyLayoutMargin = UI_GetPropertyFromBehaviour(behaviour, "layoutMargin");
00495
00496 Com_RegisterConstInt("LAYOUTALIGN_TOPLEFT", LAYOUTALIGN_TOPLEFT);
00497 Com_RegisterConstInt("LAYOUTALIGN_TOP", LAYOUTALIGN_TOP);
00498 Com_RegisterConstInt("LAYOUTALIGN_TOPRIGHT", LAYOUTALIGN_TOPRIGHT);
00499 Com_RegisterConstInt("LAYOUTALIGN_LEFT", LAYOUTALIGN_LEFT);
00500 Com_RegisterConstInt("LAYOUTALIGN_MIDDLE", LAYOUTALIGN_MIDDLE);
00501 Com_RegisterConstInt("LAYOUTALIGN_RIGHT", LAYOUTALIGN_RIGHT);
00502 Com_RegisterConstInt("LAYOUTALIGN_BOTTOMLEFT", LAYOUTALIGN_BOTTOMLEFT);
00503 Com_RegisterConstInt("LAYOUTALIGN_BOTTOM", LAYOUTALIGN_BOTTOM);
00504 Com_RegisterConstInt("LAYOUTALIGN_BOTTOMRIGHT", LAYOUTALIGN_BOTTOMRIGHT);
00505 Com_RegisterConstInt("LAYOUTALIGN_FILL", LAYOUTALIGN_FILL);
00506
00507 Com_RegisterConstInt("LAYOUT_TOP_DOWN_FLOW", LAYOUT_TOP_DOWN_FLOW);
00508 Com_RegisterConstInt("LAYOUT_BORDER", LAYOUT_BORDER);
00509 Com_RegisterConstInt("LAYOUT_PACK", LAYOUT_PACK);
00510 Com_RegisterConstInt("LAYOUT_STAR", LAYOUT_STAR);
00511 Com_RegisterConstInt("LAYOUT_CLIENT", LAYOUT_CLIENT);
00512 Com_RegisterConstInt("LAYOUT_COLUMN", LAYOUT_COLUMN);
00513 }