ui_node_panel.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 2002-2010 UFO: Alien Invasion.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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     /* top */
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     /* bottom */
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     /* left */
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     /* right */
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     /* middle */
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     /* top */
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     /* check the first row */
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     /* compute column position */
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     /* fix child position */
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         /*UI_NodeSetSize(child, node->size);*/
00366         child->behaviour->doLayout(child);
00367         i++;
00368     }
00369 
00370     /* fix scroll */
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     /* @todo find a good place for this kind of initialization */
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 }

Generated by  doxygen 1.6.2