From ba20cc940460b324d422a902a6f3316de428b2ba Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:19:13 -0600 Subject: [PATCH 001/107] Update apos docs and code comments. --- centrallix/include/apos.h | 19 +- centrallix/wgtr/apos.c | 568 +++++++++++++++++++++++++++++++------- 2 files changed, 479 insertions(+), 108 deletions(-) diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index a40d3d26d..36f5e1ee4 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -115,23 +115,32 @@ int aposSpaceOutLines(pXArray, pXArray, int); /**Adjusts spaces between lines to int aposSnapWidgetsToGrid(pXArray, int); /**Refreshes widget dimensions to match adjusted grid**/ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree to process windows**/ -/** # defines **/ +/** # defines names for magic values to make them easier to read. **/ + +/** Indicates how a line links to a widget. */ #define APOS_SWIDGETS 1 #define APOS_EWIDGETS 2 +/*** Indicates if a section or line is a row (horizontal) or a column (vertical). + *** A row spans horizontally between two vertical lines, and a column spans + *** vertically between two horizontal lines. + ***/ #define APOS_ROW 1 #define APOS_COL 2 #define APOS_FUDGEFACTOR 0.5 -/** The greatest width between two widgets that still defines them as "adjacent," -*** indicating that we don't want to increase the distance between them **/ +/*** The greatest width between two widgets that still defines them as + *** "adjacent," indicating that we don't want to increase the distance + *** between them. Therefore, a section of this size or less is considered + *** a "spacer" which will not be resized. + ***/ #define APOS_MINSPACE 20 -/**Lowest acceptable width or height for a widget**/ +/** The lowest acceptable width or height for a widget. **/ #define APOS_MINWIDTH 30 -/**Default flexibilities for widgetless gaps in expanding or contracting applications **/ +/** Default flexibilities for widgetless gaps in expanding or contracting applications. **/ #define APOS_EGAPFLEX 30 #define APOS_CGAPFLEX 50 diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 8c3780205..b4719bca3 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -26,9 +26,59 @@ /* Author: Nathaniel Colson */ /* Creation: August 9, 2005 */ /* Description: Applies layout logic to the widgets of an application. */ -/* See centrallix-sysdoc/Auto-Positioning.md for more information. */ +/* See centrallix-sysdoc/Auto-Positioning.md for more information. */ /************************************************************************/ +/*** Author: Israel Fuller + *** Date: June, 2025 + *** + *** I wasn't the one to write most of this (although I did write a ton of + *** comments), but after doing my best to understand it, I hope that you will + *** find the compiled information below helpful. + *** + *** Execution of this file usually begins when wgtrVerify() in wgtr.c calls + *** aposAutoPositionWidgetTree(). To auto position the tree, the code first + *** it draws four lines on the four edges of every visible widget (with some + *** exceptions, @see aposAddLinesToGrid()). These lines divide the page into + *** into horizontal sections (rows) and vertical sections (columns) which span + *** the page. @see aposAddSectionsToGrid() for more detail. + *** + *** The program guesses that some of these sections are "spacers", which are + *** small amounts of space intended to provide visual room between widgets. + *** When resizing, these do not flex at all. However, many elements are able + *** to flex. @see aposSetFlexibilities() for more information about flexing. + *** + *** Next, the program uses aposSetLimits() to honor minimum and maximum sizes + *** of widgets, and finally calls aposAutoPositionContainers() to position + *** the widgets on the screen. Lastly, it calls aposProcessWindows() to handle + *** floating window widgets, which are typically ignored by most of the rest + *** of the code. + *** + *** Note: Due to this approach, this means that all sections and widgets start + *** and end at a line. The way these lines are set up ensures that start + *** lines are always on the top or left, and end lines are always on the + *** bottom or right. @see aposAddLinesForChildren() + *** + *** Notes: I wrote some information about various structs below that's good to + *** know. Some of this is covered elsewhere in the documentation. + *** + *** AposGrid: A data structure to store sections and lines. + *** + *** AposLine: An AposLine spans the entire page. + *** + *** AposSection: After lines are created, sections are added in between the + *** lines (aka. in between the nodes). Every node begins and ends on the + *** edge of a section, although it may span multiple sections. + *** + *** pWgtrNode: A pointer to a widget node instance. You can think of this + *** like a DOM node, but remember that it's common for them to expand + *** into multiple DOM nodes. Also, these can have children, just like + *** a DOM node, which is why a single widget node pointer is really + *** more of a tree of them. + *** + *** XArray: This array also stores its size (nAlloc) and the number of items + *** stored (nItems), so you don't have to pass that info separately. + ***/ #include #include @@ -37,6 +87,12 @@ #include "cxlib/xarray.h" #include "cxlib/datatypes.h" +/*** Allocate space for a grid, section, and line using the custom allocation + *** system. Note that register is similar to creating a new heap-allocated + *** variable, then binding it to a name. + *** + *** @returns 0, success. + ***/ int aposInit() { @@ -47,6 +103,14 @@ aposInit() return 0; } +/*** Dumps the grid content of a widget node and its floating children. This + *** function is most likely intended for debugging. + *** + *** @param tree The widget tree from which to extract the layout grid. + *** @param indent The number of 4-space indentations to indent the output. + *** Note: Included for the sake of recursion; just pass 0. + *** @returns 0, success. + ***/ int aposDumpGrid(pWgtrNode tree, int indent) { @@ -57,6 +121,7 @@ pWgtrNode child; printf("%*.*s*** %s ***\n", indent*4, indent*4, "", tree->Name); if (tree->LayoutGrid) { + /** Dump the grid rows. **/ sectionCnt = xaCount(&AGRID(tree->LayoutGrid)->Rows); for(i=0;iStartLine->Loc, section->Width); printf("\n"); } + + /** Dump the grid columns. **/ sectionCnt = xaCount(&AGRID(tree->LayoutGrid)->Cols); for(i=0;iChildren); for(i=0;iName); return -1; } - /** Set flexibilities on containers **/ + /** Set flexibilities on containers. **/ if (aposSetFlexibilities(tree) < 0) { return -1; @@ -110,7 +186,7 @@ int i=0, count=0; /*aposDumpGrid(tree, 0);*/ - /** Honor minimum/maximum space requirements **/ + /** Detect and honor minimum/maximum space requirements. **/ if (aposSetLimits(tree) < 0) { return -1; @@ -133,7 +209,7 @@ int i=0, count=0; /**makes a final pass through the tree and processes html windows**/ aposProcessWindows(tree, tree); - /**unpatches all of the heights that were specified in aposPrepareTree**/ + /** Unpatches the heights specified by aposPrepareTree(). **/ count=xaCount(&PatchedWidgets); for(i=0; ipre_height = -1; } + /** Free the PatchedWidgets XArray.**/ xaDeInit(&PatchedWidgets); return 0; } - +/*** Recursively sets flexibility values for containers and their children. + *** + *** @param Parent The parent node who's flexibilities are being set. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposSetFlexibilities(pWgtrNode Parent) { @@ -156,14 +237,14 @@ pWgtrNode Child; int i=0, childCount=xaCount(&(Parent->Children)); int sectCount; - /** Check recursion **/ + /** Check recursion. **/ if (thExcessiveRecursion()) { mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); return -1; } - + /** Recursively set the flexibilities of all children. **/ for(i=0; iChildren), i); @@ -174,7 +255,7 @@ int sectCount; } } - /** Reset flexibility values in the grid **/ + /** Reset flexibility values in the grid. **/ if (theGrid && childCount > 0 && !(Parent->Flags & WGTR_F_NONVISUAL)) { sectCount = xaCount(&(theGrid->Rows)); @@ -191,7 +272,7 @@ int sectCount; } } - /**set the flexibility of the given container, if it is visual**/ + /** Set the flexibility of the given container, if it is visual. **/ if(!(Parent->Flags & WGTR_F_NONVISUAL)) if(aposSetContainerFlex(Parent) < 0) { @@ -202,8 +283,13 @@ int sectCount; return 0; } - -/** this function is the recursive function that actually does the work **/ +/*** Adjusts space to acomodate children, somehow? I think? + *** + *** @param Parent The widget node parent who's limits are being calculated. + *** @param delta_w The change in width required to accomodate children. + *** @param delta_h The change in height required to accomodate children. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposSetLimits_r(pWgtrNode Parent, int* delta_w, int* delta_h) { @@ -215,14 +301,14 @@ int sectionCount; pAposSection s; pWgtrNode Child; - /** Check recursion **/ + /** Check recursion. **/ if (thExcessiveRecursion()) { mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); return -1; } - /** Figure what is needed for children **/ + /** Calculate the total required space for children. **/ childCount = xaCount(&(Parent->Children)); total_child_delta_w = total_child_delta_h = 0; for(i=0;iLayoutGrid) { sectionCount = xaCount(&(AGRID(Parent->LayoutGrid)->Rows)); - for(i=0;iLayoutGrid)->Rows), i)); + /*** If it has a desired width, increase the height + *** enough to give it that width. + ***/ if (s->DesiredWidth >= 0) { *delta_h += (s->DesiredWidth - s->Width); @@ -263,6 +356,9 @@ pWgtrNode Child; for(i=0;iLayoutGrid)->Cols), i)); + /*** If it has a desired width, increase the width + *** enough to give it that width. + ***/ if (s->DesiredWidth >= 0) { *delta_w += (s->DesiredWidth - s->Width); @@ -273,7 +369,7 @@ pWgtrNode Child; } } - /** Make space for this widget bigger **/ + /** If there is extra space, expand this widget to fill that space. **/ if (*delta_w) { if (Parent->StartVLine && ALINE(Parent->StartVLine)->SSection) @@ -298,8 +394,11 @@ pWgtrNode Child; return 0; } - -/** This function simply call the recursive version **/ +/*** Adjusts space to acomodate children, somehow? I think? + *** + *** @param Parent The widget node parent who's limits are being calculated. + *** @returns 0, success. + ***/ int aposSetLimits(pWgtrNode Parent) { @@ -314,30 +413,40 @@ int rval; return 0; } - +/*** Patch children of the given Parent node with unspecified heights. Searches + *** recursively within containers. Patched children are logged in the given + *** PatchedWidgets array. + *** + *** @param Parent The parent node who's childen should be patched. + *** @param PatchedWidgets The widget children which have been patched. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposPrepareTree(pWgtrNode Parent, pXArray PatchedWidgets) { pWgtrNode Child; int i=0, childCount=xaCount(&(Parent->Children)); - /** Check recursion **/ + /** Check recursion. **/ if (thExcessiveRecursion()) { mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); return -1; } + /** Loop through each child. **/ for(i=0; iChildren), i); - /**if a visual child has an unspecified height, patch it, unless it is a scrollpane**/ + /*** If a visual child has an unspecified height, patch it, unless it is in a scrollpane + *** Remember here that strcmp() returns 0 (false) if the strings are equal. + ***/ if((Child->height < 0) && !(Child->Flags & WGTR_F_NONVISUAL) && strcmp(Parent->Type, "widget/scrollpane")) aposPatchNegativeHeight(Child, PatchedWidgets); - /** If child is a container but not a window, recursively prepare it as well **/ + /** If child is a container, but not a floating window, recursively prepare it as well. **/ if((Child->Flags & WGTR_F_CONTAINER) && !(Child->Flags & WGTR_F_FLOATING)) if (aposPrepareTree(Child, PatchedWidgets) < 0) return -1; @@ -346,12 +455,18 @@ int i=0, childCount=xaCount(&(Parent->Children)); return 0; } +/*** Try to guess the height of a widget with an unspecified height. + *** + *** @param Widget The widget child who's height is unspecified. + *** @param PatchedWidgets The array to add the widget to after patching it. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposPatchNegativeHeight(pWgtrNode Widget, pXArray PatchedWidgets) { ObjData val; - /** set unspecified height of widget to an educated guess**/ + /** Try to guess the height based on the type of widget. **/ if(!strcmp(Widget->Type, "widget/editbox")) { Widget->height = 16; @@ -390,12 +505,21 @@ ObjData val; return 0; } + /** Add the widget to the provided array. **/ xaAddItem(PatchedWidgets, Widget); + + /** Overwrite the "prepositioning" height because it's most likely also invalid. **/ Widget->pre_height = Widget->height; return 0; } +/*** Calculates and sets the flexibility for a container by taking weighted + *** averages in each direction. + *** + *** @param W The container to be set. + *** @returns 0, success. + ***/ int aposSetContainerFlex(pWgtrNode W) { @@ -405,7 +529,8 @@ int i=0, sectCount=0, TotalWidth=0, ProductSum=0; if (!theGrid) return 0; - /**calculate average row flexibility, weighted by height **/ + /** Calculate average row flexibility, weighted by height. **/ + /** Note: Height is called width because rows are 1 dimentional. **/ sectCount = xaCount(&(theGrid->Rows)); for(i=0; iCols)); for(i=0; iType, "widget/scrollpane")); - /**set isWin to compensate windows' titlebars, if any**/ + /** Set isWin to compensate windows' titlebars, if any. **/ if(isWin && !strcmp(W->Type, "widget/childwindow")) { + /*** Set isWin (is window) to compensate for a titlebar. If the + *** node does not specify if it has a titlebar, assume it does. + ***/ if(wgtrGetPropertyValue(W, "titlebar", DATA_T_STRING, &val) < 0) - *isWin = 1; //if property not found, assume it has a titlebar + *isWin = 1; // Property not found, assume it has a titlebar. else *isWin = !strcmp(val.String, "yes"); } - /**isTopTab and isSideTab are used to compensate for tabs**/ + /** isTopTab and isSideTab are used to compensate for tabs. **/ if(isTopTab && !strcmp(W->Type, "widget/tab")) { - /**set isTopTab and isSideTab**/ + /*** Set isTopTab and isSideTab. If the node does not specify the + *** tab location, assume it has a top tab and leave side-tab unset. + **/ if(wgtrGetPropertyValue(W, "tab_location", DATA_T_STRING, &val) < 0) - *isTopTab = 1; //if property not found, assume top tab**/ + *isTopTab = 1; // Property not found, assume it has a top tab only. else { *isTopTab = (!strcmp(val.String, "top") || !strcmp(val.String, "bottom")); - *isSideTab = (!strcmp(val.String, "left") || (!strcmp(val.String, "right"))); + *isSideTab = (!strcmp(val.String, "left") || (!strcmp(val.String, "right"))); // Warning: Unchecked assignment. } - /**set tabWidth**/ + /** Set the tab width. If none is specified, default to 80. **/ if(wgtrGetPropertyValue(W, "tab_width", DATA_T_INTEGER, &val) < 0) *tabWidth = 80; else *tabWidth = val.Integer; @@ -467,7 +616,12 @@ ObjData val; return 0; } - +/*** Builds the layout grid for recursively for this container and all of its + *** children, including the lines and sections required for positioning. + *** + *** @param Parent The parent node who's grid is being built. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposBuildGrid(pWgtrNode Parent) { @@ -475,25 +629,24 @@ int childCount, i; pWgtrNode Child; pAposGrid theGrid = NULL; - /** Check recursion **/ + /** Check recursion. **/ if (thExcessiveRecursion()) { mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); return -1; } - /** Allocate a grid **/ + /** Allocate a grid. **/ if (Parent->Flags & WGTR_F_CONTAINER) { if (!(Parent->Flags & WGTR_F_NONVISUAL) || !Parent->Parent) { + /** Allocate and initialize a new pAposGrid. **/ theGrid = Parent->LayoutGrid = (pAposGrid)nmMalloc(sizeof(AposGrid)); if (!Parent->LayoutGrid) goto error; - - /**initiallize the XArrays in the grid**/ aposInitiallizeGrid(theGrid); - /**Add the lines to the grid**/ + /** Add lines for children to the grid. **/ if(aposAddLinesToGrid(Parent, &(theGrid->HLines), &(theGrid->VLines)) < 0) { mssError(0, "APOS", "aposBuildGrid: Couldn't add lines to %s's grid", @@ -501,7 +654,7 @@ pAposGrid theGrid = NULL; return -1; } - /**Add the sections to the grid**/ + /** Add the sections to the grid. **/ if(aposAddSectionsToGrid(theGrid, (Parent->height-Parent->pre_height), (Parent->width-Parent->pre_width)) < 0) @@ -512,7 +665,7 @@ pAposGrid theGrid = NULL; } } - /** Do it for all children of this widget **/ + /** Recursively build this grid for all children of this widget. **/ childCount = xaCount(&(Parent->Children)); for(i=0; iChildren)); for(i=0;ipre_height < Parent->min_height && Parent->min_height != 0) height_adj = Parent->min_height - Parent->pre_height; - /**Add the 2 horizontal border lines, unless parent is a scrollpane**/ + /** Add the 2 horizontal border lines, unless parent is a scrollpane. **/ if(strcmp(Parent->Type, "widget/scrollpane")) { if(aposCreateLine(NULL, HLines, 0, 0, 1, 0, 0) < 0) @@ -650,18 +838,19 @@ pXArray FirstCross, LastCross; if(aposCreateLine(NULL, HLines, (Parent->pre_height-isWin*24), 0, 1, height_adj, 0) < 0) goto CreateLineError; } - /**Add the 2 vertical border lines**/ - if(aposCreateLine(NULL, VLines, 0, 0, 1, 0, 1) < 0) + + /** Add the 2 vertical border lines. **/ goto CreateLineError; if(aposCreateLine(NULL, VLines, (Parent->pre_width-isSP*18), 0, 1, width_adj, 1) < 0) goto CreateLineError; + /** Recursively add the nonborder lines for all child nodes. **/ if(aposAddLinesForChildren(Parent, HLines, VLines) < 0) goto CreateLineError; - /**populate horizontal line cross XArrays**/ + /** Record the widgets that cross each horizontal line in its CWidgets XArray. **/ count = xaCount(HLines); - for(i=1; iCWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); } - /**populate vertical line cross XArrays**/ + /** Record the widgets that cross each vertical line in its CWidgets XArray. **/ count = xaCount(VLines); - for(i=1; iCWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); } - /**sanity check to make sure no widgets cross the border lines**/ - if(xaCount(HLines)) //don't test borderlines unless they exist + /** Sanity check to make sure no widgets cross the border lines. **/ + if(xaCount(HLines)) // Only check borderlines if they exist. { FirstCross = &(((pAposLine)xaGetItem(HLines, 0))->CWidgets); LastCross = &(((pAposLine)xaGetItem(HLines, (xaCount(HLines)-1)))->CWidgets); @@ -708,6 +897,16 @@ pXArray FirstCross, LastCross; return -1; } +/*** Adds 4 lines for the edges of each visual child. Searches nonvisual + *** containers recursively for qualifying grandchildren. Floating windows + *** are ignored. Scrollpanes recieve only 2 vertical lines (skipping their + *** horizontal edges). + *** + *** @param Parent The parent who's children are being given lines. + *** @param HLines The array to which horizontal lines should be added. + *** @param VLines The array to which vertical lines should be added. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposAddLinesForChildren(pWgtrNode Parent, pXArray HLines, pXArray VLines) { @@ -716,14 +915,14 @@ int isTopTab=0, isSideTab=0, tabWidth=0; int height_adj, width_adj; pWgtrNode C; - /** Check recursion **/ + /** Check recursion. **/ if (thExcessiveRecursion()) { mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); return -1; } - /**loop through the children and create 4 lines for each child's 4 edges**/ + /** Loop through the children and create 4 lines for each child's 4 edges. **/ for(i=0; iChildren), i); @@ -736,27 +935,36 @@ pWgtrNode C; if (C->pre_height < C->min_height && C->min_height != 0) height_adj = C->min_height - C->pre_height; - /** If C is a nonvisual container, add lines for - *** the grandchildren. Otherwise, if C is visual - *** and not a window, just add 4 lines for it **/ + /** If the child (C) is a nonvisual container, recursively add lines for any grandchildren. **/ if((C->Flags & WGTR_F_NONVISUAL) && (C->Flags & WGTR_F_CONTAINER)) { if (aposAddLinesForChildren(C, HLines, VLines) < 0) goto CreateLineError; } + /** Otherwise, if child (C) is visual and not a floating window, add 4 lines for it. **/ else if(!(C->Flags & WGTR_F_NONVISUAL) && !(C->Flags & WGTR_F_FLOATING)) { - /**add horizontal lines, unless parent is a scrollpane**/ + /** Add horizontal lines, unless parent is a scrollpane. **/ if(strcmp(Parent->Type, "widget/scrollpane")) { - if(aposCreateLine(C, HLines, (C->y), APOS_SWIDGETS, 0, 0, 0) < 0) + /*** From this code, we see that the start line is + *** always the minY, and the end of the line is + *** always the maxY. Thus, the top line is the + *** start line and the bottom line is the end line + *** because Y increases as we decend the page. + ***/ goto CreateLineError; if(aposCreateLine(C, HLines, (C->y + C->height + isTopTab*24), APOS_EWIDGETS, 0, height_adj, 0) < 0) goto CreateLineError; } - /**add vertical lines**/ - if(aposCreateLine(C, VLines, (C->x), APOS_SWIDGETS, 0, 0, 1) < 0) + /** Add vertical lines. **/ + /*** From this code, we see that the start line is always + *** the minX, and the end of the line is always the maxX. + *** Thus, the left line is the start line and the right + *** line is the end line because X increases as we move + *** right along the page. + ***/ goto CreateLineError; if(aposCreateLine(C, VLines, (C->x + C->width + isSideTab*tabWidth), APOS_EWIDGETS, 0, width_adj, 1) < 0) goto CreateLineError; @@ -770,12 +978,36 @@ pWgtrNode C; return -1; } +/*** Creates a new line in the grid or updates an existing line if it exists + *** in the same location. Remember that lines record the widgets that start + *** on them (SWidgets), end on them (EWidgets), and cross them (CWidgets). + *** + *** Note: This function all lines in the given array are oriented in the same + *** direction as the new line. At the time of this writing (June 2025), + *** all known calling functions upheld by maintaining an HLines and a + *** VLines array to store horizontal and vertical lines separately. + *** + *** @param Widget The widget which determined the location of this line, + *** which we add to the SWidgets or EWidgets array. + *** @param Lines The array that stores the lines. + *** @param Loc The location of the line. Only a single coordinate in + *** one dimension is needed since lines span the entire grid. + *** @param type The type, indicating whether the associated widget starts + *** or ends on this line. + *** @param isBorder A boolean that is true if this is a grid border line. + *** @param adj An adjustment added to or subtracted from the line to + *** satisfy min or max constraints (respectively). + *** @param is_vert A boolean that is true if this line is vertical. + *** See APOS_VERTICAL and APOS_HORIZONTAL. + *** + *** @returns 0, success. + ***/ int aposCreateLine(pWgtrNode Widget, pXArray Lines, int Loc, int type, int isBorder, int adj, int is_vert) { pAposLine Line = aposExistingLine(Lines, Loc); - /**if there is already a line, just upgrade it**/ + /** If there is already a line, we upgrade it instead of creating a new one. **/ if(Line != NULL) { @@ -789,14 +1021,14 @@ pAposLine Line = aposExistingLine(Lines, Loc); else { - /**otherwise, create and add the new line**/ + /** There's not already a line, so we allocate a new one. **/ if((Line = (pAposLine)nmMalloc(sizeof(AposLine))) < 0) { mssError(1, "APOS", "aposCreateLine: Couldn't allocate memory for new grid line"); return -1; } - /**initiallize new line**/ + /** Initiallize the new line. **/ memset(Line, 0, sizeof(AposLine)); xaInit(&(Line->SWidgets),16); xaInit(&(Line->EWidgets),16); @@ -807,11 +1039,11 @@ pAposLine Line = aposExistingLine(Lines, Loc); Line->SSection = NULL; Line->ESection = NULL; - /**add new line, sorted by location**/ + /** Add the new line, to the list of lines, sorted by location. **/ xaAddItemSortedInt32(Lines, Line, 0); } - /** Link the line and the widget together **/ + /** Link the line and the widget together. **/ if (type == APOS_SWIDGETS) { xaAddItem(&(Line->SWidgets), Widget); @@ -834,6 +1066,16 @@ pAposLine Line = aposExistingLine(Lines, Loc); return 0; } +/*** Gets a line from the array at the location, or returns NULL if none exists. + *** + *** Note: This function all lines in the given array are oriented in the same + *** direction. This is not tested, although at the time of this writing + *** (June, 2025), all calling functions upheld this contract. + *** + *** @param Lines The array of lines to search. + *** @param Loc The location to check for a line. + *** @returns A pointer to the line, if it exists, and NULL otherwise. + ***/ pAposLine aposExistingLine(pXArray Lines, int Loc) { @@ -848,36 +1090,64 @@ int i, count = xaCount(Lines); return NULL; } +/*** Detects if a widget in PrevList (usually the widgets that started in or + *** crossed the pevious line) ends on this line (aka. appears in EWidgets). + *** If it does not end on this line, we know it crosses this line, so we add + *** the widget to CWidgets. + *** + *** @param PrevList The list of previous widgets being checked. + *** @param EWidgets The list of widgets ending on the line in question. + *** @param CWidgets The list to which widgets that cross should be added. + *** @returns 0, success. + ***/ int aposFillInCWidget(pXArray PrevList, pXArray EWidgets, pXArray CWidgets) { pWgtrNode AddCandidate; int found=0, i=0, j=0, pCount=xaCount(PrevList), eCount=xaCount(EWidgets); - /** loop through the SWidgets or CWidgets array of the previous line**/ + /*** Loop through the array from the previous line. + *** Note: Could be that line's SWidgets OR CWidgets. + **/ for(i=0; iHLines)); for(i=1; iRows), ((pAposLine)xaGetItem(&(theGrid->HLines),(i-1))), @@ -887,7 +1157,7 @@ int count=0, i=0; return -1; } - /**Add columns**/ + /** Add column sections between vertical lines. **/ count = xaCount(&(theGrid->VLines)); for(i=1; iCols), ((pAposLine)xaGetItem(&(theGrid->VLines),(i-1))), @@ -900,15 +1170,29 @@ int count=0, i=0; return 0; } +/*** Calculate and set the flexibility value for a section. Spacers have 0 flex + *** and containers use the flex of their least flexible children. + *** + *** @param sect The section being set. + *** @param type The type of section (either APOS_ROW or APOS_COL). + *** @returns 0 if successful or -1 if a default value should be used instead + ***/ int aposSetSectionFlex(pAposSection sect, int type) { +/*** Note: + *** sCount + cCount includes all widgets intersecting this section because a + *** widget cannot begin inside a section. It always starts or eds at the edge + *** of a section. + ***/ int sCount = xaCount(&(sect->StartLine->SWidgets)); int cCount = xaCount(&(sect->StartLine->CWidgets)); - /** Set flex to 0 if the section is a spacer or contains non-flexible children, - *** otherwise set it to the average of the children. If none of those apply - *** it must be a wide, widgetless gap, assign a default flexibility **/ + /** Set flex to 0 if the section is a spacer or contains non-flexible + *** children, otherwise set it to the minimum of the children. If none + *** of those apply it must be a wide, widgetless gap. In this case, + *** return -1 to prompt the caller to determine a default flexibility. + ***/ if((sect->isSpacer) || (aposNonFlexChildren(sect->StartLine, type))) sect->Flex = 0; else if(sCount || cCount) @@ -919,12 +1203,21 @@ int cCount = xaCount(&(sect->StartLine->CWidgets)); return 0; } +/*** Creates a new row or column section between two lines in the grid. + *** + *** @param Sections The array of sections, to which this section will be added. + *** @param StartL The line which starts this section (typically the top/left line). + *** @param EndL The line which ends this section (typically the bottom/right line). + *** @param Diff I had a hard time figuring out what this means. + *** @param type Whether the section is a row (APOS_ROW) or a column (APAS_COL). + *** @returns 0 if successful, -1 otherwise. + ***/ int aposCreateSection(pXArray Sections, pAposLine StartL, pAposLine EndL, int Diff, int type) { pAposSection NewSect; - /**Allocate and initiallize a new section**/ + /** Allocate and initiallize a new section. **/ if((NewSect = (pAposSection)(nmMalloc(sizeof(AposSection)))) < 0) { mssError(1, "APOS", "nmMalloc(): Couldn't allocate memory for new row or column"); @@ -940,11 +1233,11 @@ pAposSection NewSect; StartL->SSection = NewSect; EndL->ESection = NewSect; - /** Need to adjust section width/height? **/ + /** Apply the adjustment from the end line, if needed. **/ if (EndL->Adj) NewSect->DesiredWidth = NewSect->Width + EndL->Adj; - /** Set section flexibility **/ + /** Set section flexibility. **/ if (aposSetSectionFlex(NewSect, type) < 0) { if (Diff < 0) @@ -958,22 +1251,36 @@ pAposSection NewSect; return 0; } +/*** Determines if a section between two lines is a spacer. + *** + *** If a section is a spacer, the assumption is that the designer probably put + *** that space there to provide visual breathing room in their design. Thus, we + *** should avoid resizing it as this may interfere with their design. + *** + *** @param StartL The line starting the section. (I think this is always the left/top.) + *** @param EndL The line starting the section. (I think this is always the right/bottom.) + *** @param type Whether the section is a row (APOS_ROW) or a column (APAS_COL). + *** @param isBorder Whether the section is on the border of the page. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposIsSpacer(pAposLine StartL, pAposLine EndL, int type, int isBorder) { pWgtrNode SW, EW; int i=0, j=0; +/** @brief The number of widgets starting at the end of this section.**/ int sCount=xaCount(&(EndL->SWidgets)); +/** @brief The number of widgets ending at the start of this section.**/ int eCount=xaCount(&(StartL->EWidgets)); - if((EndL->Loc - StartL->Loc) <= APOS_MINSPACE) //if section is sufficiently narrow + if((EndL->Loc - StartL->Loc) <= APOS_MINSPACE) // If section is sufficiently narrow. { - /**gap between border and widget**/ + /** Gaps between the border and any widget(s) are spacers. **/ if(isBorder && (sCount || eCount)) return 1; - /** Checks every widget ending on one side of the section against - *** every widget beginning on the other side to see if any of them - *** are directly across from each other **/ + /** Checks every widget ending on one side to see if a widget + *** starts directly across from it on the other side. + ***/ for(i=0; iSWidgets), i)); @@ -981,9 +1288,10 @@ int eCount=xaCount(&(StartL->EWidgets)); { EW = (pWgtrNode)(xaGetItem(&(StartL->EWidgets), j)); - /** if a corner of a widget on one side of the - *** section falls between the two corners of a widget - *** on the other side, return 1 **/ + /** If a corner of the widget on one side falls + *** between the two corners of a widget on the + *** other side, this is a spacer. + ***/ if((type == APOS_ROW) && (((EW->x >= SW->x) && (EW->x < (SW->x + SW->width))) || (((EW->x + EW->width) > SW->x) && ((EW->x + EW->width) <= (SW->x + SW->width))))) return 1; @@ -998,6 +1306,14 @@ int eCount=xaCount(&(StartL->EWidgets)); return 0; } +/*** Checks for any widgets starting on or crossing a line that are non-flexible + *** in the relevant dimention. + *** + *** @param L The line along which to check. + *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @returns 1 if any child widget is non-flexible in the relevant dimension, + *** 0 otherwise. + ***/ int aposNonFlexChildren(pAposLine L, int type) { @@ -1005,8 +1321,9 @@ int i=0; int sCount = xaCount(&(L->SWidgets)); int cCount = xaCount(&(L->CWidgets)); - /** returns 1 if the widgets starting on or crossing the given - *** line have children that are completely non-flexible **/ + /*** Return 1 if the widgets starting on or crossing the given line have + *** children that are completely non-flexible. + ***/ if(type == APOS_ROW) { for(i=0; iCWidgets)); if(((pWgtrNode)(xaGetItem(&(L->CWidgets), i)))->fl_height == 0) return 1; } - else //type == APOS_COL + else // type == APOS_COL { for(i=0; iSWidgets), i)))->fl_width == 0) @@ -1029,6 +1346,12 @@ int cCount = xaCount(&(L->CWidgets)); return 0; } +/*** Calculates the average flexibility of widgets on a line. + *** + *** @param L The line along which to check. + *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @returns The average flexibility of children on the line. + ***/ int aposAverageChildFlex(pAposLine L, int type) { @@ -1036,7 +1359,7 @@ int TotalFlex=0, i=0; int sCount = xaCount(&(L->SWidgets)); int cCount = xaCount(&(L->CWidgets)); - /** Sum the flexibilities of widgets within the section proceeding the line**/ + /** Sum the flexibilities. **/ if(type == APOS_ROW) { for(i=0; iCWidgets)); TotalFlex += ((pWgtrNode)xaGetItem(&(L->CWidgets), i))->fl_width; } - /**return average flexibility**/ + /** Return the average flexibility with an aditional fudge factor. **/ return (int)(APOS_FUDGEFACTOR + ((float)TotalFlex)/((float)sCount+(float)cCount)); } +/*** Calculates the minimum flexibility of widgets on a line. + *** + *** @param L The line along which to check. + *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @returns The minimum flexibility of children on the line. + ***/ int aposMinimumChildFlex(pAposLine L, int type) { @@ -1063,7 +1392,7 @@ int MinFlex=100, i=0, f; int sCount = xaCount(&(L->SWidgets)); int cCount = xaCount(&(L->CWidgets)); - /** Find the min flex within the section proceeding the line**/ + /** Find the min flexibility. **/ if(type == APOS_ROW) { for(i=0; iCWidgets)); } } - /**return min flexibility**/ + /** Return the minimum flexibility. **/ return MinFlex; } +/*** Distributes extra or missing space among grid lines based on section flexibility. + *** + *** @param Lines The array of lines in the relevant direction on this grid. + *** @param Secctions The array of sections in the relevant direction on this grid. + *** @param Diff The space differencce from how the elements are currently spaced. + *** @returns The remaining space difference after spacing out elements as much as possible. + ***/ int aposSpaceOutLines(pXArray Lines, pXArray Sections, int Diff) { @@ -1105,7 +1441,7 @@ int FlexibleSections=0; float FlexWeight=0, SizeWeight=0; float TotalSum=0; - /**if there are no sections, don't bother going on**/ + /** If there are no sections, we have nothing to space out. **/ if(!count) return Diff; /**Sum the flexibilities of the sections**/ @@ -1118,9 +1454,13 @@ float TotalSum=0; FlexibleSections++; TotalFlexibleSpace += CurrSect->Width; } + else CurrSect->AdjWeight = 0.0f; } - /** if there is no flexibility for expansion or contraction return 0**/ + /*** If there is no flexibility (no expansion or contraction), we can't + *** space anything out. Return the original difference so this can be + *** spaced out elsewhere. + ***/ if(TotalFlex == 0) return Diff; /** sets each line's location equal to the previous line's location @@ -1201,6 +1541,14 @@ float TotalSum=0; return Extra; } +/*** Adjusts widget positions and sizes to snap them to grid lines. This + *** function should be called after updating grid lines to ensure that + *** widgets properly reflect the changes. + *** + *** @param Lines The lines being updated. + *** @param flag Either APOS_ROW or APOS_COL. + *** @returns 0, success. + ***/ int aposSnapWidgetsToGrid(pXArray Lines, int flag) { @@ -1231,6 +1579,7 @@ pWgtrNode Widget; aposSetOffsetBools(Widget, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); if(flag==APOS_ROW && Widget->fl_height) { + /** Calculate the new size, taking APOS_MINWIDTH into account.**/ newsize = CurrLine->Loc - Widget->y - isTopTab*24; if (newsize < APOS_MINWIDTH && Widget->pre_height >= APOS_MINWIDTH) Widget->height = APOS_MINWIDTH; @@ -1242,6 +1591,7 @@ pWgtrNode Widget; } else if(flag==APOS_COL && Widget->fl_width) { + /** Calculate the new size, taking APOS_MINWIDTH into account.**/ newsize = CurrLine->Loc - Widget->x - isSideTab*tabWidth; if (newsize < APOS_MINWIDTH && Widget->pre_width >= APOS_MINWIDTH) Widget->width = APOS_MINWIDTH; @@ -1257,6 +1607,14 @@ pWgtrNode Widget; return 0; } +/*** + *** Processes floating windows and recursively positions visual and + *** nonvisual containers. + *** + *** @param VisualRef The last visual container up the inheritance tree. + *** @param Parent The widget being scanned for windows. + *** @returns 0 if successful, -1 otherwise. + ***/ int aposProcessWindows(pWgtrNode VisualRef, pWgtrNode Parent) { @@ -1359,6 +1717,10 @@ int ival; return 0; } +/*** Frees all memory used by a grid, including its lines and sections. + *** + *** @param theGrid The grid being freed. + ***/ int aposFree(pAposGrid theGrid) { From 49f4641284949bc12bb7b97fcc5a9848aee3ff3f Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:25:50 -0600 Subject: [PATCH 002/107] Improve code readability without affecting functionality. --- centrallix/include/apos.h | 9 +++++++++ centrallix/wgtr/apos.c | 21 +++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index 36f5e1ee4..e99a90868 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -118,9 +118,14 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t /** # defines names for magic values to make them easier to read. **/ /** Indicates how a line links to a widget. */ +#define APOS_NOT_LINKED 0 #define APOS_SWIDGETS 1 #define APOS_EWIDGETS 2 +/** Indicates if a line is vertical. */ +#define APOS_VERTICAL 1 +#define APOS_HORIZONTAL 0 + /*** Indicates if a section or line is a row (horizontal) or a column (vertical). *** A row spans horizontally between two vertical lines, and a column spans *** vertically between two horizontal lines. @@ -128,6 +133,10 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t #define APOS_ROW 1 #define APOS_COL 2 +/** Indicates if a line is a border. */ +#define APOS_IS_BORDER 1 +#define APOS_NOT_BORDER 0 + #define APOS_FUDGEFACTOR 0.5 /*** The greatest width between two widgets that still defines them as diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index b4719bca3..49cfeb0db 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -833,15 +833,18 @@ pXArray FirstCross, LastCross; /** Add the 2 horizontal border lines, unless parent is a scrollpane. **/ if(strcmp(Parent->Type, "widget/scrollpane")) { - if(aposCreateLine(NULL, HLines, 0, 0, 1, 0, 0) < 0) + int minHeightLoc = 0, maxHeightLoc = Parent->pre_height - isWin * 24; + if(aposCreateLine(NULL, HLines, minHeightLoc, APOS_NOT_LINKED, APOS_IS_BORDER, 0, APOS_HORIZONTAL) < 0) goto CreateLineError; - if(aposCreateLine(NULL, HLines, (Parent->pre_height-isWin*24), 0, 1, height_adj, 0) < 0) + if(aposCreateLine(NULL, HLines, maxHeightLoc, APOS_NOT_LINKED, APOS_IS_BORDER, height_adj, APOS_HORIZONTAL) < 0) goto CreateLineError; } /** Add the 2 vertical border lines. **/ + int minWidthLoc = 0, maxWidthLoc = (Parent->pre_width-isSP*18); + if(aposCreateLine(NULL, VLines, minWidthLoc, APOS_NOT_LINKED, APOS_IS_BORDER, 0, APOS_VERTICAL) < 0) goto CreateLineError; - if(aposCreateLine(NULL, VLines, (Parent->pre_width-isSP*18), 0, 1, width_adj, 1) < 0) + if(aposCreateLine(NULL, VLines, maxWidthLoc, APOS_NOT_LINKED, APOS_IS_BORDER, width_adj, APOS_VERTICAL) < 0) goto CreateLineError; /** Recursively add the nonborder lines for all child nodes. **/ @@ -953,8 +956,10 @@ pWgtrNode C; *** start line and the bottom line is the end line *** because Y increases as we decend the page. ***/ + int minY = (C->y), maxY = (C->y + C->height + isTopTab*24); + if(aposCreateLine(C, HLines, minY, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_HORIZONTAL) < 0) goto CreateLineError; - if(aposCreateLine(C, HLines, (C->y + C->height + isTopTab*24), APOS_EWIDGETS, 0, height_adj, 0) < 0) + if(aposCreateLine(C, HLines, maxY, APOS_EWIDGETS, APOS_NOT_BORDER, height_adj, APOS_HORIZONTAL) < 0) goto CreateLineError; } @@ -965,8 +970,10 @@ pWgtrNode C; *** line is the end line because X increases as we move *** right along the page. ***/ + int minX = (C->x), maxX = (C->x + C->width + isSideTab*tabWidth); + if(aposCreateLine(C, VLines, minX, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_VERTICAL) < 0) goto CreateLineError; - if(aposCreateLine(C, VLines, (C->x + C->width + isSideTab*tabWidth), APOS_EWIDGETS, 0, width_adj, 1) < 0) + if(aposCreateLine(C, VLines, maxX, APOS_EWIDGETS, APOS_NOT_BORDER, width_adj, APOS_VERTICAL) < 0) goto CreateLineError; } } @@ -1470,9 +1477,7 @@ float TotalSum=0; { PrevSect = (pAposSection)xaGetItem(Sections, (i-1)); FlexWeight = (float)(PrevSect->Flex) / (float)(TotalFlex); - SizeWeight = 0; - if(FlexWeight > 0) - SizeWeight = (float)(PrevSect->Width) / (float)(TotalFlexibleSpace); + SizeWeight = (FlexWeight > 0) ? (float)(PrevSect->Width) / (float)(TotalFlexibleSpace) : 0; TotalSum += (FlexWeight * SizeWeight); } From 817f6e1a0edf4b7412c6a7a00a01d14101dee90b Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:32:59 -0600 Subject: [PATCH 003/107] Store adj weights for later CSS use. --- centrallix/include/apos.h | 21 +++++++++++++++++ centrallix/include/wgtr.h | 2 ++ centrallix/wgtr/apos.c | 48 ++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index e99a90868..70bb49f12 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -51,6 +51,14 @@ struct _APOS_L XArray CWidgets; //widgets that cross the line pAposSection SSection; // section starting with this line pAposSection ESection; // section ending with this line + + /*** Used to find the distance the line should move when the parent + *** container is resized. LocAdjWeight is the weight that this line + *** moves relative to the container. MyAdjWeight is the amount that + *** this line moves relative to the line before (left of) it. + *** Used for generating responsive CSS. + ***/ + float LocAdjWeight, MyAdjWeight; }; /**Section Structure (used for both rows and columns)**/ @@ -63,6 +71,19 @@ struct _APOS_S int DesiredWidth; //When we need to resize to honor max/mins int isSpacer; //set for narrow spaces between widgets int isBorder; //set for grid border sections + + /*** Computed value which stores the weight for how much this section + *** should be adjusted when resizing. All sections along each dimention + *** inside a single page or other container should have a total adjWeight + *** of 1.0. + *** + *** For example, if we have two sections with adj weights of 0.4 and 0.6. + *** If the container/page is stretched by 10 px, then 4 px is distributed + *** to the first section and 6 px is distributed to the second section. + *** + *** Warning: Currently unused. + ***/ + float AdjWeight; }; /**Grid Structure**/ diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index 99f8c3760..eaf865ff4 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -102,6 +102,8 @@ typedef struct _WN int pre_x, pre_y, pre_width, pre_height; /** pre-layout geom. **/ int fl_x, fl_y, fl_width, fl_height;/** Flexibility **/ double fx, fy, fw, fh; /** internal flexibility calculations **/ + float xAdjWeight, yAdjWeight; /** Responsive CSS adjustment weights for x and y */ + float wAdjWeight, hAdjWeight; /** Responsive CSS adjustment weights for width and height */ int min_width, min_height; /** absolute minimums **/ int x, y, width, height; /** actual geometry **/ int top, bottom, left, right; /** container offsets **/ diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 49cfeb0db..d802fca9c 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -1482,6 +1482,10 @@ float TotalSum=0; TotalSum += (FlexWeight * SizeWeight); } + /** The initial borders do not adjust.**/ + pAposLine leftBorder = (pAposLine)xaGetItem(Lines, 0); + leftBorder->LocAdjWeight = leftBorder->MyAdjWeight = 0.0f; + for(i=1; i 0) SizeWeight = (float)(PrevSect->Width) / (float)(TotalFlexibleSpace); + + /*** Calculate the adjustment weight, and also save it so we can + *** replicate some of the following logic in the CSS we will + *** eventually send to the client. + ***/ + float AdjWeight = PrevSect->AdjWeight = (float)(FlexWeight*SizeWeight)/TotalSum; /**for expanding lines**/ if(Diff > 0) { - /*Adj = APOS_FUDGEFACTOR + (float)(Diff) * ((float)(FlexWeight+SizeWeight)/2.0);*/ - Adj = APOS_FUDGEFACTOR + (float)(Diff) * ((float)(FlexWeight*SizeWeight)/TotalSum); + /** Calculate adjustment using the adjustment weight. **/ + Adj = (float)(Diff) * AdjWeight + APOS_FUDGEFACTOR; + + /** Store the line adjustment weight for responsive CSS later.**/ + CurrLine->LocAdjWeight = PrevLine->LocAdjWeight + AdjWeight; + CurrLine->MyAdjWeight = AdjWeight; + + /** Apply the calculated adjustment.**/ CurrLine->Loc = PrevLine->Loc + PrevSect->Width + Adj; PrevSect->Width += Adj; } /**for contracting lines**/ else if(Diff < 0) { - /*Adj = (float)(Diff) * ((float)(FlexWeight+SizeWeight)/2.0) - APOS_FUDGEFACTOR;*/ - Adj = (float)(Diff) * ((float)(FlexWeight*SizeWeight)/TotalSum) - APOS_FUDGEFACTOR; + /** Calculate adjustment using the adjustment weight. **/ + Adj = (float)(Diff) * AdjWeight - APOS_FUDGEFACTOR; /** if the section width will be unacceptably *** narrow or negative after the adjustment **/ @@ -1572,8 +1588,14 @@ pWgtrNode Widget; for(j=0; jSWidgets), j); - if(flag == APOS_ROW) Widget->y = CurrLine->Loc; - else Widget->x = CurrLine->Loc; + if(flag == APOS_ROW) { + Widget->y = CurrLine->Loc; + Widget->yAdjWeight = CurrLine->LocAdjWeight; + } + else { + Widget->x = CurrLine->Loc; + Widget->xAdjWeight = CurrLine->LocAdjWeight; + } } /** Adjusts width or height of widgets ending on this line **/ @@ -1593,6 +1615,13 @@ pWgtrNode Widget; else /*Widget->height = APOS_MINWIDTH;*/ Widget->height = Widget->pre_height; + + /*** The widget copies the adjustment weight of the + *** line, ignoring APOS_MINWIDTH. This might lead + *** to problems down the road, but I plan to fix + *** them if and when I encounter them. + ***/ + Widget->hAdjWeight = CurrLine->MyAdjWeight; } else if(flag==APOS_COL && Widget->fl_width) { @@ -1605,6 +1634,13 @@ pWgtrNode Widget; else /*Widget->width = APOS_MINWIDTH;*/ Widget->width = Widget->pre_width; + + /*** The widget copies the adjustment weight of the + *** line, ignoring APOS_MINWIDTH. This might lead + *** to problems down the road, but I plan to fix + *** them if and when I encounter them. + ***/ + Widget->hAdjWeight = CurrLine->MyAdjWeight; } } } From 7670b48916fb3fc537bb40511d6266c74e118d0e Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:34:04 -0600 Subject: [PATCH 004/107] Add a helpful error value when attempting to write widget properties with an unknown type. --- centrallix/htmlgen/ht_render.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 285a1c0da..29b0152e7 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1419,6 +1419,10 @@ htr_internal_WriteWgtrProperty(pHtSession s, pWgtrNode tree, char* propname) xsDeInit(&proptxt); xsDeInit(&exptxt); break; + + default: + htrAddScriptWgtr_va(s, "%STR&SYM:'Unknown Datatype (%INT) - Add it in ht_render.c:htr_internal_WriteWgtrProperty()', ", propname, t); + break; } } } From 1a3dc3cd88dfe9321b55e620b75951876b2a22c4 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:35:32 -0600 Subject: [PATCH 005/107] Implement rendering doubles as widget properties. --- centrallix/htmlgen/ht_render.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 29b0152e7..a925d67fe 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1405,6 +1405,10 @@ htr_internal_WriteWgtrProperty(pHtSession s, pWgtrNode tree, char* propname) htrAddScriptWgtr_va(s, "%STR&SYM:%INT, ", propname, od.Integer); break; + case DATA_T_DOUBLE: + htrAddScriptWgtr_va(s, "%STR&SYM:%DBL, ", propname, od.Double); + break; + case DATA_T_STRING: htrAddScriptWgtr_va(s, "%STR&SYM:'%STR&JSSTR', ", propname, od.String); break; From c5855faddde96ffaca223c808681872056982b64 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 26 Jun 2025 17:36:49 -0600 Subject: [PATCH 006/107] Render the new adjustment weight doubles as widget properties for debugging. --- centrallix/htmlgen/ht_render.c | 4 ++++ centrallix/wgtr/wgtr.c | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index a925d67fe..f2555e697 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1496,6 +1496,10 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) htr_internal_WriteWgtrProperty(s, tree, "r_y"); htr_internal_WriteWgtrProperty(s, tree, "r_width"); htr_internal_WriteWgtrProperty(s, tree, "r_height"); + htr_internal_WriteWgtrProperty(s, tree, "adj_weight_x"); + htr_internal_WriteWgtrProperty(s, tree, "adj_weight_y"); + htr_internal_WriteWgtrProperty(s, tree, "adj_weight_w"); + htr_internal_WriteWgtrProperty(s, tree, "adj_weight_h"); } propname = wgtrFirstPropertyName(tree); while(propname) diff --git a/centrallix/wgtr/wgtr.c b/centrallix/wgtr/wgtr.c index 0293dfc62..fc7d48e79 100755 --- a/centrallix/wgtr/wgtr.c +++ b/centrallix/wgtr/wgtr.c @@ -1312,6 +1312,9 @@ wgtrGetPropertyType(pWgtrNode widget, char* name) !strcmp(name, "r_x") || !strcmp(name, "r_y") || !strcmp(name, "r_width") || !strcmp(name, "r_height") || !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height")) return DATA_T_INTEGER; + else if (!strcmp(name, "adj_weight_x") || !strcmp(name, "adj_weight_y") || + !strcmp(name, "adj_weight_w") || !strcmp(name, "adj_weight_h")) + return DATA_T_DOUBLE; count = xaCount(&(widget->Properties)); for (i=0;iInteger = widget->fl_height; return 0; } } } + if (datatype == DATA_T_DOUBLE) + { + if (!strncmp(name, "adj_weight_", 11)) + { + if (!strcmp(name+11, "x")) { val->Double = (double)widget->xAdjWeight; return 0; } + else if (!strcmp(name+11, "y")) { val->Double = (double)widget->yAdjWeight; return 0; } + else if (!strcmp(name+11, "w")) { val->Double = (double)widget->wAdjWeight; return 0; } + else if (!strcmp(name+11, "h")) { val->Double = (double)widget->hAdjWeight; return 0; } + } + } else if (datatype == DATA_T_STRING) { if (!strcmp(name, "name")) { val->String = widget->Name; return 0; } From 837f6b4bf4b7d22a5b76686d2a76d32fc0db3f2b Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 27 Jun 2025 16:15:13 -0600 Subject: [PATCH 007/107] Reformat CSS. --- centrallix/htmlgen/ht_render.c | 59 ++++++++++++++++++++++++-------- centrallix/htmlgen/htdrv_pane.c | 60 +++++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 24 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index f2555e697..32eb99bdf 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2692,20 +2692,51 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y shadow_angle = strtod(strval, NULL); /** Generate the style CSS **/ - htrAddStylesheetItem_va(s, "\t%STR { left:%POSpx; top:%POSpx; %[width:%POSpx; %]%[height:%POSpx; %]%[z-index:%POS; %]%[color:%STR&CSSVAL; %]%[font-weight:bold; %]%[text-decoration:underline; %]%[font-style:italic; %]%[font:%STR&CSSVAL; %]%[font-size:%DBLpx; %]%[background-color:%STR&CSSVAL; %]%[background-image:url('%STR&CSSURL'); %]%[padding:%DBLpx; %]%[border:1px %STR&CSSVAL %STR&CSSVAL; %]%[border-radius:%DBLpx; %]%[text-align:%STR&CSSVAL; %]%[white-space:nowrap; %]%[box-shadow:%DBLpx %DBLpx %DBLpx %STR&CSSVAL%STR&CSSVAL; %]%[%STR %]}\n", - id, - x, y, w > 0, w, h > 0, h, z > 0, z, - *textcolor, textcolor, - !strcmp(style, "bold"), !strcmp(style, "underline"), !strcmp(style, "italic"), - *font, font, font_size > 0, font_size, - *bgcolor, bgcolor, *background, background, - padding > 0, padding, - *border_color, (*border_style)?border_style:"solid", border_color, border_radius > 0, border_radius, - *align, align, - !wrap, - (*shadow_color && shadow_radius > 0), sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color, (!strcasecmp(shadow_location,"inside"))?" inset":"", - addl && *addl, addl - ); + htrAddStylesheetItem_va(s, + "\t%STR {" + "left:%POSpx; " + "top:%POSpx; " + "%[width:%POSpx; %]" + "%[height:%POSpx; %]" + "%[z-index:%POS; %]" + "%[color:%STR&CSSVAL; %]" + "%[font-weight:bold; %]" + "%[text-decoration:underline; %]" + "%[font-style:italic; %]" + "%[font:%STR&CSSVAL; %]" + "%[font-size:%DBLpx; %]" + "%[background-color:%STR&CSSVAL; %]" + "%[background-image:url('%STR&CSSURL'); %]" + "%[padding:%DBLpx; %]" + "%[border:1px %STR&CSSVAL %STR&CSSVAL; %]" + "%[border-radius:%DBLpx; %]" + "%[text-align:%STR&CSSVAL; %]" + "%[white-space:nowrap; %]" + "%[box-shadow:%DBLpx %DBLpx %DBLpx %STR&CSSVAL%STR&CSSVAL; %]" + "%[%STR %]" + "}\n", + id, + x, + y, + (w > 0), w, + (h > 0), h, + (z > 0), z, + (*textcolor), textcolor, + (!strcmp(style, "bold")), + (!strcmp(style, "underline")), + (!strcmp(style, "italic")), + (*font), font, + (font_size > 0), font_size, + (*bgcolor), bgcolor, + (*background), background, + (padding > 0), padding, + (*border_color), (*border_style) ? border_style : "solid", border_color, + (border_radius > 0), border_radius, + (*align), align, + (!wrap), + (*shadow_color && shadow_radius > 0), sin(shadow_angle * M_PI/180) * shadow_offset, cos(shadow_angle * M_PI/180) *(-shadow_offset), shadow_radius, shadow_color, (!strcasecmp(shadow_location,"inside"))?" inset":"", + (addl && *addl), addl + ); return 0; } diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 0683cc6e6..64d5c0e68 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -154,24 +154,64 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) } /** Ok, write the style header items. **/ - if (style == 2) /* flat */ - { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-radius: %INTpx; %STR}\n",id,border_radius,main_bg); - } + int offset = 0; + if (style == 2) { /* flat, the default style, nothing to do */ } else if (style == 0 || style == 1) /* lowered or raised */ { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow: hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w-2*box_offset,h-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-style: solid; border-width: 1px; border-color: %STR %STR %STR %STR; border-radius: %INTpx; %STR}\n",id,c1,c2,c2,c1,border_radius,main_bg); + offset = -2 * box_offset; + htrAddStylesheetItem_va(s, + "\t#pn%POSmain {" + "border-style: solid; " + "border-width: 1px; " + "border-color: %STR %STR %STR %STR; " + "}\n", + id, + c1, c2, c2, c1 + ); } else if (style == 3) /* bordered */ { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow: hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w-2*box_offset,h-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-style: solid; border-width: 1px; border-color:%STR&CSSVAL; border-radius: %INTpx; %STR}\n",id,bdr,border_radius,main_bg); + offset = -2 * box_offset; + htrAddStylesheetItem_va(s, + "\t#pn%POSmain {" + "border-style: solid;" + "border-width: 1px; " + "border-color:%STR&CSSVAL; " + "}\n", + id, + bdr + ); } + + htrAddStylesheetItem_va(s, + "\t#pn%POSmain {" + "POSITION:absolute; " + "VISIBILITY:inherit; " + "overflow:hidden; " + "LEFT:%INTpx; " + "TOP:%INTpx; " + "WIDTH:%POSpx; " + "HEIGHT:%POSpx; " + "Z-INDEX:%POS; " + "border-radius: %INTpx;" + "%STR" + "}\n", + id, + x, + y, + w + offset, + h + offset, + z, + border_radius, + main_bg + ); + if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", id, shadow_offset, shadow_offset, shadow_radius, shadow_color); + htrAddStylesheetItem_va(s, + "\t#pn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", + id, shadow_offset, shadow_offset, shadow_radius, shadow_color + ); } /** DOM linkages **/ From 0f0d1e8783da4bccfd1b3932b12c0d0e08e86fba Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 27 Jun 2025 16:20:47 -0600 Subject: [PATCH 008/107] Make design responsive by replacing px with %. --- centrallix/htmlgen/ht_render.c | 16 ++++++++-------- centrallix/htmlgen/htdrv_pane.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 32eb99bdf..084c38e65 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2694,10 +2694,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y /** Generate the style CSS **/ htrAddStylesheetItem_va(s, "\t%STR {" - "left:%POSpx; " - "top:%POSpx; " - "%[width:%POSpx; %]" - "%[height:%POSpx; %]" + "left:%DBL%%; " + "top:%DBL%%; " + "%[width:%DBL%%; %]" // BUG! + "%[height:%DBL%%; %]"// BUG! "%[z-index:%POS; %]" "%[color:%STR&CSSVAL; %]" "%[font-weight:bold; %]" @@ -2716,10 +2716,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - x, - y, - (w > 0), w, - (h > 0), h, + (double)x / node->Root->width * 100.0, + (double)y / node->Root->height * 100.0, + (w > 0), (double)w / node->Parent->width * 100.0, + (h > 0), (double)h / node->Parent->height * 100.0, (z > 0), z, (*textcolor), textcolor, (!strcmp(style, "bold")), diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 64d5c0e68..86db161d9 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -188,19 +188,19 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "POSITION:absolute; " "VISIBILITY:inherit; " "overflow:hidden; " - "LEFT:%INTpx; " - "TOP:%INTpx; " - "WIDTH:%POSpx; " - "HEIGHT:%POSpx; " + "LEFT:%DBL%%; " + "TOP:%DBL%%; " + "WIDTH:%DBL%%; " + "HEIGHT:%DBL%%; " "Z-INDEX:%POS; " "border-radius: %INTpx;" "%STR" "}\n", id, - x, - y, - w + offset, - h + offset, + (double)tree->x / tree->Root->width * 100.0, + (double)tree->y / tree->Root->height * 100.0, + (double)(w + offset) / tree->Parent->width * 100.0, + (double)(h + offset) / tree->Parent->height * 100.0, z, border_radius, main_bg From 6f327d59d47e1679fb3ed74149b65e4572da1ee4 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 27 Jun 2025 16:40:45 -0600 Subject: [PATCH 009/107] Reformat CSS for autolayout. --- centrallix/htmlgen/htdrv_autolayout.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index 0dee2198b..ae1321182 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -99,11 +99,26 @@ htalRender(pHtSession s, pWgtrNode tree, int z) strtcpy(name,wgtrGetName(tree),sizeof(name)); /** Add the stylesheet for the layer **/ - htrAddStylesheetItem_va(s,"\t#al%POSbase { POSITION:absolute; VISIBILITY:inherit; OVERFLOW:visible; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n", - //htrAddStylesheetItem_va(s,"\t#al%POSbase { POSITION:absolute; VISIBILITY:inherit; OVERFLOW:visible; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; CLIP:rect(%INTpx,%INTpx,%INTpx,%INTpx); Z-INDEX:%POS; }\n", - id,x,y,w,h, - //-1, w+1, h+1, -1, - z); + htrAddStylesheetItem_va(s, + "\t#al%POSbase {" + "POSITION:absolute; " + "VISIBILITY:inherit; " + "OVERFLOW:visible; " + "LEFT:%POSpx; " + "TOP:%POSpx; " + "WIDTH:%POSpx; " + "HEIGHT:%POSpx; " + //"CLIP:rect(%INTpx,%INTpx,%INTpx,%INTpx);" + "Z-INDEX:%POS; " + "}\n", + id, + x, + y, + w, + h, + //-1, w+1, h+1, -1, + z + ); /** Linkage **/ htrAddWgtrObjLinkage_va(s, tree, "al%POSbase",id); From 7becc703a0b6821f4ccc4bec06a8237a9d96a41d Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Tue, 1 Jul 2025 13:37:08 -0600 Subject: [PATCH 010/107] Improve debugging. --- centrallix/htmlgen/ht_render.c | 4 ++++ centrallix/htmlgen/htdrv_pane.c | 4 ++-- centrallix/wgtr/wgtr.c | 12 ++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 084c38e65..7a5919f58 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1496,6 +1496,10 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) htr_internal_WriteWgtrProperty(s, tree, "r_y"); htr_internal_WriteWgtrProperty(s, tree, "r_width"); htr_internal_WriteWgtrProperty(s, tree, "r_height"); + htr_internal_WriteWgtrProperty(s, tree, "fl_x"); + htr_internal_WriteWgtrProperty(s, tree, "fl_y"); + htr_internal_WriteWgtrProperty(s, tree, "fl_width"); + htr_internal_WriteWgtrProperty(s, tree, "fl_height"); htr_internal_WriteWgtrProperty(s, tree, "adj_weight_x"); htr_internal_WriteWgtrProperty(s, tree, "adj_weight_y"); htr_internal_WriteWgtrProperty(s, tree, "adj_weight_w"); diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 86db161d9..e1503e8b5 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -197,8 +197,8 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "%STR" "}\n", id, - (double)tree->x / tree->Root->width * 100.0, - (double)tree->y / tree->Root->height * 100.0, + (double)x / tree->Root->width * 100.0, + (double)y / tree->Root->height * 100.0, (double)(w + offset) / tree->Parent->width * 100.0, (double)(h + offset) / tree->Parent->height * 100.0, z, diff --git a/centrallix/wgtr/wgtr.c b/centrallix/wgtr/wgtr.c index fc7d48e79..0fa312f19 100755 --- a/centrallix/wgtr/wgtr.c +++ b/centrallix/wgtr/wgtr.c @@ -1313,7 +1313,8 @@ wgtrGetPropertyType(pWgtrNode widget, char* name) !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height")) return DATA_T_INTEGER; else if (!strcmp(name, "adj_weight_x") || !strcmp(name, "adj_weight_y") || - !strcmp(name, "adj_weight_w") || !strcmp(name, "adj_weight_h")) + !strcmp(name, "adj_weight_w") || !strcmp(name, "adj_weight_h") || + !strcmp(name, "fx") || !strcmp(name, "fy") || !strcmp(name, "fw") || !strcmp(name, "fh")) return DATA_T_DOUBLE; count = xaCount(&(widget->Properties)); for (i=0;iDouble = widget->fx; return 0; } + else if (!strcmp(name+1, "y")) { val->Double = widget->fy; return 0; } + else if (!strcmp(name+1, "w")) { val->Double = widget->fw; return 0; } + else if (!strcmp(name+1, "h")) { val->Double = widget->fh; return 0; } + } + else if (!strncmp(name, "adj_weight_", 11)) { if (!strcmp(name+11, "x")) { val->Double = (double)widget->xAdjWeight; return 0; } else if (!strcmp(name+11, "y")) { val->Double = (double)widget->yAdjWeight; return 0; } From e3f07570ffb93cdca87f4959f128353acd8d3905 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Tue, 1 Jul 2025 15:52:13 -0600 Subject: [PATCH 011/107] Add flexibility to autoscaling, using macros to make it more readable. --- centrallix/htmlgen/ht_render.c | 16 ++++++++-------- centrallix/htmlgen/htdrv_pane.c | 16 ++++++++-------- centrallix/include/ht_render.h | 4 ++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 7a5919f58..2139ae124 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2698,10 +2698,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y /** Generate the style CSS **/ htrAddStylesheetItem_va(s, "\t%STR {" - "left:%DBL%%; " - "top:%DBL%%; " - "%[width:%DBL%%; %]" // BUG! - "%[height:%DBL%%; %]"// BUG! + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[width:"ht_flex_format"; %]" // BUG! + "%[height:"ht_flex_format"; %]"// BUG! "%[z-index:%POS; %]" "%[color:%STR&CSSVAL; %]" "%[font-weight:bold; %]" @@ -2720,10 +2720,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - (double)x / node->Root->width * 100.0, - (double)y / node->Root->height * 100.0, - (w > 0), (double)w / node->Parent->width * 100.0, - (h > 0), (double)h / node->Parent->height * 100.0, + ht_flex(x, node->Parent->width, 100.0), + ht_flex(y, node->Parent->height, 100.0), + (w > 0), ht_flex(w, node->Parent->width, node->fl_width), + (h > 0), ht_flex(h, node->Parent->height, node->fl_height), (z > 0), z, (*textcolor), textcolor, (!strcmp(style, "bold")), diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index e1503e8b5..e6fe16338 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -188,19 +188,19 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "POSITION:absolute; " "VISIBILITY:inherit; " "overflow:hidden; " - "LEFT:%DBL%%; " - "TOP:%DBL%%; " - "WIDTH:%DBL%%; " - "HEIGHT:%DBL%%; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "HEIGHT:"ht_flex_format"; " "Z-INDEX:%POS; " "border-radius: %INTpx;" "%STR" "}\n", id, - (double)x / tree->Root->width * 100.0, - (double)y / tree->Root->height * 100.0, - (double)(w + offset) / tree->Parent->width * 100.0, - (double)(h + offset) / tree->Parent->height * 100.0, + ht_flex(x, tree->Parent->width, 100.0), + ht_flex(y, tree->Parent->height, 100.0), + ht_flex(w + offset, tree->Parent->width, tree->fl_width), + ht_flex(h + offset, tree->Parent->height, tree->fl_height), z, border_radius, main_bg diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index aeded08fe..7e6b20da0 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -362,5 +362,9 @@ int htrBuildClientWgtr(pHtSession s, pWgtrNode tree); /** For the rule module... **/ int htruleRegister(char* ruletype, ...); +/** My attempt to make flex code more readable. **/ +#define ht_flex_format "calc(%POSpx + (%DBL%% - %POSpx) * %DBL)" +#define ht_flex(size, total, flex) (size), (double)(size) / (total) * 100.0, (size), (double)(flex) / 100.0 + #endif /* _HT_RENDER_H */ From 50111697ac4a00e5232078dcfc2141cce6c1f3a9 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Tue, 1 Jul 2025 17:12:26 -0600 Subject: [PATCH 012/107] Replace magic values for fl_x and fl_y with macros. --- centrallix/htmlgen/ht_render.c | 4 ++-- centrallix/htmlgen/htdrv_pane.c | 4 ++-- centrallix/include/ht_render.h | 7 +++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 2139ae124..03473eb9b 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2720,8 +2720,8 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - ht_flex(x, node->Parent->width, 100.0), - ht_flex(y, node->Parent->height, 100.0), + ht_flex(x, node->Parent->width, ht_fl_x_compat), + ht_flex(y, node->Parent->height, ht_fl_y_compat), (w > 0), ht_flex(w, node->Parent->width, node->fl_width), (h > 0), ht_flex(h, node->Parent->height, node->fl_height), (z > 0), z, diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index e6fe16338..f9106a2c0 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -197,8 +197,8 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "%STR" "}\n", id, - ht_flex(x, tree->Parent->width, 100.0), - ht_flex(y, tree->Parent->height, 100.0), + ht_flex(x, tree->Parent->width, ht_fl_x_compat), + ht_flex(y, tree->Parent->height, ht_fl_y_compat), ht_flex(w + offset, tree->Parent->width, tree->fl_width), ht_flex(h + offset, tree->Parent->height, tree->fl_height), z, diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index 7e6b20da0..3ef7893f9 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -366,5 +366,12 @@ int htruleRegister(char* ruletype, ...); #define ht_flex_format "calc(%POSpx + (%DBL%% - %POSpx) * %DBL)" #define ht_flex(size, total, flex) (size), (double)(size) / (total) * 100.0, (size), (double)(flex) / 100.0 +/*** The widget fl_x and fl_y fields are never used in the generated layout. + *** Thus, the CSS I created to add responsiveness uses the values below + *** instead of the provided value to preserve backwards compatibility. + ***/ +#define ht_fl_x_compat 100.0 +#define ht_fl_y_compat 100.0 + #endif /* _HT_RENDER_H */ From 9ea50118b47eb814e7e7d3adc013ae97ee036b72 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 3 Jul 2025 15:34:08 -0600 Subject: [PATCH 013/107] Improve code documentation and readability. --- centrallix/include/apos.h | 3 ++- centrallix/include/ht_render.h | 6 +++--- centrallix/wgtr/apos.c | 26 ++++++++++++++++---------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index 70bb49f12..da8792946 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -158,12 +158,13 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t #define APOS_IS_BORDER 1 #define APOS_NOT_BORDER 0 +/** Allows rounding when casting floats or doubles to ints. */ #define APOS_FUDGEFACTOR 0.5 /*** The greatest width between two widgets that still defines them as *** "adjacent," indicating that we don't want to increase the distance *** between them. Therefore, a section of this size or less is considered - *** a "spacer" which will not be resized. + *** a "spacer" which will not be resized (aka. flex = 0). ***/ #define APOS_MINSPACE 20 diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index 3ef7893f9..e0448404f 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -362,9 +362,9 @@ int htrBuildClientWgtr(pHtSession s, pWgtrNode tree); /** For the rule module... **/ int htruleRegister(char* ruletype, ...); -/** My attempt to make flex code more readable. **/ -#define ht_flex_format "calc(%POSpx + (%DBL%% - %POSpx) * %DBL)" -#define ht_flex(size, total, flex) (size), (double)(size) / (total) * 100.0, (size), (double)(flex) / 100.0 +/** ===================================================== **/ +/** Define macros for implementing responsive dimensions. **/ +/** ===================================================== **/ /*** The widget fl_x and fl_y fields are never used in the generated layout. *** Thus, the CSS I created to add responsiveness uses the values below diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index d802fca9c..4b476038c 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -78,6 +78,11 @@ *** *** XArray: This array also stores its size (nAlloc) and the number of items *** stored (nItems), so you don't have to pass that info separately. + *** + *** SWidgets, CWidgets, and EWidgets: Lines record which widgets start, cross, + *** and end on them. These categories are exclusive, so a widget which + *** starts on a given line will be in the SWidgets list but it will not be + *** in the CWidgets list. ***/ #include @@ -529,8 +534,10 @@ int i=0, sectCount=0, TotalWidth=0, ProductSum=0; if (!theGrid) return 0; - /** Calculate average row flexibility, weighted by height. **/ - /** Note: Height is called width because rows are 1 dimentional. **/ + /*** Calculate average row flexibility, weighted by height. + *** Note: Section height is called width here because rows + *** are one dimentional and the feild is reused. + ***/ sectCount = xaCount(&(theGrid->Rows)); for(i=0; iFlex) / (float)(TotalFlex); - SizeWeight = 0; - - /** unless there's at least some flexibility, don't factor in size **/ - if(FlexWeight > 0) - SizeWeight = (float)(PrevSect->Width) / (float)(TotalFlexibleSpace); + SizeWeight = (FlexWeight > 0) ? (float)(PrevSect->Width) / (float)(TotalFlexibleSpace) : 0; /*** Calculate the adjustment weight, and also save it so we can *** replicate some of the following logic in the CSS we will @@ -1598,7 +1600,7 @@ pWgtrNode Widget; } } - /** Adjusts width or height of widgets ending on this line **/ + /** Adjusts width or height of widgets ending on this line. **/ count = xaCount(&(CurrLine->EWidgets)); for(j=0; jLoc - Widget->x - isSideTab*tabWidth; + + /** If the new size is now smaller than the minimum, clamp it. **/ if (newsize < APOS_MINWIDTH && Widget->pre_width >= APOS_MINWIDTH) Widget->width = APOS_MINWIDTH; + /** If the size is bigger than the minimum, or growing, that's fine. **/ else if (newsize >= APOS_MINWIDTH || newsize >= Widget->pre_width) Widget->width = newsize; + /** Otherwise, we can't update the size. **/ else /*Widget->width = APOS_MINWIDTH;*/ Widget->width = Widget->pre_width; From 108e14c6778223be3c9532a1e2f18e513d3f8a46 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 3 Jul 2025 15:36:30 -0600 Subject: [PATCH 014/107] Fix flexibility a little, but it's still broken. --- centrallix/htmlgen/ht_render.c | 8 ++--- centrallix/htmlgen/htdrv_pane.c | 8 ++--- centrallix/include/ht_render.h | 54 +++++++++++++++++++++++++++++++-- centrallix/include/wgtr.h | 4 +-- centrallix/wgtr/apos.c | 31 +++++++++++++------ 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 03473eb9b..0a6844768 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2720,10 +2720,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - ht_flex(x, node->Parent->width, ht_fl_x_compat), - ht_flex(y, node->Parent->height, ht_fl_y_compat), - (w > 0), ht_flex(w, node->Parent->width, node->fl_width), - (h > 0), ht_flex(h, node->Parent->height, node->fl_height), + ht_flex(x, node->Parent->width, ht_get_fl_x(node)), + ht_flex(y, node->Parent->height, ht_get_fl_y(node)), + (w > 0), ht_flex(w, node->Parent->width, ht_get_fl_w(node)), + (h > 0), ht_flex(h, node->Parent->height, ht_get_fl_h(node)), (z > 0), z, (*textcolor), textcolor, (!strcmp(style, "bold")), diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index f9106a2c0..9d0f23416 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -197,10 +197,10 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "%STR" "}\n", id, - ht_flex(x, tree->Parent->width, ht_fl_x_compat), - ht_flex(y, tree->Parent->height, ht_fl_y_compat), - ht_flex(w + offset, tree->Parent->width, tree->fl_width), - ht_flex(h + offset, tree->Parent->height, tree->fl_height), + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w + offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h + offset, tree->Parent->height, ht_get_fl_h(tree)), z, border_radius, main_bg diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index e0448404f..d078ba1e0 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -370,8 +370,58 @@ int htruleRegister(char* ruletype, ...); *** Thus, the CSS I created to add responsiveness uses the values below *** instead of the provided value to preserve backwards compatibility. ***/ -#define ht_fl_x_compat 100.0 -#define ht_fl_y_compat 100.0 +/** @brief widget->fl_x is never used. Use this instead for compatibility. **/ +#define ht_fl_x_compat 1.0 +/** @brief widget->fl_y is never used. Use this instead for compatibility. **/ +#define ht_fl_y_compat 1.0 + +/** @brief The qprintf format to specify a responsive dimension. **/ +#define ht_flex_format "calc(%INTpx + (%DBL%% - %INTpx) * %DBL)" +/*** @brief The function which generates the values that should be passed to + *** qprintf in order to satisfy an ht_flex_format. + *** + *** @param size The original size of the ui element. + *** @param total The total size of the ui element's container. + *** @param flex The flexibility of the ui element. It is strongly recomended + *** to generate this with an ht_get_fl function call. + *** @returns Several values to serve as parameters for a qprintf call. + ***/ +#define ht_flex(size, total, flex) (size), (double)(size) / (total) * 100.0, (size), (flex) +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the x direction. + ***/ +#define ht_get_fl_x(widget) (ht_fl_x_compat) +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the y direction. + ***/ +#define ht_get_fl_y(widget) (ht_fl_y_compat) +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the width direction. + ***/ +#define ht_get_fl_w(widget) ((widget)->wAdjWeight) +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the height direction. + ***/ +#define ht_get_fl_h(widget) ((widget)->hAdjWeight) +/*** @brief A shortcut function to get the flexibility when writing the + *** LEFT CSS attribute. + *** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the left direction. + ***/ +#define ht_get_fl_l ht_get_fl_x +/*** @brief A shortcut function to get the flexibility when writing the + *** TOP CSS attribute. + *** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the top direction. + ***/ +#define ht_get_fl_t ht_get_fl_y + +/*** Alternate formula suggested in the GitHub Issue. I think this should work + *** and be more efficient, but for some reason it doesn't at all, and I can't + *** understand why. I probably just implemented it wrong. + ***/ +// #define ht_flex_format "calc(%INTpx + (100%% - %INTpx) * %DBL)" +// #define ht_flex(size, total, flex) (size), (total), (double)(flex) / 100.0 #endif /* _HT_RENDER_H */ diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index eaf865ff4..f11584f0d 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -102,8 +102,8 @@ typedef struct _WN int pre_x, pre_y, pre_width, pre_height; /** pre-layout geom. **/ int fl_x, fl_y, fl_width, fl_height;/** Flexibility **/ double fx, fy, fw, fh; /** internal flexibility calculations **/ - float xAdjWeight, yAdjWeight; /** Responsive CSS adjustment weights for x and y */ - float wAdjWeight, hAdjWeight; /** Responsive CSS adjustment weights for width and height */ + double xAdjWeight, yAdjWeight; /** Responsive CSS adjustment weights for x and y */ + double wAdjWeight, hAdjWeight; /** Responsive CSS adjustment weights for width and height */ int min_width, min_height; /** absolute minimums **/ int x, y, width, height; /** actual geometry **/ int top, bottom, left, right; /** container offsets **/ diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 4b476038c..619736703 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -1505,20 +1505,20 @@ float TotalSum=0; *** eventually send to the client. ***/ float AdjWeight = PrevSect->AdjWeight = (float)(FlexWeight*SizeWeight)/TotalSum; + + /** Store the line adjustment weight for responsive CSS later.**/ + CurrLine->LocAdjWeight = PrevLine->LocAdjWeight + AdjWeight; + CurrLine->MyAdjWeight = AdjWeight; /**for expanding lines**/ if(Diff > 0) { /** Calculate adjustment using the adjustment weight. **/ Adj = (float)(Diff) * AdjWeight + APOS_FUDGEFACTOR; - - /** Store the line adjustment weight for responsive CSS later.**/ - CurrLine->LocAdjWeight = PrevLine->LocAdjWeight + AdjWeight; - CurrLine->MyAdjWeight = AdjWeight; /** Apply the calculated adjustment.**/ - CurrLine->Loc = PrevLine->Loc + PrevSect->Width + Adj; PrevSect->Width += Adj; + CurrLine->Loc = PrevLine->Loc + PrevSect->Width; } /**for contracting lines**/ else if(Diff < 0) @@ -1551,8 +1551,8 @@ float TotalSum=0; } else { - CurrLine->Loc = PrevLine->Loc + PrevSect->Width + Adj; PrevSect->Width += Adj; + CurrLine->Loc = PrevLine->Loc + PrevSect->Width; } } } @@ -1623,11 +1623,11 @@ pWgtrNode Widget; *** to problems down the road, but I plan to fix *** them if and when I encounter them. ***/ - Widget->hAdjWeight = CurrLine->MyAdjWeight; + Widget->hAdjWeight += CurrLine->MyAdjWeight; } else if(flag==APOS_COL && Widget->fl_width) { - /** Calculate the new size, taking APOS_MINWIDTH into account.**/ + /** Calculate the new size, taking APOS_MINWIDTH into account. **/ newsize = CurrLine->Loc - Widget->x - isSideTab*tabWidth; /** If the new size is now smaller than the minimum, clamp it. **/ @@ -1646,9 +1646,22 @@ pWgtrNode Widget; *** to problems down the road, but I plan to fix *** them if and when I encounter them. ***/ - Widget->hAdjWeight = CurrLine->MyAdjWeight; + Widget->wAdjWeight += CurrLine->MyAdjWeight; } } + + /** Adjusts width or height of widgets ending on this line. **/ + count = xaCount(&(CurrLine->CWidgets)); + printf("Doing %d widgets.\n", count); + for(j=0; jCWidgets), j); + if(flag==APOS_ROW && Widget->fl_height) + Widget->hAdjWeight += CurrLine->MyAdjWeight; + else if(flag==APOS_COL && Widget->fl_width) + Widget->wAdjWeight += CurrLine->MyAdjWeight; + } + } return 0; From bbc0db3847c7f4cde2d6160e947a8e8f978a38c7 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 3 Jul 2025 15:37:24 -0600 Subject: [PATCH 015/107] Attempt to add responsiveness to widgets (doesn't quite work): autolayout, button, editbox, html, image, label, scrollpane, textbutton, & treeview. --- centrallix/htmlgen/htdrv_autolayout.c | 20 ++-- centrallix/htmlgen/htdrv_button.c | 100 +++++++++++++++--- centrallix/htmlgen/htdrv_editbox.c | 38 ++++++- centrallix/htmlgen/htdrv_html.c | 62 +++++++++-- centrallix/htmlgen/htdrv_image.c | 17 ++- centrallix/htmlgen/htdrv_label.c | 57 ++++++++-- centrallix/htmlgen/htdrv_scrollpane.c | 144 ++++++++++++++++++++++++-- centrallix/htmlgen/htdrv_textbutton.c | 48 ++++++--- centrallix/htmlgen/htdrv_treeview.c | 43 +++++++- 9 files changed, 458 insertions(+), 71 deletions(-) diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index ae1321182..66b7f83e0 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -100,23 +100,21 @@ htalRender(pHtSession s, pWgtrNode tree, int z) /** Add the stylesheet for the layer **/ htrAddStylesheetItem_va(s, - "\t#al%POSbase {" + "\t#al%POSbase { " "POSITION:absolute; " "VISIBILITY:inherit; " "OVERFLOW:visible; " - "LEFT:%POSpx; " - "TOP:%POSpx; " - "WIDTH:%POSpx; " - "HEIGHT:%POSpx; " - //"CLIP:rect(%INTpx,%INTpx,%INTpx,%INTpx);" + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "HEIGHT:"ht_flex_format"; " "Z-INDEX:%POS; " "}\n", id, - x, - y, - w, - h, - //-1, w+1, h+1, -1, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), z ); diff --git a/centrallix/htmlgen/htdrv_button.c b/centrallix/htmlgen/htdrv_button.c index 67439566e..1779b54be 100644 --- a/centrallix/htmlgen/htdrv_button.c +++ b/centrallix/htmlgen/htdrv_button.c @@ -150,7 +150,21 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if (!strcmp(type,"image") || !strcmp(type,"textoverimage")) { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); + htrAddStylesheetItem_va(s, + "\t#gb%POSpane { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z + ); htrAddScriptGlobal(s, "gb_cur_img", "null", 0); htrAddScriptGlobal(s, "gb_current", "null", 0); @@ -191,10 +205,24 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) } /* text over image */ if(!strcmp(type,"textoverimage")) - { - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+1); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h,fgcolor1,text); - } + { + htrAddStylesheetItem_va(s, + "\t#gb%POSpane2 { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:%POS; " + "TOP:%POS; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + 0, + 0, + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z+1 + ); + htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h,fgcolor1,text); + } else htrAddBodyItem_va(s,""); } @@ -226,11 +254,11 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if(s->Capabilities.Dom0NS) { /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:%STR; LEFT:-1; TOP:-1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_enabled?"inherit":"hidden",w-1,z+1); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_enabled?"hidden":"inherit",w-1,z+1); - htrAddStylesheetItem_va(s,"\t#gb%POStop { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",w,z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSbtm { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",w,z+2); + htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:"ht_flex_format"; TOP:"ht_flex_format"; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(x,tree->Parent->width,ht_get_fl_x(tree)),ht_flex(y,tree->Parent->height,ht_get_fl_y(tree)),ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); + htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:%STR; LEFT:-1; TOP:-1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_enabled?"inherit":"hidden",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+1); + htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_enabled?"hidden":"inherit",ht_flex(w-1,tree->Parent->width,ht_get_fl_w(tree)),z+1); + htrAddStylesheetItem_va(s,"\t#gb%POStop { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+2); + htrAddStylesheetItem_va(s,"\t#gb%POSbtm { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+2); htrAddStylesheetItem_va(s,"\t#gb%POSrgt { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); htrAddStylesheetItem_va(s,"\t#gb%POSlft { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); @@ -307,19 +335,59 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) { if(h >=0 ) { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx %INTpx %INTpx)}\n",id,x,y,w-1-2*box_offset,z,0,w-1-2*box_offset+2*clip_offset,h-1-2*box_offset+2*clip_offset,0); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2, #gb%POSpane3 { height: %POSpx;}\n",id,id,h-3); - htrAddStylesheetItem_va(s,"\t#gb%POSpane { height: %POSpx;}\n",id,h-1-2*box_offset); + htrAddStylesheetItem_va(s, + "\t#gb%POSpane { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "OVERFLOW:hidden; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "CLIP:rect(" + "0, " + ht_flex_format", " + ht_flex_format", " + "0" + "); " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(w-1-2*box_offset+2*clip_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h-1-2*box_offset+2*clip_offset, tree->Parent->height, ht_get_fl_h(tree)), + z + ); + htrAddStylesheetItem_va(s,"\t#gb%POSpane2, #gb%POSpane3 { height: "ht_flex_format";}\n",id,id,ht_flex(h-3,tree->Parent->height,ht_get_fl_h(tree))); + htrAddStylesheetItem_va(s,"\t#gb%POSpane { height: "ht_flex_format";}\n",id,ht_flex(h-1-2*box_offset,tree->Parent->height,ht_get_fl_h(tree))); } else { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx auto %INTpx)}\n",id,x,y,w-1-2*box_offset,z,0,w-1-2*box_offset+2*clip_offset,0); + htrAddStylesheetItem_va(s, + "\t#gb%POSpane { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "OVERFLOW:hidden; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "CLIP:rect(0, "ht_flex_format", auto, 0);" + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(w-1-2*box_offset+2*clip_offset, tree->Parent->width, ht_get_fl_w(tree)), + z + ); } htrAddStylesheetItem_va(s,"\t#gb%POSpane, #gb%POSpane2, #gb%POSpane3 { cursor:default; text-align: center; }\n",id,id,id); htrAddStylesheetItem_va(s,"\t#gb%POSpane { %STR border-width: 1px; border-style: solid; border-color: white gray gray white; }\n",id,bgstyle); /*htrAddStylesheetItem_va(s,"\t#gb%dpane { color: %s; }\n",id,fgcolor2);*/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:-1px; top: -1px; width:%POSpx; }\n",id,is_enabled?"inherit":"hidden",z+1,w-3); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:0px; top: 0px; width:%POSpx; }\n",id,is_enabled?"hidden":"inherit",z+1,w-3); + htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:-1px; top: -1px; width:"ht_flex_format"; }\n",id,is_enabled?"inherit":"hidden",z+1,ht_flex(w-3,tree->Parent->width,ht_get_fl_w(tree))); + htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:0px; top: 0px; width:"ht_flex_format"; }\n",id,is_enabled?"hidden":"inherit",z+1,ht_flex(w-3,tree->Parent->width,ht_get_fl_w(tree))); if(!strcmp(type,"text")) { diff --git a/centrallix/htmlgen/htdrv_editbox.c b/centrallix/htmlgen/htdrv_editbox.c index 0223038f9..7290692a7 100644 --- a/centrallix/htmlgen/htdrv_editbox.c +++ b/centrallix/htmlgen/htdrv_editbox.c @@ -149,8 +149,35 @@ htebRender(pHtSession s, pWgtrNode tree, int z) box_offset = 0; /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#eb%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; overflow:hidden; }\n",id,x,y,w-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#eb%POScon1 { VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; border:none; }\n",id,5,0,w-10,z+1); + htrAddStylesheetItem_va(s, + "\t#eb%POSbase { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "overflow:hidden; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w - 2 * box_offset, tree->Parent->width, ht_get_fl_w(tree)), + z + ); + htrAddStylesheetItem_va(s, + "\t#eb%POScon1 { " + "VISIBILITY:inherit; " + "LEFT:5px; " + "TOP:0px; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "border:none; " + "}\n", + id, + ht_flex(w - 10, tree->Parent->width, ht_get_fl_w(tree)), + z+1 + ); /** Write named global **/ htrAddWgtrObjLinkage_va(s, tree, "eb%POSbase",id); @@ -187,7 +214,12 @@ htebRender(pHtSession s, pWgtrNode tree, int z) else htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: gray white white gray; %STR }\n",id, main_bg); if (h >= 0) - htrAddStylesheetItem_va(s,"\t#eb%POSbase { height:%POSpx; }\n\t#eb%POScon1 { height:%POSpx; }\n", id, h-2*box_offset, id, h-2*box_offset-2); + htrAddStylesheetItem_va(s, + "\t#eb%POSbase { height:"ht_flex_format"; }\n" + "\t#eb%POScon1 { height:"ht_flex_format"; }\n", + id, ht_flex(h - 2 * box_offset, tree->Parent->height, ht_get_fl_h(tree)), + id, ht_flex(h - 2 * box_offset - 2, tree->Parent->height, ht_get_fl_h(tree)) + ); //htrAddBodyItem_va(s, "
 
\n", w-2, h-2); //htrAddBodyItem_va(s, "
\n",id); diff --git a/centrallix/htmlgen/htdrv_html.c b/centrallix/htmlgen/htdrv_html.c index 9b899802a..3e7238209 100644 --- a/centrallix/htmlgen/htdrv_html.c +++ b/centrallix/htmlgen/htdrv_html.c @@ -101,17 +101,59 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) { /** Only give x and y if supplied. **/ if (x < 0 || y < 0) - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z+1); - } + { + htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); + htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); + htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+1); + } else - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z+1); - } + { + htrAddStylesheetItem_va(s, + "\t#ht%POSpane { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z + ); + htrAddStylesheetItem_va(s, + "\t#ht%POSpane2 { " + "POSITION:absolute; " + "VISIBILITY:hidden; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z + ); + htrAddStylesheetItem_va(s, + "\t#ht%POSfader { " + "POSITION:absolute; " + "VISIBILITY:hidden; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z+1 + ); + } if (s->Capabilities.CSS1) { diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index 7ea3b9d47..b7195771b 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -145,7 +145,22 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) form[0]='\0'; /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#img%POS { left:%INTpx; top:%INTpx; width:%POSpx; height:%POSpx; z-index:%POS; text-align:center; }\n",id,x,y,w,h,z); + htrAddStylesheetItem_va(s, + "\t#img%POS { " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "text-align:center; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + z + ); /** Init image widget (?) **/ htrAddWgtrObjLinkage_va(s, tree, "img%POS",id); diff --git a/centrallix/htmlgen/htdrv_label.c b/centrallix/htmlgen/htdrv_label.c index d7140489f..27f53c9d2 100644 --- a/centrallix/htmlgen/htdrv_label.c +++ b/centrallix/htmlgen/htdrv_label.c @@ -197,17 +197,60 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) form[0]='\0'; /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#lbl%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]WIDTH:%POSpx; Z-INDEX:%POS; cursor:default; %[font-weight:bold; %]%[color:%STR&CSSVAL; %]%[font-size:%POSpx; %]text-align:%STR&CSSVAL; vertical-align:%STR&CSSVAL; %[white-space:nowrap; %]%[text-overflow:ellipsis; overflow:hidden; %]%[font-style:italic; %]}\n", - id,x,y, - !auto_height, h, - w,z, - is_bold, *fgcolor, fgcolor, font_size > 0, font_size, align, valign, - !allow_break, overflow_ellipsis, is_italic); + htrAddStylesheetItem_va(s, + "\t#lbl%POS { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "%[HEIGHT:"ht_flex_format"; %]" + "Z-INDEX:%POS; " + "cursor:default; " + "%[font-weight:bold; %]" + "%[color:%STR&CSSVAL; %]" + "%[font-size:%POSpx; %]" + "text-align:%STR&CSSVAL; " + "vertical-align:%STR&CSSVAL; " + "%[white-space:nowrap; %]" + "%[text-overflow:ellipsis; overflow:hidden; %]" + "%[font-style:italic; %]" + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + (!auto_height), ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + z, + (is_bold), + (*fgcolor), fgcolor, + (font_size > 0), font_size, + align, + valign, + (!allow_break), + (overflow_ellipsis), + (is_italic) + ); + if (is_link) htrAddStylesheetItem_va(s,"\t#lbl%POS:hover { %[color:%STR&CSSVAL; %]text-decoration:underline; cursor:pointer; }\n", id, *pfgcolor, pfgcolor); if (is_link && *cfgcolor) htrAddStylesheetItem_va(s,"\t#lbl%POS:active { color:%STR&CSSVAL; text-decoration:underline; cursor:pointer; }\n", id, cfgcolor); - htrAddStylesheetItem_va(s,"\t#lbl%POS p { text-align:%STR&CSSVAL; %[position:relative; top:50%%; transform:translateY(-50%%); %]padding:0px; margin:0px; border-spacing:0px; width:%POSpx; }\n", id, align, !strcmp(valign, "middle"), w); + + htrAddStylesheetItem_va(s, + "\t#lbl%POS p { " + "text-align:%STR&CSSVAL; " + "%[position:relative; top:50%%; transform:translateY(-50%%); %]" + "padding:0px; " + "margin:0px; " + "border-spacing:0px; " + "width:"ht_flex_format"; " + "}\n", + id, + align, + !strcmp(valign, "middle"), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)) + ); htrAddWgtrObjLinkage_va(s, tree, "lbl%POS",id); stylestr[0] = '\0'; diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index db1a6ec40..3ceafbd4d 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -118,9 +118,52 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ if (s->Capabilities.Dom0NS) { - htrAddStylesheetItem_va(s,"\t#sp%POSpane { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; clip:rect(0px,%POSpx,%POSpx,0px); Z-INDEX:%POS; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); - htrAddStylesheetItem_va(s,"\t#sp%POSarea { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:0px; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w-18,z+1); - htrAddStylesheetItem_va(s,"\t#sp%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:18px; WIDTH:18px; Z-INDEX:%POS; }\n",id,w-18,z+1); + htrAddStylesheetItem_va(s, + "\t#sp%POSpane { " + "POSITION:absolute; " + "VISIBILITY:%STR; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "HEIGHT:"ht_flex_format"; " + "clip:rect(0px, "ht_flex_format", "ht_flex_format", 0px); " + "Z-INDEX:%POS; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + z + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSarea { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:0px; " + "TOP:0px; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_w(tree)), + z + 1 + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSthum { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:18px; " + "WIDTH:18px; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + z + 1 + ); } /** Write globals for internal use **/ @@ -155,15 +198,98 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) //htrAddStylesheetItem_va(s,"\t#sp%dpane { POSITION:absolute; VISIBILITY:%s; LEFT:%dpx; TOP:%dpx; WIDTH:%dpx; HEIGHT:%dpx; clip:rect(0px,%dpx,%dpx,0px); Z-INDEX:%d; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); //htrAddStylesheetItem_va(s,"\t#sp%darea { HEIGHT: %dpx; WIDTH:%dpx; }\n",id, h, w-18); //htrAddStylesheetItem_va(s,"\t#sp%dthum { POSITION:absolute; VISIBILITY:inherit; LEFT:%dpx; TOP:18px; WIDTH:18px; Z-INDEX:%dpx; }\n",id,w-18,z+1); - htrAddBodyItem_va(s,"
\n",id,visible?"inherit":"hidden",x,y,w,h,w,h,z); + htrAddBodyItem_va(s, + "
\n", + id, + (visible) ? "inherit" : "hidden", + ht_flex(x, tree->Parent->width, ht_fl_x_compat), + ht_flex(y, tree->Parent->height, ht_fl_y_compat), + ht_flex(w, tree->Parent->width, tree->fl_width), + ht_flex(h, tree->Parent->height, tree->fl_height), + ht_flex(w, tree->Parent->width, tree->fl_width), ht_flex(h, tree->Parent->height, tree->fl_height), + z + ); htrAddBodyItem_va(s,"", id); htrAddBodyItem_va(s,"", id); htrAddBodyItem_va(s,"", id); - htrAddStylesheetItem_va(s,"\t#sp%POSup { POSITION: absolute; LEFT: %INTpx; TOP: 0px; }\n",id, w-18); - htrAddStylesheetItem_va(s,"\t#sp%POSbar { POSITION: absolute; LEFT: %INTpx; TOP: 18px; WIDTH: 18px; HEIGHT: %POSpx;}\n",id, w-18, h-36); - htrAddStylesheetItem_va(s,"\t#sp%POSdown { POSITION: absolute; LEFT: %INTpx; TOP: %INTpx; }\n",id, w-18, h-18); - htrAddBodyItem_va(s,"
\n", id,w-18,z+1); - htrAddBodyItem_va(s,"
",id,h,w-18,z+1); + htrAddStylesheetItem_va(s, + "\t#sp%POSup { " + "POSITION:absolute; " + "LEFT:"ht_flex_format"; " + "TOP:0px; " + "}\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)) + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSbar { " + "POSITION:absolute; " + "LEFT:"ht_flex_format"; " + "TOP:18px; " + "WIDTH:18px; " + "HEIGHT:"ht_flex_format"; " + "}\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(h - 36, tree->Parent->height, ht_get_fl_h(tree)) + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSdown { " + "POSITION:absolute; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "}\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(h - 18, tree->Parent->height, ht_get_fl_y(tree)) + ); + htrAddBodyItem_va(s, + "
" + "" + "
\n", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + z + 1 + ); + htrAddBodyItem_va(s, + "
", + id, + ht_flex(w - 18, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + z + 1 + ); } else { diff --git a/centrallix/htmlgen/htdrv_textbutton.c b/centrallix/htmlgen/htdrv_textbutton.c index 34575df52..1f6c1afef 100644 --- a/centrallix/htmlgen/htdrv_textbutton.c +++ b/centrallix/htmlgen/htdrv_textbutton.c @@ -206,10 +206,25 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); /** Initial CSS styles **/ - htrAddStylesheetItem_va(s,"\t#tb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; display:table; }\n", - id, - x, y, h>=0, h-1-2*box_offset, w-1-2*box_offset, z - ); + htrAddStylesheetItem_va(s, + "\t#tb%POSpane { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "%[HEIGHT:"ht_flex_format"; %]" + "Z-INDEX:%POS; " + "OVERFLOW:hidden; " + "display:table; " + "}\n", + id, + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), + (h>=0), ht_flex(h-1-2*box_offset, tree->Parent->height, ht_get_fl_h(tree)), + z + ); htrAddStylesheetItem_va(s, "\t#tb%POSpane .cell { height:100%%; width:100%%; vertical-align:middle; display:table-cell; padding:1px; font-weight:bold; cursor:default; text-align:%STR; border-width:1px; border-style:%STR&CSSVAL; border-color:%STR&CSSVAL; border-radius:%INTpx; color:%STR&CSSVAL; %[text-shadow:1px 1px %STR&CSSVAL; %]%STR }\n", /* clipping no longer needed: 0, w-1-2*box_offset+2*clip_offset, h-1-2*box_offset+2*clip_offset, 0, */ id, @@ -249,14 +264,23 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) //htrAddBodyItem(s, "
"); /** We need two DIVs here because of a long-outstanding Firefox bug :( **/ - htrAddBodyItem_va(s,"
%[
%]%[%]%STR&HTE%[%]%[
%]
", - id, - image[0] && !strcmp(image_position, "top"), image, - image[0] && !strcmp(image_position, "left"), image, - text, - image[0] && !strcmp(image_position, "right"), image, - image[0] && !strcmp(image_position, "bottom"), image - ); + htrAddBodyItem_va(s, + "
" + "
" + "%[
%]" + "%[%]" + "%STR&HTE" + "%[%]" + "%[
%]" + "
" + "
", + id, + (image[0] && !strcmp(image_position, "top")), image, + (image[0] && !strcmp(image_position, "left")), image, + text, + (image[0] && !strcmp(image_position, "right")), image, + (image[0] && !strcmp(image_position, "bottom")), image + ); /** Script initialization call. **/ //htrAddScriptInit_va(s, " tb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), span:document.getElementById(\"tb%POSspan\"), ena:%INT, c1:\"%STR&JSSTR\", c2:\"%STR&JSSTR\", dc1:\"%STR&JSSTR\", top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR'});\n", diff --git a/centrallix/htmlgen/htdrv_treeview.c b/centrallix/htmlgen/htdrv_treeview.c index 413a2123c..106b49718 100644 --- a/centrallix/htmlgen/htdrv_treeview.c +++ b/centrallix/htmlgen/htdrv_treeview.c @@ -172,7 +172,22 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ if (s->Capabilities.Dom0NS) { - htrAddStylesheetItem_va(s,"\t#tv%POSroot { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,show_root?"inherit":"hidden",x,y,w,z); + htrAddStylesheetItem_va(s, + "\t#tv%POSroot { " + "POSITION:absolute; " + "VISIBILITY:%STR; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + (show_root) ? "inherit" : "hidden", + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z + ); } htrAddStylesheetItem_va(s,"\t#tv%POSload { POSITION:absolute; VISIBILITY:hidden; OVERFLOW:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; clip:rect(0px,0px,0px,0px); Z-INDEX:0; }\n",id); htrAddStylesheetItem_va(s,"\tdiv.tv%POS a { %[color:%STR&CSSVAL;%] }\n", id, *fgcolor, fgcolor); @@ -205,7 +220,31 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) } else { - htrAddBodyItem_va(s, "
 %STR&HTE
\n",id,id,show_root?"inherit":"hidden",x,y,w,z,(*icon)?icon:"/sys/images/ico02b.gif", src); + htrAddBodyItem_va(s, + "
" + "" + " %STR&HTE" + "
\n", + id, /** Class **/ + id, /** ID **/ + (show_root) ? "inherit" : "hidden", + ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), + ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + z, + (*icon) ? icon : "/sys/images/ico02b.gif", src + ); htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "tv%POSload", id, NULL, ""); /*htrAddBodyItem_va(s, "
\n",id);*/ } From bfadfe7adc7a99dbf8e55c49f2d7fb5309f62074 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Thu, 3 Jul 2025 15:37:40 -0600 Subject: [PATCH 016/107] Build testing apps. --- centrallix-os/samples/autoscale_test.app | 71 +++++++++++++++++++++++ centrallix-os/samples/autoscale_test2.app | 19 ++++++ 2 files changed, 90 insertions(+) create mode 100644 centrallix-os/samples/autoscale_test.app create mode 100644 centrallix-os/samples/autoscale_test2.app diff --git a/centrallix-os/samples/autoscale_test.app b/centrallix-os/samples/autoscale_test.app new file mode 100644 index 000000000..bcaace5c6 --- /dev/null +++ b/centrallix-os/samples/autoscale_test.app @@ -0,0 +1,71 @@ +$Version=2$ +MyPage "widget/page" + { + title = "Responsive Testing Page"; + bgcolor = "black"; + textcolor = "#00f8ff"; + width = 1000; + height = 1000; + + auto "widget/hbox" + { + x=100; y=50; width=900; height=750; + spacing=20; row_height=300; + fl_width=100; fl_height=100; + + pane0 "widget/pane" { fl_width=100; fl_height=100; width=190; height=180; bgcolor = "#9cf"; } // x=100; y=50; + pane1 "widget/pane" { fl_width=100; fl_height=100; width=130; height=180; bgcolor = "#ccc"; } // x=305; y=50; + pane2 "widget/pane" { fl_width=100; fl_height=100; width=80; height=320; bgcolor = "#f99"; } // x=455; y=50; + pane3 "widget/pane" { fl_width=100; fl_height=100; width=150; height=140; bgcolor = "#9f9"; } // x=555; y=50; + pane4 "widget/pane" { fl_width=100; fl_height=100; width=90; height=240; bgcolor = "#99f"; } // x=725; y=50; + pane5 "widget/pane" { fl_width=100; fl_height=100; width=230; height=100; bgcolor = "#ff9"; } // x=80; y=390; + pane6 "widget/pane" { fl_width=100; fl_height=100; width=80; height=200; bgcolor = "#f9f"; } // x=325; y=390; + pane7 "widget/pane" { fl_width=100; fl_height=100; width=170; height=220; bgcolor = "#9ff"; } // x=425; y=390; + pane8 "widget/pane" { fl_width=100; fl_height=100; width=110; height=200; bgcolor = "#fc9"; } // x=615; y=390; + pane9 "widget/pane" { fl_width=100; fl_height=100; width=130; height=120; bgcolor = "#cf9"; } // x=745; y=390; + } + + // pane0 "widget/pane" { x=100; y=50; width=190; height=180; bgcolor = "#9cf"; } + // pane1 "widget/pane" { x=305; y=50; width=130; height=180; bgcolor = "#ccc"; } + // pane2 "widget/pane" { x=455; y=50; width=80; height=320; bgcolor = "#f99"; } + // pane3 "widget/pane" { x=555; y=50; width=150; height=140; bgcolor = "#9f9"; } + // pane4 "widget/pane" { x=725; y=50; width= 90; height=240; bgcolor = "#99f"; } + // pane5 "widget/pane" { x=80; y=390; width=230; height=100; bgcolor = "#ff9"; } + // pane6 "widget/pane" { x=325; y=390; width=80; height=200; bgcolor = "#f9f"; } + // pane7 "widget/pane" { x=425; y=390; width=170; height=220; bgcolor = "#9ff"; } + // pane8 "widget/pane" { x=615; y=390; width=110; height=200; bgcolor = "#fc9"; } + // pane9 "widget/pane" { x=745; y=390; width=130; height=120; bgcolor = "#cf9"; } + + paneA "widget/pane" { x=40; y=680; width=890; height=220; bgcolor = "#620"; } + + // Outline the visible area. + top_left0 "widget/pane" { x=0; y=0; width=10; height=10; bgcolor = "#f00"; } + top_right0 "widget/pane" { x=990; y=0; width=10; height=10; bgcolor = "#ff0"; } + bottom_left0 "widget/pane" { x=0; y=990; width=10; height=10; bgcolor = "#0f0"; } + bottom_right0 "widget/pane" { x=990; y=990; width=10; height=10; bgcolor = "#00f"; } + + // Advance markers. + top_left1 "widget/pane" { x=100; y=100; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#a00"; } + top_right1 "widget/pane" { x=890; y=100; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#aa0"; } + bottom_left1 "widget/pane" { x=100; y=890; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#0a0"; } + bottom_right1 "widget/pane" { x=890; y=890; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#00a"; } + + // Interior markers. + top_left2 "widget/pane" { x=250; y=250; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#700"; } + top_right2 "widget/pane" { x=740; y=250; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#770"; } + bottom_left2 "widget/pane" { x=250; y=740; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#070"; } + bottom_right2 "widget/pane" { x=740; y=740; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#007"; } + + // Deep interior markers. + top_left3 "widget/pane" { x=400; y=400; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#500"; } + top_right3 "widget/pane" { x=590; y=400; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#550"; } + bottom_left3 "widget/pane" { x=400; y=590; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#050"; } + bottom_right3 "widget/pane" { x=590; y=590; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#005"; } + + // Center marker. + center "widget/pane" + { + x=450; y=450; width=100; height=100; bgcolor = "orange"; + centerer "widget/pane" { x=25; y=25; width=50; height=50; bgcolor = "purple"; } // Debug + } + } \ No newline at end of file diff --git a/centrallix-os/samples/autoscale_test2.app b/centrallix-os/samples/autoscale_test2.app new file mode 100644 index 000000000..10b7a4cff --- /dev/null +++ b/centrallix-os/samples/autoscale_test2.app @@ -0,0 +1,19 @@ +$Version=2$ +MyPage "widget/page" + { + title = "Flex Testing Page"; + bgcolor = "black"; + textcolor = "#00f8ff"; + width = 1000; + height = 1000; + + box "widget/pane" + { + x=25; y=25; width=975; height=975; bgcolor = "#111"; + + // Note: fl_x and fl_y seem to be ignored. + standard "widget/pane" { x=100; y=100; width=100; height=100; fl_x=100; fl_y=100; fl_width=100; fl_height=100; bgcolor = "orange"; } + big "widget/pane" { x=250; y=250; width=200; height=200; fl_x=10; fl_y=50; fl_width=10; fl_height=10; bgcolor = "purple"; } + double "widget/pane" { x=500; y=500; width=200; height=200; fl_x=50; fl_y=10; fl_width=50; fl_height=50; bgcolor = "green"; } + } + } \ No newline at end of file From c5d998c7b98cfe3ee9a3502869f367e36b415888 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 23 Jul 2025 14:21:00 -0600 Subject: [PATCH 017/107] Sample pages appear to work now. --- centrallix-os/sys/js/ht_geom_dom1html.js | 42 ++++++++++++++++++- centrallix-os/sys/js/htdrv_html.js | 15 ++++++- centrallix-os/sys/js/htdrv_scrollpane.js | 44 ++++++++++++++------ centrallix-os/sys/js/htdrv_textbutton.js | 2 - centrallix/htmlgen/ht_render.c | 37 +++++++++++++++-- centrallix/htmlgen/htdrv_autolayout.c | 8 ++-- centrallix/htmlgen/htdrv_editbox.c | 12 +++--- centrallix/htmlgen/htdrv_html.c | 24 +++++------ centrallix/htmlgen/htdrv_image.c | 8 ++-- centrallix/htmlgen/htdrv_label.c | 10 ++--- centrallix/htmlgen/htdrv_pane.c | 8 ++-- centrallix/htmlgen/htdrv_scrollpane.c | 42 +++++++++---------- centrallix/htmlgen/htdrv_textbutton.c | 50 +++++++++++++++++------ centrallix/htmlgen/htdrv_treeview.c | 14 +++---- centrallix/include/ht_render.h | 52 ++++++++++++++++-------- centrallix/wgtr/apos.c | 24 ++++++----- 16 files changed, 267 insertions(+), 125 deletions(-) diff --git a/centrallix-os/sys/js/ht_geom_dom1html.js b/centrallix-os/sys/js/ht_geom_dom1html.js index b8c17ab7d..f137d5dba 100644 --- a/centrallix-os/sys/js/ht_geom_dom1html.js +++ b/centrallix-os/sys/js/ht_geom_dom1html.js @@ -11,6 +11,35 @@ // Cross browser Geometry DOM1HTML +/*** Experimental system for turning off clipping CSS. + *** The clip values are still stored and can be queried + *** for legacy compatibility, but they will not output + *** any clip rectangles or clip paths in the CSS or HTML. + ***/ +/** Ensure clipping is disabled for a layer / HTML node. **/ +function disableClippingCSS(l) { + console.log(`Turning off clipping for ${l.clip.obj.id}.`); + l.clip.noclip = true; + updateClippingCSS(l); +} +/** Ensure clipping is enabled for a layer / HTML node. **/ +function enableClippingCSS(l) { + l.clip.noclip = false; + updateClippingCSS(l); +} +/** Update clipping without changing any specific values. **/ +function updateClippingCSS(l) { + setClipTop(l, getClipTop(l)); +} + +/** Debug function for finding clipped dom nodes. **/ +function getClipped() { + return Array + .from(Window.clipped) + .filter(id=>id) + .map(id=>document.getElementById(id)); +} + // Clip Width function getClipWidth(l) { @@ -321,6 +350,7 @@ function getHeight(l) // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. +Window.clipped = new Set(); // Debug set for saving clipped dom nodes. function ClipObject_SetAll(top,right,bottom,left) { var str = "rect(" @@ -329,7 +359,17 @@ function ClipObject_SetAll(top,right,bottom,left) + bottom + "px, " + left + "px)"; this.arr = {1:top,2:right,3:bottom,4:left}; - this.obj.style.setProperty('clip',str,""); + if (this.noclip) + { + Window.clipped.delete(this.obj.id); // debug + this.obj.style.setProperty('clip', ""); + } + else + { + Window.clipped.add(this.obj.id); // debug + this.obj.style.setProperty('clip', str, ""); + } + } var ClipRegexp = /rect\((.*), (.*), (.*), (.*)\)/; diff --git a/centrallix-os/sys/js/htdrv_html.js b/centrallix-os/sys/js/htdrv_html.js index b40264bec..3289d0b61 100644 --- a/centrallix-os/sys/js/htdrv_html.js +++ b/centrallix-os/sys/js/htdrv_html.js @@ -240,7 +240,10 @@ function ht_init(param) { setClipHeight(l, getdocHeight(l)); } - pg_set_style(l, 'height', getdocHeight(l)); + /*** This break's responsive design, and it looks like it's + *** put in for compatibility and is no longer necessary. + ***/ + // pg_set_style(l, 'height', getdocHeight(l)); if (param.width != -1) { setClipWidth(l, param.width); @@ -250,7 +253,15 @@ function ht_init(param) { setClipWidth(l, getdocWidth(l)); } - pg_set_style(l, 'width', getdocWidth(l)); + /*** This break's responsive design, and it looks like it's + *** put in for compatibility and is no longer necessary. + ***/ + // pg_set_style(l, 'width', getdocWidth(l)); + + /** Clipping breaks responsive pages and is not required in modern browers. **/ + disableClippingCSS(l); + disableClippingCSS(l2); + if (source.substr(0,5) == 'http:') { //pg_serialized_load(l, source, ht_reloaded); diff --git a/centrallix-os/sys/js/htdrv_scrollpane.js b/centrallix-os/sys/js/htdrv_scrollpane.js index f57d1313f..ccbf0dba5 100644 --- a/centrallix-os/sys/js/htdrv_scrollpane.js +++ b/centrallix-os/sys/js/htdrv_scrollpane.js @@ -58,6 +58,7 @@ function sp_init(param) images[0].area=alayer; images[0].pane=l; setClipWidth(alayer, getClipWidth(l)-18); + disableClippingCSS(alayer); // Clipping breaks responsive pages and is not required in modern browers. alayer.maxwidth=getClipWidth(alayer); alayer.minwidth=getClipWidth(alayer); tlayer.nofocus = true; @@ -96,10 +97,27 @@ function sp_init(param) } } +/** @returns The height of the scrollpane content (including content outside the visibile area). **/ +// Replaces getClipHeight(area) + getClipTop(area) +function sp_get_content_height(area) { + return getClipHeight(area) + getClipTop(area); +} + +/** @returns The height of visible area in the scrollpane. **/ +// Replaces getClipHeight(pane) +function sp_get_total_height(pane) { + return parseInt(window.getComputedStyle(pane).height); +} + +/** @returns The height of visible area in the scroll bar. **/ +function sp_get_sb_height(pane) { + return sp_get_total_height(pane) - (3*18); +} + function sp_action_scrollto(aparam) { - var h = getClipHeight(this.area)+getClipTop(this.area); // height of content - var ch = getClipHeight(this); + var h = sp_get_content_height(this.area); // height of content + var ch = sp_get_total_height(this); var d = h-ch; // height of non-visible content (max scrollable distance) if (d < 0) d=0; if (typeof aparam.Percent != 'undefined') @@ -135,8 +153,10 @@ function sp_WatchHeight(property, oldvalue, newvalue) // make sure region not offscreen now newvalue += getClipTop(this.pane.area); - if (getRelativeY(this.pane.area) + newvalue < getClipHeight(this.pane)) setRelativeY(this.pane.area, getClipHeight(this.pane) - newvalue); - if (newvalue < getClipHeight(this.pane)) setRelativeY(this.pane.area, 0); + if (getRelativeY(this.pane.area) + newvalue < sp_get_total_height(this.pane)) { + setRelativeY(this.pane.area, sp_get_total_height(this.pane) - newvalue); + } + if (newvalue < sp_get_total_height(this.pane)) setRelativeY(this.pane.area, 0); this.pane.UpdateThumb(newvalue); newvalue -= getClipTop(this.pane.area); this.bottom = this.top + newvalue; /* ns seems to unlink bottom = top + height if you modify clip obj */ @@ -148,10 +168,10 @@ function sp_UpdateThumb(h) /** 'this' is a spXpane **/ if(!h) { /** if h is supplied, it is the soon-to-be clip.height of the spXarea **/ - h=getClipHeight(this.area)+getClipTop(this.area); // height of content + h=sp_get_content_height(this.area); // height of content } - var d=h-getClipHeight(this); // height of non-visible content (max scrollable distance) - var v=getClipHeight(this)-(3*18); + var d=h-sp_get_total_height(this); // height of non-visible content (max scrollable distance) + var v=sp_get_sb_height(this); if(d<=0) setRelativeY(this.thum, 18); else @@ -167,8 +187,8 @@ function do_mv() { return; } - var h=getClipHeight(ti.area)+getClipTop(ti.area); // height of content - var d=h-getClipHeight(ti.pane); // height of non-visible content (max scrollable distance) + var h=sp_get_content_height(ti.area); // height of content + var d=h-sp_get_total_height(ti.pane); // height of non-visible content (max scrollable distance) var incr=sp_mv_incr; if(d<0) incr=0; @@ -253,13 +273,13 @@ function sp_mousemove(e) var ti=sp_target_img; if (ti != null && ti.kind=='sp' && ti.name=='t') { - var v=getClipHeight(ti.pane)-(3*18); + var v=sp_get_sb_height(ti.pane); var new_y=sp_thum_y + (e.pageY-sp_click_y); if (new_y > getPageY(ti.pane)+18+v) new_y=getPageY(ti.pane)+18+v; if (new_y < getPageY(ti.pane)+18) new_y=getPageY(ti.pane)+18; setPageY(ti.thum,new_y); - var h=getClipHeight(ti.area)+getClipTop(ti.area); - var d=h-getClipHeight(ti.pane); + var h=sp_get_content_height(ti.area); + var d=h-sp_get_total_height(ti.pane); if (d<0) d=0; var yincr = (((getRelativeY(ti.thum)-18)/v)*-d) - getRelativeY(ti.area); moveBy(ti.area, 0, yincr); diff --git a/centrallix-os/sys/js/htdrv_textbutton.js b/centrallix-os/sys/js/htdrv_textbutton.js index 77b968e75..d053453b1 100644 --- a/centrallix-os/sys/js/htdrv_textbutton.js +++ b/centrallix-os/sys/js/htdrv_textbutton.js @@ -226,7 +226,6 @@ function tb_setmode(layer,mode) break; case 1: /* point, but no click */ - moveTo(layer,layer.orig_x,layer.orig_y); $(layer).find(".cell").css({'border-style':wgtrGetServerProperty(layer, 'border_style', 'outset'), 'border-color':wgtrGetServerProperty(layer, 'border_color', '#c0c0c0')}); /*if(cx__capabilities.Dom2CSS) { @@ -259,7 +258,6 @@ function tb_setmode(layer,mode) break; case 2: /* point and click */ - moveTo(layer,layer.orig_x+1,layer.orig_y+1); var bstyle = wgtrGetServerProperty(layer, 'border_style', 'outset'); if (bstyle == 'outset') bstyle = 'inset'; diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 0a6844768..c98f81b14 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -2720,10 +2720,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - ht_flex(x, node->Parent->width, ht_get_fl_x(node)), - ht_flex(y, node->Parent->height, ht_get_fl_y(node)), - (w > 0), ht_flex(w, node->Parent->width, ht_get_fl_w(node)), - (h > 0), ht_flex(h, node->Parent->height, ht_get_fl_h(node)), + ht_flex(x, ht_get_total_w(node), ht_get_fl_x(node)), + ht_flex(y, ht_get_total_h(node), ht_get_fl_y(node)), + (w > 0), ht_flex(w, ht_get_total_w(node), ht_get_fl_w(node)), + (h > 0), ht_flex(h, ht_get_total_h(node), ht_get_fl_h(node)), (z > 0), z, (*textcolor), textcolor, (!strcmp(style, "bold")), @@ -2746,3 +2746,32 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y } +int +ht_get_total_w__INTERNAL(pWgtrNode widget) { + pWgtrNode parent = widget->Parent; + int parentWidth = parent->width; +// printf( +// "Getting total width available to '%s' (%s), child of '%s' (%s) - %dpx\n", +// widget->Name, widget->Type, parent->Name, parent->Type, parentWidth +// ); + if (parentWidth >= 0) { + int offset = parent->left + parent->right, ret = parentWidth - offset; + // printf("Returning %d-%d=%d\n", parentWidth, offset, ret); + return ret; + } else return ht_get_total_w(parent); // Tail-recursion +} + +int +ht_get_total_h__INTERNAL(pWgtrNode widget) { + pWgtrNode parent = widget->Parent; + int parentHeight = parent->height; +// printf( +// "Getting total height available to '%s' (%s), child of '%s' (%s) - %dpx\n", +// widget->Name, widget->Type, parent->Name, parent->Type, parentHeight +// ); + if (parentHeight >= 0) { + int offset = parent->top + parent->bottom, ret = parentHeight - offset; + // printf("Returning %d-%d=%d\n", parentHeight, offset, ret); + return ret; + } else return ht_get_total_h(parent); // Tail-recursion +} diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index 66b7f83e0..9cc7eb7c4 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -111,10 +111,10 @@ htalRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), z ); diff --git a/centrallix/htmlgen/htdrv_editbox.c b/centrallix/htmlgen/htdrv_editbox.c index 7290692a7..ef40c8d8a 100644 --- a/centrallix/htmlgen/htdrv_editbox.c +++ b/centrallix/htmlgen/htdrv_editbox.c @@ -160,9 +160,9 @@ htebRender(pHtSession s, pWgtrNode tree, int z) "overflow:hidden; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w - 2 * box_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w - 2 * box_offset, ht_get_total_w(tree), ht_get_fl_w(tree)), z ); htrAddStylesheetItem_va(s, @@ -175,7 +175,7 @@ htebRender(pHtSession s, pWgtrNode tree, int z) "border:none; " "}\n", id, - ht_flex(w - 10, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(w - 10, ht_get_total_w(tree), ht_get_fl_w(tree)), z+1 ); @@ -217,8 +217,8 @@ htebRender(pHtSession s, pWgtrNode tree, int z) htrAddStylesheetItem_va(s, "\t#eb%POSbase { height:"ht_flex_format"; }\n" "\t#eb%POScon1 { height:"ht_flex_format"; }\n", - id, ht_flex(h - 2 * box_offset, tree->Parent->height, ht_get_fl_h(tree)), - id, ht_flex(h - 2 * box_offset - 2, tree->Parent->height, ht_get_fl_h(tree)) + id, ht_flex(h - 2 * box_offset, ht_get_total_h(tree), ht_get_fl_h(tree)), + id, ht_flex(h - 2 * box_offset - 2, ht_get_total_h(tree), ht_get_fl_h(tree)) ); //htrAddBodyItem_va(s, "
 
\n", w-2, h-2); diff --git a/centrallix/htmlgen/htdrv_html.c b/centrallix/htmlgen/htdrv_html.c index 3e7238209..0b41d7b35 100644 --- a/centrallix/htmlgen/htdrv_html.c +++ b/centrallix/htmlgen/htdrv_html.c @@ -102,9 +102,9 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) /** Only give x and y if supplied. **/ if (x < 0 || y < 0) { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+1); + htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z); + htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z); + htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z+1); } else { @@ -118,9 +118,9 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), z ); htrAddStylesheetItem_va(s, @@ -133,9 +133,9 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), z ); htrAddStylesheetItem_va(s, @@ -148,9 +148,9 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), z+1 ); } diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index b7195771b..7ad156f71 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -155,10 +155,10 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) "text-align:center; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), z ); diff --git a/centrallix/htmlgen/htdrv_label.c b/centrallix/htmlgen/htdrv_label.c index 27f53c9d2..ea315a9a5 100644 --- a/centrallix/htmlgen/htdrv_label.c +++ b/centrallix/htmlgen/htdrv_label.c @@ -217,10 +217,10 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) "%[font-style:italic; %]" "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), - (!auto_height), ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + (!auto_height), ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), z, (is_bold), (*fgcolor), fgcolor, @@ -249,7 +249,7 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) id, align, !strcmp(valign, "middle"), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)) + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)) ); htrAddWgtrObjLinkage_va(s, tree, "lbl%POS",id); diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 9d0f23416..0caf96258 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -197,10 +197,10 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "%STR" "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w + offset, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h + offset, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w + offset, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h + offset, ht_get_total_h(tree), ht_get_fl_h(tree)), z, border_radius, main_bg diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index 3ceafbd4d..e5c061d16 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -126,16 +126,15 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "TOP:"ht_flex_format"; " "WIDTH:"ht_flex_format"; " "HEIGHT:"ht_flex_format"; " - "clip:rect(0px, "ht_flex_format", "ht_flex_format", 0px); " + "OVERFLOW: clip; " "Z-INDEX:%POS; " "}\n", id, (visible) ? "inherit" : "hidden", - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), z ); htrAddStylesheetItem_va(s, @@ -148,7 +147,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(w - 18, ht_get_total_w(tree), 1.0), z + 1 ); htrAddStylesheetItem_va(s, @@ -161,7 +160,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(w - 18, tree->width, 1.0), z + 1 ); } @@ -207,17 +206,16 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "TOP:"ht_flex_format"; " "WIDTH:"ht_flex_format"; " "HEIGHT:"ht_flex_format"; " - "clip:rect(0, "ht_flex_format", "ht_flex_format", 0); " + "OVERFLOW: clip; " "Z-INDEX:%POS; " "\"" ">\n", id, (visible) ? "inherit" : "hidden", - ht_flex(x, tree->Parent->width, ht_fl_x_compat), - ht_flex(y, tree->Parent->height, ht_fl_y_compat), - ht_flex(w, tree->Parent->width, tree->fl_width), - ht_flex(h, tree->Parent->height, tree->fl_height), - ht_flex(w, tree->Parent->width, tree->fl_width), ht_flex(h, tree->Parent->height, tree->fl_height), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), z ); htrAddBodyItem_va(s,"", id); @@ -230,7 +228,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "TOP:0px; " "}\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)) + ht_flex(w - 18, tree->width, 1.0) ); htrAddStylesheetItem_va(s, "\t#sp%POSbar { " @@ -241,8 +239,8 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "HEIGHT:"ht_flex_format"; " "}\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(h - 36, tree->Parent->height, ht_get_fl_h(tree)) + ht_flex(w - 18, tree->width, 1.0), + ht_flex(h - 36, tree->height, 1.0) ); htrAddStylesheetItem_va(s, "\t#sp%POSdown { " @@ -251,8 +249,8 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "TOP:"ht_flex_format"; " "}\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(h - 18, tree->Parent->height, ht_get_fl_y(tree)) + ht_flex(w - 18, tree->width, 1.0), + ht_flex(h - 18, tree->height, 1.0) ); htrAddBodyItem_va(s, "
" "
\n", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_x(tree)), + ht_flex(w - 18, tree->width, 1.0), z + 1 ); htrAddBodyItem_va(s, @@ -286,8 +284,8 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "\"" ">", id, - ht_flex(w - 18, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(w - 18, ht_get_total_w(tree), 1.0), + ht_flex(h, ht_get_total_h(tree), 1.0), z + 1 ); } diff --git a/centrallix/htmlgen/htdrv_textbutton.c b/centrallix/htmlgen/htdrv_textbutton.c index 1f6c1afef..92c07357b 100644 --- a/centrallix/htmlgen/htdrv_textbutton.c +++ b/centrallix/htmlgen/htdrv_textbutton.c @@ -219,20 +219,46 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) "display:table; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), - (h>=0), ht_flex(h-1-2*box_offset, tree->Parent->height, ht_get_fl_h(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w-1-2*box_offset, ht_get_total_w(tree), ht_get_fl_w(tree)), + (h>=0), ht_flex(h-1-2*box_offset, ht_get_total_h(tree), ht_get_fl_h(tree)), z ); - htrAddStylesheetItem_va(s, "\t#tb%POSpane .cell { height:100%%; width:100%%; vertical-align:middle; display:table-cell; padding:1px; font-weight:bold; cursor:default; text-align:%STR; border-width:1px; border-style:%STR&CSSVAL; border-color:%STR&CSSVAL; border-radius:%INTpx; color:%STR&CSSVAL; %[text-shadow:1px 1px %STR&CSSVAL; %]%STR }\n", - /* clipping no longer needed: 0, w-1-2*box_offset+2*clip_offset, h-1-2*box_offset+2*clip_offset, 0, */ - id, - h_align, - border_style, border_color, border_radius, - is_enabled?fgcolor1:disable_color, is_enabled, fgcolor2, - bgstyle - ); + // CSS button click animation (replaces manual the JS implementation). + if (is_enabled) { + htrAddStylesheetItem_va(s, + "\t#tb%POSpane:active { transform: translate(1px, 1px); }\n", + id + ); + } + htrAddStylesheetItem_va(s, + "\t#tb%POSpane .cell { " + "height:100%%; " + "width:100%%; " + "vertical-align:middle; " + "display:table-cell; " + "padding:1px; " + "font-weight:bold; " + "cursor:default; " + "text-align:%STR; " + "border-width:1px; " + "border-style:%STR&CSSVAL; " + "border-color:%STR&CSSVAL; " + "border-radius:%INTpx; " + "color:%STR&CSSVAL; " + "%[text-shadow:1px 1px %STR&CSSVAL; %]" + "%STR " + "}\n", + id, + h_align, + border_style, + border_color, + border_radius, + (is_enabled) ? fgcolor1 : disable_color, + (is_enabled), fgcolor2, + bgstyle + ); /** CSS for image on the button **/ if (image[0] && (image_width || image_height || image_margin)) diff --git a/centrallix/htmlgen/htdrv_treeview.c b/centrallix/htmlgen/htdrv_treeview.c index 106b49718..05a2bae8f 100644 --- a/centrallix/htmlgen/htdrv_treeview.c +++ b/centrallix/htmlgen/htdrv_treeview.c @@ -183,13 +183,13 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) "}\n", id, (show_root) ? "inherit" : "hidden", - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), z ); } - htrAddStylesheetItem_va(s,"\t#tv%POSload { POSITION:absolute; VISIBILITY:hidden; OVERFLOW:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; clip:rect(0px,0px,0px,0px); Z-INDEX:0; }\n",id); + htrAddStylesheetItem_va(s,"\t#tv%POSload { POSITION:absolute; VISIBILITY:hidden; OVERFLOW:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; clip-path:inset(0px 0px 0px 0px); Z-INDEX:0; }\n",id); htrAddStylesheetItem_va(s,"\tdiv.tv%POS a { %[color:%STR&CSSVAL;%] }\n", id, *fgcolor, fgcolor); htrAddStylesheetItem_va(s,"\tdiv.tv%POSh a { %[color:%STR&CSSVAL;%] }\n", id, *hfgcolor, hfgcolor); @@ -239,9 +239,9 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) id, /** Class **/ id, /** ID **/ (show_root) ? "inherit" : "hidden", - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), z, (*icon) ? icon : "/sys/images/ico02b.gif", src ); diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index d078ba1e0..b725eb9d4 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -366,17 +366,25 @@ int htruleRegister(char* ruletype, ...); /** Define macros for implementing responsive dimensions. **/ /** ===================================================== **/ -/*** The widget fl_x and fl_y fields are never used in the generated layout. - *** Thus, the CSS I created to add responsiveness uses the values below - *** instead of the provided value to preserve backwards compatibility. +/*** Brief explanation of the responsiveness formula. + *** + *** Responsive dimensions in widgets use the following formula: + *** Original px + (100% - Total px) * Flex + *** Where "Original" is the original size of the object in an adaptive layout, + *** "Total" is the total size of the widget's parent container, and + *** "Flex" is the widget flexibility (where all flexibilities add to 1). + *** All, with respect to the given dimension. + *** + *** The intuition behind this formula is that (100% - Total px) is 0px + *** if the parent container is the size intended by the adaptive design. + *** However, if the user resizes the window, (100% - Total px) is the + *** difference between the size in the adaptive design and the current + *** size, so the widget changes size with respect to that difference. ***/ -/** @brief widget->fl_x is never used. Use this instead for compatibility. **/ -#define ht_fl_x_compat 1.0 -/** @brief widget->fl_y is never used. Use this instead for compatibility. **/ -#define ht_fl_y_compat 1.0 /** @brief The qprintf format to specify a responsive dimension. **/ -#define ht_flex_format "calc(%INTpx + (%DBL%% - %INTpx) * %DBL)" +#define ht_flex_format "calc(%INTpx + (100%% - %INTpx) * %DBL)" + /*** @brief The function which generates the values that should be passed to *** qprintf in order to satisfy an ht_flex_format. *** @@ -386,29 +394,44 @@ int htruleRegister(char* ruletype, ...); *** to generate this with an ht_get_fl function call. *** @returns Several values to serve as parameters for a qprintf call. ***/ -#define ht_flex(size, total, flex) (size), (double)(size) / (total) * 100.0, (size), (flex) +#define ht_flex(size, total, flex) (size), (total), (flex) + /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the x direction. ***/ -#define ht_get_fl_x(widget) (ht_fl_x_compat) +#define ht_get_fl_x(widget) ((widget)->xAdjWeight) + /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the y direction. ***/ -#define ht_get_fl_y(widget) (ht_fl_y_compat) +#define ht_get_fl_y(widget) ((widget)->yAdjWeight) + /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the width direction. ***/ #define ht_get_fl_w(widget) ((widget)->wAdjWeight) + /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the height direction. ***/ #define ht_get_fl_h(widget) ((widget)->hAdjWeight) + +int ht_get_total_w__INTERNAL(pWgtrNode widget); +int ht_get_total_h__INTERNAL(pWgtrNode widget); + +#define ht_get_total_w(widget) ht_get_total_w__INTERNAL(widget) +#define ht_get_total_h(widget) ht_get_total_h__INTERNAL(widget) + +// #define ht_get_total_w(widget) ((widget)->Parent->width - (widget)->Parent->left - (widget)->Parent->right) +// #define ht_get_total_h(widget) ((widget)->Parent->height - (widget)->Parent->top - (widget)->Parent->bottom) + /*** @brief A shortcut function to get the flexibility when writing the *** LEFT CSS attribute. *** @param widget The widget to be queried. *** @returns The flexibility of the widget in the left direction. ***/ #define ht_get_fl_l ht_get_fl_x + /*** @brief A shortcut function to get the flexibility when writing the *** TOP CSS attribute. *** @param widget The widget to be queried. @@ -416,12 +439,5 @@ int htruleRegister(char* ruletype, ...); ***/ #define ht_get_fl_t ht_get_fl_y -/*** Alternate formula suggested in the GitHub Issue. I think this should work - *** and be more efficient, but for some reason it doesn't at all, and I can't - *** understand why. I probably just implemented it wrong. - ***/ -// #define ht_flex_format "calc(%INTpx + (100%% - %INTpx) * %DBL)" -// #define ht_flex(size, total, flex) (size), (total), (double)(flex) / 100.0 - #endif /* _HT_RENDER_H */ diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 619736703..f51d508b1 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -222,7 +222,7 @@ int i=0, count=0; ((pWgtrNode)xaGetItem(&PatchedWidgets, i))->pre_height = -1; } - /** Free the PatchedWidgets XArray.**/ + /** Free the PatchedWidgets XArray. **/ xaDeInit(&PatchedWidgets); return 0; @@ -1282,9 +1282,9 @@ aposIsSpacer(pAposLine StartL, pAposLine EndL, int type, int isBorder) { pWgtrNode SW, EW; int i=0, j=0; -/** @brief The number of widgets starting at the end of this section.**/ +/** @brief The number of widgets starting at the end of this section. **/ int sCount=xaCount(&(EndL->SWidgets)); -/** @brief The number of widgets ending at the start of this section.**/ +/** @brief The number of widgets ending at the start of this section. **/ int eCount=xaCount(&(StartL->EWidgets)); if((EndL->Loc - StartL->Loc) <= APOS_MINSPACE) // If section is sufficiently narrow. @@ -1488,7 +1488,7 @@ float TotalSum=0; TotalSum += (FlexWeight * SizeWeight); } - /** The initial borders do not adjust.**/ + /** The initial borders do not adjust. **/ pAposLine leftBorder = (pAposLine)xaGetItem(Lines, 0); leftBorder->LocAdjWeight = leftBorder->MyAdjWeight = 0.0f; @@ -1506,26 +1506,30 @@ float TotalSum=0; ***/ float AdjWeight = PrevSect->AdjWeight = (float)(FlexWeight*SizeWeight)/TotalSum; - /** Store the line adjustment weight for responsive CSS later.**/ + /** Store the line adjustment weight for responsive CSS later. **/ CurrLine->LocAdjWeight = PrevLine->LocAdjWeight + AdjWeight; CurrLine->MyAdjWeight = AdjWeight; - /**for expanding lines**/ + /** Expand lines. **/ if(Diff > 0) { /** Calculate adjustment using the adjustment weight. **/ Adj = (float)(Diff) * AdjWeight + APOS_FUDGEFACTOR; - /** Apply the calculated adjustment.**/ + // printf("Expanding lines by %d*%f=%d\n", Diff, AdjWeight, Adj); + + /** Apply the calculated adjustment. **/ PrevSect->Width += Adj; CurrLine->Loc = PrevLine->Loc + PrevSect->Width; } - /**for contracting lines**/ + /** Contract lines. **/ else if(Diff < 0) { /** Calculate adjustment using the adjustment weight. **/ Adj = (float)(Diff) * AdjWeight - APOS_FUDGEFACTOR; + // printf("Contracting lines by %d*%f=%d\n", Diff, AdjWeight, Adj); + /** if the section width will be unacceptably *** narrow or negative after the adjustment **/ if((Adj + PrevSect->Width) <= APOS_MINSPACE) @@ -1608,7 +1612,7 @@ pWgtrNode Widget; aposSetOffsetBools(Widget, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); if(flag==APOS_ROW && Widget->fl_height) { - /** Calculate the new size, taking APOS_MINWIDTH into account.**/ + /** Calculate the new size, taking APOS_MINWIDTH into account. **/ newsize = CurrLine->Loc - Widget->y - isTopTab*24; if (newsize < APOS_MINWIDTH && Widget->pre_height >= APOS_MINWIDTH) Widget->height = APOS_MINWIDTH; @@ -1652,7 +1656,7 @@ pWgtrNode Widget; /** Adjusts width or height of widgets ending on this line. **/ count = xaCount(&(CurrLine->CWidgets)); - printf("Doing %d widgets.\n", count); + // printf("Doing %d widgets.\n", count); for(j=0; jCWidgets), j); From 5ae7c542ed6a221f2854381145ea33e3f6e404f7 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 30 Jul 2025 16:11:58 -0600 Subject: [PATCH 018/107] Working, server-side code. Fixes components, dropdowns, and images. Renames flex variables to be more clear. Send parent_w and parent_h to client. --- centrallix-os/sys/js/htdrv_pane.js | 4 ++ centrallix/htmlgen/ht_render.c | 94 ++++++++++++++++++++++------ centrallix/htmlgen/htdrv_component.c | 28 ++++++++- centrallix/htmlgen/htdrv_dropdown.c | 56 ++++++++++++++++- centrallix/htmlgen/htdrv_image.c | 33 ++++++++-- centrallix/htmlgen/htdrv_pane.c | 4 +- centrallix/include/apos.h | 24 +++---- centrallix/include/ht_render.h | 55 ++++++++++++---- centrallix/include/wgtr.h | 11 ++-- centrallix/wgtr/apos.c | 58 +++++++++-------- centrallix/wgtr/wgtr.c | 26 +++++--- 11 files changed, 293 insertions(+), 100 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_pane.js b/centrallix-os/sys/js/htdrv_pane.js index dfdb597ad..9489912e7 100644 --- a/centrallix-os/sys/js/htdrv_pane.js +++ b/centrallix-os/sys/js/htdrv_pane.js @@ -79,6 +79,10 @@ function pn_setbackground(aparam) function pn_action_resize(aparam) { + // I think this code is a better implementation of the following lines: + // var w = (aparam.Width) ?? pg_get_style(this, 'width'); + // var h = (aparam.Height) ?? pg_get_style(this, 'height'); + var w = aparam.Width?aparam.Width:pg_get_style(this, 'width'); var h = aparam.Height?aparam.Height:pg_get_style(this, 'height'); resizeTo(this, w, h); diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index c98f81b14..647374a67 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1500,10 +1500,12 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) htr_internal_WriteWgtrProperty(s, tree, "fl_y"); htr_internal_WriteWgtrProperty(s, tree, "fl_width"); htr_internal_WriteWgtrProperty(s, tree, "fl_height"); - htr_internal_WriteWgtrProperty(s, tree, "adj_weight_x"); - htr_internal_WriteWgtrProperty(s, tree, "adj_weight_y"); - htr_internal_WriteWgtrProperty(s, tree, "adj_weight_w"); - htr_internal_WriteWgtrProperty(s, tree, "adj_weight_h"); + htr_internal_WriteWgtrProperty(s, tree, "total_fl_x"); + htr_internal_WriteWgtrProperty(s, tree, "total_fl_y"); + htr_internal_WriteWgtrProperty(s, tree, "total_fl_w"); + htr_internal_WriteWgtrProperty(s, tree, "total_fl_h"); + htr_internal_WriteWgtrProperty(s, tree, "parent_w"); + htr_internal_WriteWgtrProperty(s, tree, "parent_h"); } propname = wgtrFirstPropertyName(tree); while(propname) @@ -2748,30 +2750,82 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y int ht_get_total_w__INTERNAL(pWgtrNode widget) { + /** Check to see if the value was already cached by a previous call. **/ + int cached_value = widget->parent_w; + if (cached_value != -1) { +// printf( +// "Got total width available to '%s' (%s) from cache: %dpx\n", +// widget->Name, widget->Type, cached_value +// ); + return cached_value; + } + + // DEBUG + if (widget->Parent == NULL) { + printf("\nPANIC: Call to ht_get_total_w__INTERNAL() on widget with no parent!\n\n"); + wgtrPrint(widget, 1); + } + + /** Cache miss, we'll need to traverse to the parent and find its width manually. */ pWgtrNode parent = widget->Parent; int parentWidth = parent->width; -// printf( -// "Getting total width available to '%s' (%s), child of '%s' (%s) - %dpx\n", -// widget->Name, widget->Type, parent->Name, parent->Type, parentWidth -// ); - if (parentWidth >= 0) { +// int isParentVisual = !(parent->Flags & WGTR_F_NONVISUAL); +// printf( +// "Getting total width available to '%s' (%s), child of '%s' (%s) - %dpx %d\n", +// widget->Name, widget->Type, parent->Name, parent->Type, parentWidth, isParentVisual +// ); + + /** Check if the parent has a width value. **/ + if (parentWidth >= 0 /* && isParentVisual */) { int offset = parent->left + parent->right, ret = parentWidth - offset; - // printf("Returning %d-%d=%d\n", parentWidth, offset, ret); - return ret; - } else return ht_get_total_w(parent); // Tail-recursion + printf("Returning %d-%d=%d\n", parentWidth, offset, ret); + return (widget->parent_w = ret); + } else { + if (parent->Parent == NULL) { + printf("Recursive call would segfault! Guessing %dpx instead.\n", parentWidth); + return (widget->parent_w = parentWidth); + } + return (widget->parent_w = ht_get_total_w(parent)); + } } int ht_get_total_h__INTERNAL(pWgtrNode widget) { + /** Check to see if the value was already cached by a previous call. **/ + int cached_value = widget->parent_h; + if (cached_value != -1) { +// printf( +// "Got total height available to '%s' (%s) from cache: %dpx\n", +// widget->Name, widget->Type, cached_value +// ); + return cached_value; + } + + // DEBUG + if (widget->Parent == NULL) { + printf("\nPANIC: Call to ht_get_total_h__INTERNAL() on widget with no parent!\n\n"); + wgtrPrint(widget, 1); + } + + /** Cache miss, we'll need to traverse to the parent and find its height manually. */ pWgtrNode parent = widget->Parent; int parentHeight = parent->height; -// printf( -// "Getting total height available to '%s' (%s), child of '%s' (%s) - %dpx\n", -// widget->Name, widget->Type, parent->Name, parent->Type, parentHeight -// ); - if (parentHeight >= 0) { +// int isParentVisual = !(parent->Flags & WGTR_F_NONVISUAL); +// printf( +// "Getting total height available to '%s' (%s), child of '%s' (%s) - %dpx %d\n", +// widget->Name, widget->Type, parent->Name, parent->Type, parentHeight, isParentVisual +// ); + + /** Check if the parent has a height value. **/ + if (parentHeight >= 0 /* && isParentVisual */) { int offset = parent->top + parent->bottom, ret = parentHeight - offset; - // printf("Returning %d-%d=%d\n", parentHeight, offset, ret); - return ret; - } else return ht_get_total_h(parent); // Tail-recursion + printf("Returning %d-%d=%d\n", parentHeight, offset, ret); + return (widget->parent_h = ret); + } else { + if (parent->Parent == NULL) { + printf("Recursive call would segfault! Guessing %dpx instead.\n", parentHeight); + return (widget->parent_h = parentHeight); + } + return (widget->parent_h = ht_get_total_h__INTERNAL(parent)); + } } diff --git a/centrallix/htmlgen/htdrv_component.c b/centrallix/htmlgen/htdrv_component.c index 93c5533a5..14988d1a4 100644 --- a/centrallix/htmlgen/htdrv_component.c +++ b/centrallix/htmlgen/htdrv_component.c @@ -353,8 +353,31 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) mssError(0,"HTCMP","Invalid component for widget '%s'",name); goto out; } - wgtrMoveChildren(cmp_tree, x, y); + /*** Adjusting children is no longer necessary because + *** the component is placed inside an isolating div. + *** Thus, the children's location within this div will + *** be correct without adjustment. + ***/ + // wgtrMoveChildren(cmp_tree, x, y); + + /** Style enclosing div. **/ + htrAddStylesheetItem_va(s, + "\t#cmp%POSbase { " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "OVERFLOW:hidden; " + ht_flex_format_all + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex_all(x, y, w, h, tree), + z + ); + + /** Enclosing div to isolate children. **/ + htrAddBodyItem_va(s,"
\n", id); + /** Check param references **/ htcmp_internal_CheckReferences(cmp_tree, params, s->Namespace->DName); @@ -369,6 +392,9 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Switch the namespace back **/ htrLeaveNamespace(s); + /** End the containing layer. **/ + htrAddBodyItem(s, "
\n"); + /** End Init component **/ htrAddScriptInit_va(s, " cmp_endinit(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", name); diff --git a/centrallix/htmlgen/htdrv_dropdown.c b/centrallix/htmlgen/htdrv_dropdown.c index 335fe7f48..7f16bcbe5 100644 --- a/centrallix/htmlgen/htdrv_dropdown.c +++ b/centrallix/htmlgen/htdrv_dropdown.c @@ -145,12 +145,62 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { strtcpy(name,ptr,sizeof(name)); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#dd%POSbtn { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:%POSpx; WIDTH:%POSpx; Z-INDEX:%POS; cursor:default; background-color: %STR&CSSVAL; border:1px outset #e0e0e0;}\n",id,x,y,h,w,z,bgstr); + htrAddStylesheetItem_va(s, + "\t#dd%POSbtn { " + "OVERFLOW:hidden; " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:"ht_flex_format"; " + "TOP:"ht_flex_format"; " + "WIDTH:"ht_flex_format"; " + "HEIGHT:"ht_flex_format"; " + "Z-INDEX:%POS; " + "cursor:default; " + "background-color: %STR&CSSVAL; " + "border:1px outset #e0e0e0; " + "}\n", + id, + ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + z, + bgstr + ); if (*textcolor) { htrAddStylesheetItem_va(s,"\t#dd%POSbtn { color: %STR&CSSVAL; }\n",id,textcolor); } - htrAddStylesheetItem_va(s,"\t#dd%POScon1 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:1px; TOP:1px; WIDTH:1024px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,h-2,z+1); - htrAddStylesheetItem_va(s,"\t#dd%POScon2 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:1px; TOP:1px; WIDTH:1024px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,h-2,z+1); + htrAddStylesheetItem_va(s, + "\t#dd%POScon1 { " + "OVERFLOW:hidden; " + "POSITION:absolute; " + "VISIBILITY:inherit; " + "LEFT:1px; " + "TOP:1px; " + "WIDTH:1024px; " + "HEIGHT:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(h-2, h, 0.0), + z+1 + ); + /** I have no idea why we need dd#con2. It's hidden by default. **/ + htrAddStylesheetItem_va(s, + "\t#dd%POScon2 { " + "OVERFLOW:hidden; " + "POSITION:absolute; " + "VISIBILITY:hidden; " + "LEFT:1px; " + "TOP:1px; " + "WIDTH:1024px; " + "HEIGHT:"ht_flex_format"; " + "Z-INDEX:%POS; " + "}\n", + id, + ht_flex(h-2, h, 0.0), + z+1 + ); htrAddScriptGlobal(s, "dd_current", "null", 0); htrAddScriptGlobal(s, "dd_lastkey", "null", 0); diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index 7ad156f71..c46c66219 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -178,14 +178,39 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) /** HTML body
element for the base layer. **/ if (!strcmp(aspect, "stretch")) { - htrAddBodyItemLayer_va(s, 0, "img%POS", id, "wimage", - "\n\n", + htrAddBodyItemLayer_va(s, + 0, "img%POS", id, "wimage", + "\n\n", id, w, h, src); } else // "preserve" { - htrAddBodyItemLayer_va(s, 0, "img%POS", id, "wimage", - "\n\n", + htrAddBodyItemLayer_va(s, + 0, "img%POS", id, "wimage", + "\n\n", id, w, h, src); } diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 0caf96258..9bd8a6759 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -187,7 +187,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) "\t#pn%POSmain {" "POSITION:absolute; " "VISIBILITY:inherit; " - "overflow:hidden; " + "OVERFLOW:hidden; " "LEFT:"ht_flex_format"; " "TOP:"ht_flex_format"; " "WIDTH:"ht_flex_format"; " @@ -234,7 +234,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) /** HTML body
element for the base layer. **/ //htrAddBodyItem_va(s,"
\n",id, w-2, h-2); - htrAddBodyItem_va(s,"
\n",id, w-2, h-2); + htrAddBodyItem_va(s,"
\n", id); /** Check for objects within the pane. **/ htrRenderSubwidgets(s, tree, z+2); diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index da8792946..74fe8adf1 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -53,12 +53,12 @@ struct _APOS_L pAposSection ESection; // section ending with this line /*** Used to find the distance the line should move when the parent - *** container is resized. LocAdjWeight is the weight that this line - *** moves relative to the container. MyAdjWeight is the amount that - *** this line moves relative to the line before (left of) it. + *** container is resized. loc_fl (local flex) is the weight that this + *** line moves relative to the container. my_fl (my flex) is the amount + *** that this line moves relative to the line before (left of) it. *** Used for generating responsive CSS. ***/ - float LocAdjWeight, MyAdjWeight; + float loc_fl, my_fl; }; /**Section Structure (used for both rows and columns)**/ @@ -71,19 +71,6 @@ struct _APOS_S int DesiredWidth; //When we need to resize to honor max/mins int isSpacer; //set for narrow spaces between widgets int isBorder; //set for grid border sections - - /*** Computed value which stores the weight for how much this section - *** should be adjusted when resizing. All sections along each dimention - *** inside a single page or other container should have a total adjWeight - *** of 1.0. - *** - *** For example, if we have two sections with adj weights of 0.4 and 0.6. - *** If the container/page is stretched by 10 px, then 4 px is distributed - *** to the first section and 6 px is distributed to the second section. - *** - *** Warning: Currently unused. - ***/ - float AdjWeight; }; /**Grid Structure**/ @@ -175,4 +162,7 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t #define APOS_EGAPFLEX 30 #define APOS_CGAPFLEX 50 +/** Macros for readability and anticipation-of-change. **/ +#define isScrollpane(Parent) (!strcmp((Parent)->Type, "widget/scrollpane")) + #endif diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index b725eb9d4..02d32ec6d 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -396,34 +396,37 @@ int htruleRegister(char* ruletype, ...); ***/ #define ht_flex(size, total, flex) (size), (total), (flex) +/** ====[ Macros for getting total container size ]==== **/ +int ht_get_total_w__INTERNAL(pWgtrNode widget); +int ht_get_total_h__INTERNAL(pWgtrNode widget); + +#define ht_get_total_w(widget) ht_get_total_w__INTERNAL(widget) +#define ht_get_total_h(widget) ht_get_total_h__INTERNAL(widget) + +// #define ht_get_total_w(widget) ((widget)->Parent->width - (widget)->Parent->left - (widget)->Parent->right) +// #define ht_get_total_h(widget) ((widget)->Parent->height - (widget)->Parent->top - (widget)->Parent->bottom) + +/** ====[ Macros for getting total flexibilities ]==== **/ + /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the x direction. ***/ -#define ht_get_fl_x(widget) ((widget)->xAdjWeight) +#define ht_get_fl_x(widget) ((widget)->total_fl_x) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the y direction. ***/ -#define ht_get_fl_y(widget) ((widget)->yAdjWeight) +#define ht_get_fl_y(widget) ((widget)->total_fl_y) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the width direction. ***/ -#define ht_get_fl_w(widget) ((widget)->wAdjWeight) +#define ht_get_fl_w(widget) ((widget)->total_fl_w) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the height direction. ***/ -#define ht_get_fl_h(widget) ((widget)->hAdjWeight) - -int ht_get_total_w__INTERNAL(pWgtrNode widget); -int ht_get_total_h__INTERNAL(pWgtrNode widget); - -#define ht_get_total_w(widget) ht_get_total_w__INTERNAL(widget) -#define ht_get_total_h(widget) ht_get_total_h__INTERNAL(widget) - -// #define ht_get_total_w(widget) ((widget)->Parent->width - (widget)->Parent->left - (widget)->Parent->right) -// #define ht_get_total_h(widget) ((widget)->Parent->height - (widget)->Parent->top - (widget)->Parent->bottom) +#define ht_get_fl_h(widget) ((widget)->total_fl_h) /*** @brief A shortcut function to get the flexibility when writing the *** LEFT CSS attribute. @@ -439,5 +442,31 @@ int ht_get_total_h__INTERNAL(pWgtrNode widget); ***/ #define ht_get_fl_t ht_get_fl_y +/** ====[ Macros for being lazy ]==== **/ + +#define ht_flex_x(x, widget) ht_flex(x, ht_get_total_w(widget), ht_get_fl_x(widget)) +#define ht_flex_y(y, widget) ht_flex(y, ht_get_total_h(widget), ht_get_fl_y(widget)) +#define ht_flex_w(w, widget) ht_flex(w, ht_get_total_w(widget), ht_get_fl_w(widget)) +#define ht_flex_h(h, widget) ht_flex(h, ht_get_total_h(widget), ht_get_fl_h(widget)) + +#define ht_flex_format_all \ + "LEFT:"ht_flex_format"; " \ + "TOP:"ht_flex_format"; " \ + "WIDTH:"ht_flex_format"; " \ + "HEIGHT:"ht_flex_format"; " \ + +#define ht_flex_all(x, y, w, h, widget) \ + ht_flex_x(x, widget), \ + ht_flex_y(y, widget), \ + ht_flex_w(w, widget), \ + ht_flex_h(h, widget) \ + +// Workaround because -lm isn't passed to my editor and this was the easiest way to fix it. +// This code should not appear in a pull request. If you are a code reviewer, remove this +// code or reject the PR. +#ifndef M_PI +#define M_PI 3.14159 +#endif + #endif /* _HT_RENDER_H */ diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index f11584f0d..298bf5dc9 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -100,16 +100,17 @@ typedef struct _WN char Namespace[64]; /** Namespace this widget and subwidgets are in **/ int r_x, r_y, r_width, r_height; /** Requested geometry **/ int pre_x, pre_y, pre_width, pre_height; /** pre-layout geom. **/ - int fl_x, fl_y, fl_width, fl_height;/** Flexibility **/ - double fx, fy, fw, fh; /** internal flexibility calculations **/ - double xAdjWeight, yAdjWeight; /** Responsive CSS adjustment weights for x and y */ - double wAdjWeight, hAdjWeight; /** Responsive CSS adjustment weights for width and height */ + int fl_x, fl_y, fl_width, fl_height;/** Flexibilities as specified by the designer **/ + double fx, fy, fw, fh; /** internal flexibility calculations **/ + double total_fl_x, total_fl_y; /** Total flexiblities as calculated for this layout. */ + double total_fl_w, total_fl_h; /** Responsive CSS adjustment weights for width and height */ + int parent_w, parent_h; /** The expected size of the parent container */ int min_width, min_height; /** absolute minimums **/ int x, y, width, height; /** actual geometry **/ int top, bottom, left, right; /** container offsets **/ XArray Properties; /** Array of widget properties **/ XArray Children; /** Array of child widgets **/ - struct _WN* Parent; + struct _WN* Parent; struct _WN* Root; int CurrProperty; /** Property to return on next call to wgtNextProperty **/ int CurrChild; /** Child to return on next call to wgtrNextChild **/ diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index f51d508b1..29a1b88ec 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -31,6 +31,8 @@ /*** Author: Israel Fuller *** Date: June, 2025 + *** + *** See also: Auto-Positioning.md *** *** I wasn't the one to write most of this (although I did write a ton of *** comments), but after doing my best to understand it, I hope that you will @@ -123,6 +125,11 @@ int i, childCnt, sectionCnt; pAposSection section; pWgtrNode child; + /*** Note: The "%*.*s" format specifier here takes two parameters: + *** - The first (indent*4) specifies the minimum width (number of spaces). + *** - The second (indent*4) limits the maximum number of characters to print. + *** Essentially, this adds (indent*4) spaces to the start of the line. + ***/ printf("%*.*s*** %s ***\n", indent*4, indent*4, "", tree->Name); if (tree->LayoutGrid) { @@ -448,7 +455,7 @@ int i=0, childCount=xaCount(&(Parent->Children)); *** Remember here that strcmp() returns 0 (false) if the strings are equal. ***/ if((Child->height < 0) && !(Child->Flags & WGTR_F_NONVISUAL) && - strcmp(Parent->Type, "widget/scrollpane")) + !isScrollpane(Parent)) aposPatchNegativeHeight(Child, PatchedWidgets); /** If child is a container, but not a floating window, recursively prepare it as well. **/ @@ -587,7 +594,7 @@ aposSetOffsetBools(pWgtrNode W, int *isSP, int *isWin, int *isTopTab, int *isSid ObjData val; /** Set isSP to compensate for scrollpane scrollbars. **/ - if(isSP) *isSP = (!strcmp(W->Type, "widget/scrollpane")); + if(isSP) *isSP = (isScrollpane(W)); /** Set isWin to compensate windows' titlebars, if any. **/ if(isWin && !strcmp(W->Type, "widget/childwindow")) @@ -838,7 +845,7 @@ pXArray FirstCross, LastCross; height_adj = Parent->min_height - Parent->pre_height; /** Add the 2 horizontal border lines, unless parent is a scrollpane. **/ - if(strcmp(Parent->Type, "widget/scrollpane")) + if(!isScrollpane(Parent)) { int minHeightLoc = 0, maxHeightLoc = Parent->pre_height - isWin * 24; if(aposCreateLine(NULL, HLines, minHeightLoc, APOS_NOT_LINKED, APOS_IS_BORDER, 0, APOS_HORIZONTAL) < 0) @@ -955,9 +962,10 @@ pWgtrNode C; else if(!(C->Flags & WGTR_F_NONVISUAL) && !(C->Flags & WGTR_F_FLOATING)) { /** Add horizontal lines, unless parent is a scrollpane. **/ - if(strcmp(Parent->Type, "widget/scrollpane")) + if(!isScrollpane(Parent)) { - /*** From this code, we see that the start line is + /*** Note: + *** From this code, we see that the start line is *** always the minY, and the end of the line is *** always the maxY. Thus, the top line is the *** start line and the bottom line is the end line @@ -969,14 +977,15 @@ pWgtrNode C; if(aposCreateLine(C, HLines, maxY, APOS_EWIDGETS, APOS_NOT_BORDER, height_adj, APOS_HORIZONTAL) < 0) goto CreateLineError; } - - /** Add vertical lines. **/ - /*** From this code, we see that the start line is always + + /*** Note: + *** From this code, we see that the start line is always *** the minX, and the end of the line is always the maxX. *** Thus, the left line is the start line and the right *** line is the end line because X increases as we move *** right along the page. ***/ + /** Add vertical lines. **/ int minX = (C->x), maxX = (C->x + C->width + isSideTab*tabWidth); if(aposCreateLine(C, VLines, minX, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_VERTICAL) < 0) goto CreateLineError; @@ -1465,10 +1474,9 @@ float TotalSum=0; TotalFlex += CurrSect->Flex; if(CurrSect->Flex) { - FlexibleSections++; - TotalFlexibleSpace += CurrSect->Width; + FlexibleSections++; + TotalFlexibleSpace += CurrSect->Width; } - else CurrSect->AdjWeight = 0.0f; } /*** If there is no flexibility (no expansion or contraction), we can't @@ -1490,7 +1498,7 @@ float TotalSum=0; /** The initial borders do not adjust. **/ pAposLine leftBorder = (pAposLine)xaGetItem(Lines, 0); - leftBorder->LocAdjWeight = leftBorder->MyAdjWeight = 0.0f; + leftBorder->loc_fl = leftBorder->my_fl = 0.0f; for(i=1; iAdjWeight = (float)(FlexWeight*SizeWeight)/TotalSum; + float fl = (float)(FlexWeight * SizeWeight) / TotalSum; /** Store the line adjustment weight for responsive CSS later. **/ - CurrLine->LocAdjWeight = PrevLine->LocAdjWeight + AdjWeight; - CurrLine->MyAdjWeight = AdjWeight; + CurrLine->loc_fl = PrevLine->loc_fl + fl; + CurrLine->my_fl = fl; /** Expand lines. **/ if(Diff > 0) { /** Calculate adjustment using the adjustment weight. **/ - Adj = (float)(Diff) * AdjWeight + APOS_FUDGEFACTOR; + Adj = (float)(Diff) * fl + APOS_FUDGEFACTOR; - // printf("Expanding lines by %d*%f=%d\n", Diff, AdjWeight, Adj); + // printf("Expanding lines by %d*%f=%d\n", Diff, fl, Adj); /** Apply the calculated adjustment. **/ PrevSect->Width += Adj; @@ -1526,9 +1534,9 @@ float TotalSum=0; else if(Diff < 0) { /** Calculate adjustment using the adjustment weight. **/ - Adj = (float)(Diff) * AdjWeight - APOS_FUDGEFACTOR; + Adj = (float)(Diff) * fl - APOS_FUDGEFACTOR; - // printf("Contracting lines by %d*%f=%d\n", Diff, AdjWeight, Adj); + // printf("Contracting lines by %d*%f=%d\n", Diff, fl, Adj); /** if the section width will be unacceptably *** narrow or negative after the adjustment **/ @@ -1596,11 +1604,11 @@ pWgtrNode Widget; Widget = (pWgtrNode)xaGetItem(&(CurrLine->SWidgets), j); if(flag == APOS_ROW) { Widget->y = CurrLine->Loc; - Widget->yAdjWeight = CurrLine->LocAdjWeight; + Widget->total_fl_y = CurrLine->loc_fl; } else { Widget->x = CurrLine->Loc; - Widget->xAdjWeight = CurrLine->LocAdjWeight; + Widget->total_fl_x = CurrLine->loc_fl; } } @@ -1627,7 +1635,7 @@ pWgtrNode Widget; *** to problems down the road, but I plan to fix *** them if and when I encounter them. ***/ - Widget->hAdjWeight += CurrLine->MyAdjWeight; + Widget->total_fl_h += CurrLine->my_fl; } else if(flag==APOS_COL && Widget->fl_width) { @@ -1650,7 +1658,7 @@ pWgtrNode Widget; *** to problems down the road, but I plan to fix *** them if and when I encounter them. ***/ - Widget->wAdjWeight += CurrLine->MyAdjWeight; + Widget->total_fl_w += CurrLine->my_fl; } } @@ -1661,9 +1669,9 @@ pWgtrNode Widget; { Widget = (pWgtrNode)xaGetItem(&(CurrLine->CWidgets), j); if(flag==APOS_ROW && Widget->fl_height) - Widget->hAdjWeight += CurrLine->MyAdjWeight; + Widget->total_fl_h += CurrLine->my_fl; else if(flag==APOS_COL && Widget->fl_width) - Widget->wAdjWeight += CurrLine->MyAdjWeight; + Widget->total_fl_w += CurrLine->my_fl; } } diff --git a/centrallix/wgtr/wgtr.c b/centrallix/wgtr/wgtr.c index 0fa312f19..5723c67eb 100755 --- a/centrallix/wgtr/wgtr.c +++ b/centrallix/wgtr/wgtr.c @@ -1310,10 +1310,11 @@ wgtrGetPropertyType(pWgtrNode widget, char* name) else if (!strcmp(name, "outer_type")) return DATA_T_STRING; else if (!strcmp(name, "x") || !strcmp(name, "y") || !strcmp(name, "width") || !strcmp(name, "height") || !strcmp(name, "r_x") || !strcmp(name, "r_y") || !strcmp(name, "r_width") || !strcmp(name, "r_height") || - !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height")) + !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height") || + !strcmp(name, "parent_w") || !strcmp(name, "parent_h")) return DATA_T_INTEGER; - else if (!strcmp(name, "adj_weight_x") || !strcmp(name, "adj_weight_y") || - !strcmp(name, "adj_weight_w") || !strcmp(name, "adj_weight_h") || + else if (!strcmp(name, "total_fl_x") || !strcmp(name, "total_fl_y") || + !strcmp(name, "total_fl_w") || !strcmp(name, "total_fl_h") || !strcmp(name, "fx") || !strcmp(name, "fy") || !strcmp(name, "fw") || !strcmp(name, "fh")) return DATA_T_DOUBLE; count = xaCount(&(widget->Properties)); @@ -1356,6 +1357,11 @@ wgtrGetPropertyValue(pWgtrNode widget, char* name, int datatype, pObjData val) if (!strcmp(name+3, "width")) { val->Integer = widget->fl_width; return 0; } if (!strcmp(name+3, "height")) { val->Integer = widget->fl_height; return 0; } } + else if (!strncmp(name, "parent_", 7)) + { + if (!strcmp(name+7, "w")) { val->Integer = widget->parent_w; return 0; } + if (!strcmp(name+7, "h")) { val->Integer = widget->parent_h; return 0; } + } } if (datatype == DATA_T_DOUBLE) { @@ -1366,12 +1372,12 @@ wgtrGetPropertyValue(pWgtrNode widget, char* name, int datatype, pObjData val) else if (!strcmp(name+1, "w")) { val->Double = widget->fw; return 0; } else if (!strcmp(name+1, "h")) { val->Double = widget->fh; return 0; } } - else if (!strncmp(name, "adj_weight_", 11)) + else if (!strncmp(name, "total_fl_", 9)) { - if (!strcmp(name+11, "x")) { val->Double = (double)widget->xAdjWeight; return 0; } - else if (!strcmp(name+11, "y")) { val->Double = (double)widget->yAdjWeight; return 0; } - else if (!strcmp(name+11, "w")) { val->Double = (double)widget->wAdjWeight; return 0; } - else if (!strcmp(name+11, "h")) { val->Double = (double)widget->hAdjWeight; return 0; } + if (!strcmp(name+9, "x")) { val->Double = (double)widget->total_fl_x; return 0; } + else if (!strcmp(name+9, "y")) { val->Double = (double)widget->total_fl_y; return 0; } + else if (!strcmp(name+9, "w")) { val->Double = (double)widget->total_fl_w; return 0; } + else if (!strcmp(name+9, "h")) { val->Double = (double)widget->total_fl_h; return 0; } } } else if (datatype == DATA_T_STRING) @@ -1592,10 +1598,10 @@ wgtrNewNode( char* name, char* type, pObjSession s, node->fl_y = fly; node->fl_width = flwidth; node->fl_height = flheight; + node->parent_h = node->parent_w = -1; node->ObjSession = s; node->Parent = NULL; - node->min_height = 0; - node->min_width = 0; + node->min_height = node->min_width = 0; node->LayoutGrid = NULL; node->Root = node; /* this will change when it is added as a child */ node->DMPrivate = NULL; From 31f9a0d66a3442d3e0322328c240fb804ffb186a Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 12:45:48 -0700 Subject: [PATCH 019/107] Improve apos.c & add design support. Add tab-height support to apos.c. Add design support to apos.c. Add IsDesign to the WgtrClientInfo struct. Fix spelling mistakes. Clean up. --- centrallix/include/apos.h | 19 ++-- centrallix/include/wgtr.h | 1 + centrallix/netdrivers/net_http.c | 3 + centrallix/wgtr/apos.c | 174 ++++++++++++++++--------------- 4 files changed, 106 insertions(+), 91 deletions(-) diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index 74fe8adf1..0333421cc 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -92,7 +92,7 @@ int aposInit(); /**Registers datastructures used in auto-positioning**/ int aposInitiallizeGrid (pAposGrid); /**Initiallizes the XArrays in the grid object**/ int aposFree(pAposGrid); /**Frees dynamically allocated memory**/ int aposFreeGrids(pWgtrNode); /**Frees dynamically allocated memory**/ -int aposSetOffsetBools(pWgtrNode, int*, int*, int*, int*, int*); /**sets bools used to offset widgets**/ +int aposSetOffsetBools(pWgtrNode, int*, int*, int*, int*, int*, int*); /**sets bools used to offset widgets**/ int aposBuildGrid(pWgtrNode); /** builds the layout grids **/ int aposSetLimits(pWgtrNode); /** enforce min/max sizing **/ @@ -120,17 +120,18 @@ int aposMinimumChildFlex(pAposLine, int); /**Returns minimum flexibility of wid /**Resizing and Repositioning**/ int aposSpaceOutLines(pXArray, pXArray, int); /**Adjusts spaces between lines to expand or contract grid**/ -int aposSnapWidgetsToGrid(pXArray, int); /**Refreshes widget dimensions to match adjusted grid**/ +int aposSnapWidgetsToGrid(pXArray, int, pWgtrClientInfo); /**Refreshes widget dimensions to match adjusted grid**/ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree to process windows**/ -/** # defines names for magic values to make them easier to read. **/ -/** Indicates how a line links to a widget. */ +/** #define names for values to improve readability. **/ + +/** Indicates how a line links to a widget. **/ #define APOS_NOT_LINKED 0 #define APOS_SWIDGETS 1 #define APOS_EWIDGETS 2 -/** Indicates if a line is vertical. */ +/** Indicates if a line is vertical. **/ #define APOS_VERTICAL 1 #define APOS_HORIZONTAL 0 @@ -141,11 +142,11 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t #define APOS_ROW 1 #define APOS_COL 2 -/** Indicates if a line is a border. */ +/** Indicates if a line is a border. **/ #define APOS_IS_BORDER 1 #define APOS_NOT_BORDER 0 -/** Allows rounding when casting floats or doubles to ints. */ +/** Allows rounding when casting floats or doubles to ints. **/ #define APOS_FUDGEFACTOR 0.5 /*** The greatest width between two widgets that still defines them as @@ -159,6 +160,10 @@ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree t #define APOS_MINWIDTH 30 /** Default flexibilities for widgetless gaps in expanding or contracting applications. **/ +/*** Israel: I don't know the difference between these two values. I'm guessing + *** E stands enlarge and C stands for contract, thus (30, 50) makes it + *** easier for gaps to grown than for them to shrink. + ***/ #define APOS_EGAPFLEX 30 #define APOS_CGAPFLEX 50 diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index 298bf5dc9..d9a89b1dc 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -69,6 +69,7 @@ typedef struct int CharWidth; int CharHeight; int ParagraphHeight; /* total height of one line of text */ + int IsDesign; /* 1 if the page is rendered with cx__geom=design, 0 otherwise. */ char AKey[256]; char* Templates[WGTR_MAX_TEMPLATE]; char* Overlays[WGTR_MAX_OVERLAY]; diff --git a/centrallix/netdrivers/net_http.c b/centrallix/netdrivers/net_http.c index c74bc121b..17c6a464c 100755 --- a/centrallix/netdrivers/net_http.c +++ b/centrallix/netdrivers/net_http.c @@ -2096,6 +2096,9 @@ nht_i_GET(pNhtConn conn, pStruct url_inf, char* if_modified_since) wgtr_params.CharWidth = 7; wgtr_params.CharHeight = 16; wgtr_params.ParagraphHeight = 16; + + /** Specify design geometry **/ + wgtr_params.IsDesign = 1; } else { diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 29a1b88ec..06550eda0 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -31,13 +31,13 @@ /*** Author: Israel Fuller *** Date: June, 2025 - *** + *** *** See also: Auto-Positioning.md *** *** I wasn't the one to write most of this (although I did write a ton of *** comments), but after doing my best to understand it, I hope that you will *** find the compiled information below helpful. - *** + *** *** Execution of this file usually begins when wgtrVerify() in wgtr.c calls *** aposAutoPositionWidgetTree(). To auto position the tree, the code first *** it draws four lines on the four edges of every visible widget (with some @@ -49,25 +49,25 @@ *** small amounts of space intended to provide visual room between widgets. *** When resizing, these do not flex at all. However, many elements are able *** to flex. @see aposSetFlexibilities() for more information about flexing. - *** + *** *** Next, the program uses aposSetLimits() to honor minimum and maximum sizes *** of widgets, and finally calls aposAutoPositionContainers() to position *** the widgets on the screen. Lastly, it calls aposProcessWindows() to handle *** floating window widgets, which are typically ignored by most of the rest *** of the code. - *** + *** *** Note: Due to this approach, this means that all sections and widgets start - *** and end at a line. The way these lines are set up ensures that start - *** lines are always on the top or left, and end lines are always on the - *** bottom or right. @see aposAddLinesForChildren() - *** - *** Notes: I wrote some information about various structs below that's good to - *** know. Some of this is covered elsewhere in the documentation. + *** and end at a line. The way these lines are set up ensures that start + *** lines are always on the top or left, and end lines are always on the + *** bottom or right. @see aposAddLinesForChildren() + *** + *** Note: I wrote some information about various structs below that's good to + *** know. Some of this is covered elsewhere in the documentation. *** *** AposGrid: A data structure to store sections and lines. - *** + *** *** AposLine: An AposLine spans the entire page. - *** + *** *** AposSection: After lines are created, sections are added in between the *** lines (aka. in between the nodes). Every node begins and ends on the *** edge of a section, although it may span multiple sections. @@ -80,7 +80,7 @@ *** *** XArray: This array also stores its size (nAlloc) and the number of items *** stored (nItems), so you don't have to pass that info separately. - *** + *** *** SWidgets, CWidgets, and EWidgets: Lines record which widgets start, cross, *** and end on them. These categories are exclusive, so a widget which *** starts on a given line will be in the SWidgets list but it will not be @@ -295,11 +295,11 @@ int sectCount; return 0; } -/*** Adjusts space to acomodate children, somehow? I think? +/*** Adjusts space to accommodate children, somehow? I think? *** *** @param Parent The widget node parent who's limits are being calculated. - *** @param delta_w The change in width required to accomodate children. - *** @param delta_h The change in height required to accomodate children. + *** @param delta_w The change in width required to accommodate children. + *** @param delta_h The change in height required to accommodate children. *** @returns 0 if successful, -1 otherwise. ***/ int @@ -406,7 +406,7 @@ pWgtrNode Child; return 0; } -/*** Adjusts space to acomodate children, somehow? I think? +/*** Adjusts space to accommodate children, somehow? I think? *** *** @param Parent The widget node parent who's limits are being calculated. *** @returns 0, success. @@ -543,7 +543,7 @@ int i=0, sectCount=0, TotalWidth=0, ProductSum=0; /*** Calculate average row flexibility, weighted by height. *** Note: Section height is called width here because rows - *** are one dimentional and the feild is reused. + *** are one dimensional and the feild is reused. ***/ sectCount = xaCount(&(theGrid->Rows)); for(i=0; iType, "widget/tab")) + if((isTopTab != NULL || isSideTab != NULL) && strcmp(W->Type, "widget/tab") == 0) { /*** Set isTopTab and isSideTab. If the node does not specify the *** tab location, assume it has a top tab and leave side-tab unset. **/ if(wgtrGetPropertyValue(W, "tab_location", DATA_T_STRING, &val) < 0) - *isTopTab = 1; // Property not found, assume it has a top tab only. + { + if (isTopTab != NULL) *isTopTab = 1; // Property not found, assume it has a top tab only. + } else { *isTopTab = (!strcmp(val.String, "top") || !strcmp(val.String, "bottom")); - *isSideTab = (!strcmp(val.String, "left") || (!strcmp(val.String, "right"))); // Warning: Unchecked assignment. + *isSideTab = (!strcmp(val.String, "left") || (!strcmp(val.String, "right"))); } - - /** Set the tab width. If none is specified, default to 80. **/ - if(wgtrGetPropertyValue(W, "tab_width", DATA_T_INTEGER, &val) < 0) - *tabWidth = 80; - else *tabWidth = val.Integer; } + + /** Set the tab width and height (if needed), defaulting to 24 and 80 if unspecified. **/ + if(tabWidth != NULL) *tabWidth = (wgtrGetPropertyValue(W, "tab_width", DATA_T_INTEGER, &val) == 0) ? val.Integer : 80; + if(tabHeight != NULL) *tabHeight = (wgtrGetPropertyValue(W, "tab_height", DATA_T_INTEGER, &val) == 0) ? val.Integer : 24; return 0; } @@ -703,7 +705,7 @@ pAposGrid theGrid = NULL; /*** Recursively auto-positions containers and their children based on their grids. *** - *** Note: Assumes that the grild was already built with a call to aposBuildGrid(). + *** Note: Assumes that the grid was already built with a call to aposBuildGrid(). *** *** @param Parent The parent node who's containers are being autopositioned. *** @returns 0 if successful, -1 otherwise. @@ -739,9 +741,9 @@ int rows_extra=0, cols_extra=0; /**modify the widgets' x,y,w, and h values to snap to their adjusted lines**/ if (!(Parent->Flags & WGTR_F_VSCROLLABLE)) - aposSnapWidgetsToGrid(&(theGrid->HLines), APOS_ROW); //rows + aposSnapWidgetsToGrid(&(theGrid->HLines), APOS_ROW, Parent->Root->ClientInfo); //rows if (!(Parent->Flags & WGTR_F_HSCROLLABLE)) - aposSnapWidgetsToGrid(&(theGrid->VLines), APOS_COL); //columns + aposSnapWidgetsToGrid(&(theGrid->VLines), APOS_COL, Parent->Root->ClientInfo); //columns /** did not resize? **/ /*if (rows_extra < 0) @@ -814,7 +816,7 @@ aposInitiallizeGrid(pAposGrid theGrid) *** lines for each visual child. Searches nonvisual containers recursively for *** qualifying grandchildren. Floating windows are ignored. *** - *** Scrollpanes recieve only 2 vertical lines (skipping their horizontal edges), + *** Scrollpanes receive only 2 vertical lines (skipping their horizontal edges), *** and if the parent node is a scrollpane, the horizontal border lines are also *** skipped. *** @@ -836,7 +838,7 @@ pAposLine CurrLine, PrevLine; pXArray FirstCross, LastCross; /** Check if this node a scrollbar or window that needs an offset. **/ - aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL); + aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL, NULL); /** Does this widget need more room than it was given? **/ if (Parent->pre_width < Parent->min_width && Parent->min_width != 0) @@ -916,7 +918,7 @@ pXArray FirstCross, LastCross; /*** Adds 4 lines for the edges of each visual child. Searches nonvisual *** containers recursively for qualifying grandchildren. Floating windows - *** are ignored. Scrollpanes recieve only 2 vertical lines (skipping their + *** are ignored. Scrollpanes receive only 2 vertical lines (skipping their *** horizontal edges). *** *** @param Parent The parent who's children are being given lines. @@ -928,7 +930,7 @@ int aposAddLinesForChildren(pWgtrNode Parent, pXArray HLines, pXArray VLines) { int i=0, childCount=xaCount(&(Parent->Children)); -int isTopTab=0, isSideTab=0, tabWidth=0; +int isTopTab=0, isSideTab=0, tabWidth=0, tabHeight=0; int height_adj, width_adj; pWgtrNode C; @@ -943,7 +945,7 @@ pWgtrNode C; for(i=0; iChildren), i); - aposSetOffsetBools(C, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); + aposSetOffsetBools(C, NULL, NULL, &isTopTab, &isSideTab, &tabWidth, &tabHeight); /** Does this widget need more room than it was given? **/ height_adj = width_adj = 0; @@ -971,7 +973,7 @@ pWgtrNode C; *** start line and the bottom line is the end line *** because Y increases as we decend the page. ***/ - int minY = (C->y), maxY = (C->y + C->height + isTopTab*24); + int minY = (C->y), maxY = (C->y + C->height + isTopTab*tabHeight); if(aposCreateLine(C, HLines, minY, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_HORIZONTAL) < 0) goto CreateLineError; if(aposCreateLine(C, HLines, maxY, APOS_EWIDGETS, APOS_NOT_BORDER, height_adj, APOS_HORIZONTAL) < 0) @@ -1051,7 +1053,7 @@ pAposLine Line = aposExistingLine(Lines, Loc); return -1; } - /** Initiallize the new line. **/ + /** Initialize the new line. **/ memset(Line, 0, sizeof(AposLine)); xaInit(&(Line->SWidgets),16); xaInit(&(Line->EWidgets),16); @@ -1114,7 +1116,7 @@ int i, count = xaCount(Lines); } /*** Detects if a widget in PrevList (usually the widgets that started in or - *** crossed the pevious line) ends on this line (aka. appears in EWidgets). + *** crossed the previous line) ends on this line (aka. appears in EWidgets). *** If it does not end on this line, we know it crosses this line, so we add *** the widget to CWidgets. *** @@ -1160,7 +1162,7 @@ int found=0, i=0, j=0, pCount=xaCount(PrevList), eCount=xaCount(EWidgets); /*** Adds row and column sections to the grid based on the lines. *** - *** @param theGrid The grid to which sections shoudl be added. + *** @param theGrid The grid to which sections should be added. *** @param VDiff I had a hard time figuring out what this means. *** @param HDiff I had a hard time figuring out what this means. *** @returns 0 if successful, -1 otherwise. @@ -1240,7 +1242,7 @@ aposCreateSection(pXArray Sections, pAposLine StartL, pAposLine EndL, int Diff, { pAposSection NewSect; - /** Allocate and initiallize a new section. **/ + /** Allocate and initialize a new section. **/ if((NewSect = (pAposSection)(nmMalloc(sizeof(AposSection)))) < 0) { mssError(1, "APOS", "nmMalloc(): Couldn't allocate memory for new row or column"); @@ -1282,7 +1284,7 @@ pAposSection NewSect; *** *** @param StartL The line starting the section. (I think this is always the left/top.) *** @param EndL The line starting the section. (I think this is always the right/bottom.) - *** @param type Whether the section is a row (APOS_ROW) or a column (APAS_COL). + *** @param type Whether the section is a row (APOS_ROW) or a column (APOS_COL). *** @param isBorder Whether the section is on the border of the page. *** @returns 0 if successful, -1 otherwise. ***/ @@ -1330,10 +1332,10 @@ int eCount=xaCount(&(StartL->EWidgets)); } /*** Checks for any widgets starting on or crossing a line that are non-flexible - *** in the relevant dimention. + *** in the relevant dimension. *** *** @param L The line along which to check. - *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @param type Specifies the relevant dimension using APOS_ROW or APOS_COL. *** @returns 1 if any child widget is non-flexible in the relevant dimension, *** 0 otherwise. ***/ @@ -1372,7 +1374,7 @@ int cCount = xaCount(&(L->CWidgets)); /*** Calculates the average flexibility of widgets on a line. *** *** @param L The line along which to check. - *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @param type Specifies the relevant dimension using APOS_ROW or APOS_COL. *** @returns The average flexibility of children on the line. ***/ int @@ -1405,7 +1407,7 @@ int cCount = xaCount(&(L->CWidgets)); /*** Calculates the minimum flexibility of widgets on a line. *** *** @param L The line along which to check. - *** @param type Specifies the relevant dimetion using APOS_ROW or APOS_COL. + *** @param type Specifies the relevant dimension using APOS_ROW or APOS_COL. *** @returns The minimum flexibility of children on the line. ***/ int @@ -1450,8 +1452,8 @@ int cCount = xaCount(&(L->CWidgets)); /*** Distributes extra or missing space among grid lines based on section flexibility. *** *** @param Lines The array of lines in the relevant direction on this grid. - *** @param Secctions The array of sections in the relevant direction on this grid. - *** @param Diff The space differencce from how the elements are currently spaced. + *** @param Sections The array of sections in the relevant direction on this grid. + *** @param Diff The space difference from how the elements are currently spaced. *** @returns The remaining space difference after spacing out elements as much as possible. ***/ int @@ -1474,8 +1476,8 @@ float TotalSum=0; TotalFlex += CurrSect->Flex; if(CurrSect->Flex) { - FlexibleSections++; - TotalFlexibleSpace += CurrSect->Width; + FlexibleSections++; + TotalFlexibleSpace += CurrSect->Width; } } @@ -1576,19 +1578,23 @@ float TotalSum=0; return Extra; } +/** TODO: Israel - Update widget flex scales to be 0 if design geometry is specified. **/ /*** Adjusts widget positions and sizes to snap them to grid lines. This *** function should be called after updating grid lines to ensure that *** widgets properly reflect the changes. *** *** @param Lines The lines being updated. *** @param flag Either APOS_ROW or APOS_COL. + *** @param info Info about the page design, currently used to determine if + *** flexibility should be used. *** @returns 0, success. ***/ int -aposSnapWidgetsToGrid(pXArray Lines, int flag) +aposSnapWidgetsToGrid(pXArray Lines, int flag, pWgtrClientInfo info) { +const int is_design = info->IsDesign; int i=0, j=0, count=0, lineCount = xaCount(Lines); -int isTopTab=0, isSideTab=0, tabWidth=0; +int isTopTab=0, isSideTab=0, tabWidth=0, tabHeight=0; int newsize; pAposLine CurrLine; pWgtrNode Widget; @@ -1602,14 +1608,16 @@ pWgtrNode Widget; for(j=0; jSWidgets), j); - if(flag == APOS_ROW) { - Widget->y = CurrLine->Loc; - Widget->total_fl_y = CurrLine->loc_fl; - } - else { - Widget->x = CurrLine->Loc; - Widget->total_fl_x = CurrLine->loc_fl; - } + if(flag == APOS_ROW) + { + Widget->y = CurrLine->Loc; + Widget->fl_scale_y = (is_design) ? 0.0 : CurrLine->loc_fl; + } + else + { + Widget->x = CurrLine->Loc; + Widget->fl_scale_x = (is_design) ? 0.0 : CurrLine->loc_fl; + } } /** Adjusts width or height of widgets ending on this line. **/ @@ -1617,7 +1625,7 @@ pWgtrNode Widget; for(j=0; jEWidgets), j); - aposSetOffsetBools(Widget, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); + aposSetOffsetBools(Widget, NULL, NULL, &isTopTab, &isSideTab, &tabWidth, &tabHeight); if(flag==APOS_ROW && Widget->fl_height) { /** Calculate the new size, taking APOS_MINWIDTH into account. **/ @@ -1629,13 +1637,11 @@ pWgtrNode Widget; else /*Widget->height = APOS_MINWIDTH;*/ Widget->height = Widget->pre_height; - + /*** The widget copies the adjustment weight of the - *** line, ignoring APOS_MINWIDTH. This might lead - *** to problems down the road, but I plan to fix - *** them if and when I encounter them. + *** line, ignoring APOS_MINWIDTH. ***/ - Widget->total_fl_h += CurrLine->my_fl; + Widget->fl_scale_h += (is_design) ? 0.0 : CurrLine->my_fl; } else if(flag==APOS_COL && Widget->fl_width) { @@ -1654,24 +1660,24 @@ pWgtrNode Widget; Widget->width = Widget->pre_width; /*** The widget copies the adjustment weight of the - *** line, ignoring APOS_MINWIDTH. This might lead - *** to problems down the road, but I plan to fix - *** them if and when I encounter them. + *** line, ignoring APOS_MINWIDTH. ***/ - Widget->total_fl_w += CurrLine->my_fl; + Widget->fl_scale_w += (is_design) ? 0.0 : CurrLine->my_fl; } } - /** Adjusts width or height of widgets ending on this line. **/ - count = xaCount(&(CurrLine->CWidgets)); - // printf("Doing %d widgets.\n", count); - for(j=0; jCWidgets), j); - if(flag==APOS_ROW && Widget->fl_height) - Widget->total_fl_h += CurrLine->my_fl; - else if(flag==APOS_COL && Widget->fl_width) - Widget->total_fl_w += CurrLine->my_fl; + if (!is_design) + { + /** Adjusts width or height of widgets ending on this line. **/ + count = xaCount(&(CurrLine->CWidgets)); + for(j=0; jCWidgets), j); + if(flag==APOS_ROW && Widget->fl_height) + Widget->fl_scale_h += (is_design) ? 0.0 : CurrLine->my_fl; + else if(flag==APOS_COL && Widget->fl_width) + Widget->fl_scale_w += (is_design) ? 0.0 : CurrLine->my_fl; + } } } @@ -1704,7 +1710,7 @@ int ival; return -1; } - aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL); + aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL, NULL); /**loop through children and process any windows**/ for(i=0; i Date: Fri, 6 Feb 2026 12:47:42 -0700 Subject: [PATCH 020/107] Improve error handling. Add an error message when cxsecVerifySymbol_n() fails. Improve an existing error message when htr_internal_WriteWgtrProperty() fails to write a property of an unknown type. Set Centrallix event listener to be explicitly non-passive, fixing a console error when later code assumes that calling preventDefault() is allowed. --- centrallix-os/sys/js/ht_render.js | 2 +- centrallix/htmlgen/ht_render.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/centrallix-os/sys/js/ht_render.js b/centrallix-os/sys/js/ht_render.js index cc93e167a..6b80b9ace 100644 --- a/centrallix-os/sys/js/ht_render.js +++ b/centrallix-os/sys/js/ht_render.js @@ -1489,7 +1489,7 @@ function htr_addeventlistener(eventType,obj,handler) if (typeof pg_capturedevents[eventType] == 'undefined') { pg_capturedevents[eventType] = handler; - obj.addEventListener(eventType, handler, true); + obj.addEventListener(eventType, handler, { capture: true, passive: false }); } } else diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 647374a67..e0ec2305a 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1425,7 +1425,8 @@ htr_internal_WriteWgtrProperty(pHtSession s, pWgtrNode tree, char* propname) break; default: - htrAddScriptWgtr_va(s, "%STR&SYM:'Unknown Datatype (%INT) - Add it in ht_render.c:htr_internal_WriteWgtrProperty()', ", propname, t); + fprintf(stderr, "Failed to write widget property '%s': Unknown datatype %d (at %s:%d).\n", propname, t, __FILE__, __LINE__); + htrAddScriptWgtr_va(s, "%STR&SYM:'Unknown Datatype (%INT) - Add it to htr_internal_WriteWgtrProperty() in ht_render.c.', ", propname, t); break; } } From 7b4b20b046ea5bcbd03ac8f32e3ce47241e8b942 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 12:49:50 -0700 Subject: [PATCH 021/107] Fix the qprintf() % bug. Fix a bug in qprintf() that caused % and & characters inside conditional printing areas to always print, regardless of the condition. Improve documentation for qpfPrintf_va_internal() and qpf_grow_fn_t(). Clean up. --- centrallix-lib/src/qprintf.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/centrallix-lib/src/qprintf.c b/centrallix-lib/src/qprintf.c index 1f4538a3d..3bac70867 100644 --- a/centrallix-lib/src/qprintf.c +++ b/centrallix-lib/src/qprintf.c @@ -880,6 +880,14 @@ qpf_internal_Translate(pQPSession s, const char* srcbuf, size_t srcsize, char** *** change out from under this function to a new buffer if a realloc is *** done by the grow_fn function. Do not store pointers to 'str'. Go *** solely by offsets. + *** + *** NULL, &(s->Tmpbuf), &(s->TmpbufSize), htr_internal_GrowFn, (void*)s, fmt, va + *** @param s Optional session struct. + *** @param str Pointer to a string buffer where data will be written. + *** @param size Pointer to the current size of the string buffer. + *** @param grow_fn A function to grow the string buffer. + *** @param format The format of data which should be written. + *** @param ap The arguments list to fulfill the provided format. ***/ int qpfPrintf_va_internal(pQPSession s, char** str, size_t* size, qpf_grow_fn_t grow_fn, void* grow_arg, const char* format, va_list ap) @@ -975,6 +983,12 @@ qpfPrintf_va_internal(pQPSession s, char** str, size_t* size, qpf_grow_fn_t grow /** Simple specifiers **/ if (__builtin_expect(format[0] == '%', 0)) { + if (ignore) + { + format++; + continue; + } + if (__builtin_expect(!nogrow, 1) && (__builtin_expect(cpoffset+2 <= *size, 1) || (grow_fn(str, size, cpoffset, grow_arg, cpoffset+2)))) (*str)[cpoffset++] = '%'; else @@ -987,6 +1001,12 @@ qpfPrintf_va_internal(pQPSession s, char** str, size_t* size, qpf_grow_fn_t grow } else if (__builtin_expect(format[0] == '&',0)) { + if (ignore) + { + format++; + continue; + } + if (__builtin_expect(!nogrow, 1) && (__builtin_expect(cpoffset+2 <= *size, 1) || (grow_fn(str, size, cpoffset, grow_arg, cpoffset+2)))) (*str)[cpoffset++] = '&'; else @@ -1448,5 +1468,3 @@ qpfRegisterExt(char* ext_spec, int (*ext_fn)(), int is_source) return; } - - From f55bb7952ffdfeafb5b9c96c86a10da86454c599 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:17:54 -0700 Subject: [PATCH 022/107] Improve naming conventions and dev experience. Add shortcut functions: ht_flex_x(), ht_flex_y(), ht_flex_w(), ht_flex_h(). Rename fl_scale_x (was total_fl_x). Rename fl_scale_y (was total_fl_y). Rename fl_scale_w (was total_fl_w). Rename fl_scale_h (was total_fl_h). Rename fl_parent_w (was parent_w). Rename fl_parent_h (was parent_h). Remove ht_flex_format_all and ht_flex_all(). Improve usage of new feature in previously updated widgets. Fix spelling mistakes. Clean up. --- centrallix/htmlgen/ht_render.c | 48 +++++----- centrallix/htmlgen/htdrv_autolayout.c | 8 +- centrallix/htmlgen/htdrv_image.c | 125 +++++++++++++------------- centrallix/htmlgen/htdrv_label.c | 27 +++--- centrallix/htmlgen/htdrv_pane.c | 37 ++++---- centrallix/htmlgen/htdrv_textbutton.c | 114 ++++++++++++++++------- centrallix/htmlgen/htdrv_treeview.c | 49 +++++----- centrallix/include/ht_render.h | 42 +++------ centrallix/include/wgtr.h | 6 +- centrallix/wgtr/wgtr.c | 44 +++++---- 10 files changed, 267 insertions(+), 233 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index e0ec2305a..871eb258d 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1501,12 +1501,12 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) htr_internal_WriteWgtrProperty(s, tree, "fl_y"); htr_internal_WriteWgtrProperty(s, tree, "fl_width"); htr_internal_WriteWgtrProperty(s, tree, "fl_height"); - htr_internal_WriteWgtrProperty(s, tree, "total_fl_x"); - htr_internal_WriteWgtrProperty(s, tree, "total_fl_y"); - htr_internal_WriteWgtrProperty(s, tree, "total_fl_w"); - htr_internal_WriteWgtrProperty(s, tree, "total_fl_h"); - htr_internal_WriteWgtrProperty(s, tree, "parent_w"); - htr_internal_WriteWgtrProperty(s, tree, "parent_h"); + htr_internal_WriteWgtrProperty(s, tree, "fl_scale_x"); + htr_internal_WriteWgtrProperty(s, tree, "fl_scale_y"); + htr_internal_WriteWgtrProperty(s, tree, "fl_scale_w"); + htr_internal_WriteWgtrProperty(s, tree, "fl_scale_h"); + htr_internal_WriteWgtrProperty(s, tree, "fl_parent_w"); + htr_internal_WriteWgtrProperty(s, tree, "fl_parent_h"); } propname = wgtrFirstPropertyName(tree); while(propname) @@ -2723,10 +2723,10 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y "%[%STR %]" "}\n", id, - ht_flex(x, ht_get_total_w(node), ht_get_fl_x(node)), - ht_flex(y, ht_get_total_h(node), ht_get_fl_y(node)), - (w > 0), ht_flex(w, ht_get_total_w(node), ht_get_fl_w(node)), - (h > 0), ht_flex(h, ht_get_total_h(node), ht_get_fl_h(node)), + ht_flex_x(x, node), + ht_flex_y(y, node), + (w > 0), ht_flex_w(w, node), + (h > 0), ht_flex_h(h, node), (z > 0), z, (*textcolor), textcolor, (!strcmp(style, "bold")), @@ -2750,9 +2750,9 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y int -ht_get_total_w__INTERNAL(pWgtrNode widget) { +ht_get_parent_w__INTERNAL(pWgtrNode widget) { /** Check to see if the value was already cached by a previous call. **/ - int cached_value = widget->parent_w; + int cached_value = widget->fl_parent_w; if (cached_value != -1) { // printf( // "Got total width available to '%s' (%s) from cache: %dpx\n", @@ -2763,7 +2763,7 @@ ht_get_total_w__INTERNAL(pWgtrNode widget) { // DEBUG if (widget->Parent == NULL) { - printf("\nPANIC: Call to ht_get_total_w__INTERNAL() on widget with no parent!\n\n"); + printf("\nPANIC: Call to ht_get_parent_w__INTERNAL() on widget with no parent!\n\n"); wgtrPrint(widget, 1); } @@ -2779,21 +2779,21 @@ ht_get_total_w__INTERNAL(pWgtrNode widget) { /** Check if the parent has a width value. **/ if (parentWidth >= 0 /* && isParentVisual */) { int offset = parent->left + parent->right, ret = parentWidth - offset; - printf("Returning %d-%d=%d\n", parentWidth, offset, ret); - return (widget->parent_w = ret); + // printf("Returning %d-%d=%d\n", parentWidth, offset, ret); + return (widget->fl_parent_w = ret); } else { if (parent->Parent == NULL) { printf("Recursive call would segfault! Guessing %dpx instead.\n", parentWidth); - return (widget->parent_w = parentWidth); + return (widget->fl_parent_w = parentWidth); } - return (widget->parent_w = ht_get_total_w(parent)); + return (widget->fl_parent_w = ht_get_parent_w(parent)); } } int -ht_get_total_h__INTERNAL(pWgtrNode widget) { +ht_get_parent_h__INTERNAL(pWgtrNode widget) { /** Check to see if the value was already cached by a previous call. **/ - int cached_value = widget->parent_h; + int cached_value = widget->fl_parent_h; if (cached_value != -1) { // printf( // "Got total height available to '%s' (%s) from cache: %dpx\n", @@ -2804,7 +2804,7 @@ ht_get_total_h__INTERNAL(pWgtrNode widget) { // DEBUG if (widget->Parent == NULL) { - printf("\nPANIC: Call to ht_get_total_h__INTERNAL() on widget with no parent!\n\n"); + printf("\nPANIC: Call to ht_get_parent_h__INTERNAL() on widget with no parent!\n\n"); wgtrPrint(widget, 1); } @@ -2820,13 +2820,13 @@ ht_get_total_h__INTERNAL(pWgtrNode widget) { /** Check if the parent has a height value. **/ if (parentHeight >= 0 /* && isParentVisual */) { int offset = parent->top + parent->bottom, ret = parentHeight - offset; - printf("Returning %d-%d=%d\n", parentHeight, offset, ret); - return (widget->parent_h = ret); + // printf("Returning %d-%d=%d\n", parentHeight, offset, ret); + return (widget->fl_parent_h = ret); } else { if (parent->Parent == NULL) { printf("Recursive call would segfault! Guessing %dpx instead.\n", parentHeight); - return (widget->parent_h = parentHeight); + return (widget->fl_parent_h = parentHeight); } - return (widget->parent_h = ht_get_total_h__INTERNAL(parent)); + return (widget->fl_parent_h = ht_get_parent_h__INTERNAL(parent)); } } diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index 9cc7eb7c4..02d6d7d2a 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -111,10 +111,10 @@ htalRender(pHtSession s, pWgtrNode tree, int z) "Z-INDEX:%POS; " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), z ); diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index c46c66219..b9fe1585f 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -75,9 +75,9 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) char src[256]; int x=-1,y=-1,w,h; int id, i; - char *text; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; + char* alt_text; char* aspect; if(!(s->Capabilities.Dom0NS || s->Capabilities.Dom1HTML)) @@ -104,9 +104,9 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) } if(wgtrGetPropertyValue(tree,"text",DATA_T_STRING,POD(&ptr)) == 0) - text=nmSysStrdup(ptr); + alt_text=nmSysStrdup(ptr); else - text=nmSysStrdup(""); + alt_text=nmSysStrdup(""); /** Image aspect scaling: stretch or preserve **/ if(wgtrGetPropertyValue(tree,"aspect",DATA_T_STRING,POD(&ptr)) == 0) @@ -122,7 +122,7 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) /*if (!htrCheckAddExpression(s, tree, name, "source") && wgtrGetPropertyValue(tree,"source",DATA_T_STRING,POD(&ptr)) != 0) { mssError(1,"HTIMG","Image widget must have a 'source' property"); - nmSysFree(text); + nmSysFree(alt_text); return -1; }*/ ptr = ""; @@ -144,7 +144,28 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) else form[0]='\0'; - /** Ok, write the style header items. **/ + /** Initialize linkage. **/ + htrAddWgtrObjLinkage_va(s, tree, "img%POS", id); + + /** Initialize image scripts. **/ + htrAddScriptInit_va(s, + "im_init(" + "wgtrGetNodeRef(ns,'%STR&SYM'), " + "{field:'%STR&JSSTR', form:'%STR&JSSTR'}" + ");\n", + name, + fieldname, form + ); + htrAddScriptInclude(s, "/sys/js/htdrv_image.js", 0); + + /** Event Handlers **/ + htrAddEventHandlerFunction(s, "document","MOUSEUP", "img", "im_mouseup"); + htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "img", "im_mousedown"); + htrAddEventHandlerFunction(s, "document","MOUSEOVER", "img", "im_mouseover"); + htrAddEventHandlerFunction(s, "document","MOUSEOUT", "img", "im_mouseout"); + htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "img", "im_mousemove"); + + /** Write the style for the image container div. **/ htrAddStylesheetItem_va(s, "\t#img%POS { " "left:"ht_flex_format"; " @@ -155,70 +176,52 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) "text-align:center; " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), z ); - /** Init image widget (?) **/ - htrAddWgtrObjLinkage_va(s, tree, "img%POS",id); - htrAddScriptInit_va(s, " im_init(wgtrGetNodeRef(ns,'%STR&SYM'), {field:'%STR&JSSTR', form:'%STR&JSSTR'});\n", - name, fieldname, form); - htrAddScriptInclude(s, "/sys/js/htdrv_image.js", 0); - - /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "img", "im_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "img", "im_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "img", "im_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "img", "im_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "img", "im_mousemove"); - - /** HTML body
element for the base layer. **/ - if (!strcmp(aspect, "stretch")) - { - htrAddBodyItemLayer_va(s, - 0, "img%POS", id, "wimage", - "\n\n", - id, w, h, src); - } - else // "preserve" - { - htrAddBodyItemLayer_va(s, - 0, "img%POS", id, "wimage", - "\n\n", - id, w, h, src); - } + /** Use a style that honors the aspect ratio attribute. **/ + char* style = (strcmp(aspect, "stretch") == 0) + ? /* "stretch" */ + "width:100%; " + "height:100%; " + : /* "preserve" */ + "width:100%; " + "height:auto; " + "max-width:fit-content; " + "max-height:fit-content; " + "display:inline; "; + + /** Write image HTML, including the containing div. **/ + htrAddBodyItemLayer_va(s, 0, + "img%POS", id, "wimage", + "\n\n", + id, + w, + h, + style, + src, + alt_text + ); /** Check for more sub-widgets **/ for (i=0;iChildren));i++) htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); - nmSysFree(text); + /** Clean up. **/ + nmSysFree(alt_text); + nmSysFree(aspect); return 0; } diff --git a/centrallix/htmlgen/htdrv_label.c b/centrallix/htmlgen/htdrv_label.c index ea315a9a5..77ea3ffdc 100644 --- a/centrallix/htmlgen/htdrv_label.c +++ b/centrallix/htmlgen/htdrv_label.c @@ -199,13 +199,13 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ htrAddStylesheetItem_va(s, "\t#lbl%POS { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "%[HEIGHT:"ht_flex_format"; %]" - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "z-index:%POS; " "cursor:default; " "%[font-weight:bold; %]" "%[color:%STR&CSSVAL; %]" @@ -217,10 +217,10 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) "%[font-style:italic; %]" "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - (!auto_height), ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + (!auto_height), ht_flex_h(h, tree), z, (is_bold), (*fgcolor), fgcolor, @@ -244,12 +244,11 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) "padding:0px; " "margin:0px; " "border-spacing:0px; " - "width:"ht_flex_format"; " + "width:100%%; " "}\n", id, align, - !strcmp(valign, "middle"), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)) + (strcmp(valign, "middle") == 0) ); htrAddWgtrObjLinkage_va(s, tree, "lbl%POS",id); diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 9bd8a6759..6536a9689 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -135,6 +135,8 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) if (!strcmp(ptr,"flat")) style = 2; if (!strcmp(ptr,"bordered")) style = 3; } + + /** Computes styling colors. **/ if (style == 1) /* raised */ { c1 = "white"; @@ -153,7 +155,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) strtcpy(bdr,ptr,sizeof(bdr)); } - /** Ok, write the style header items. **/ + /** Write the CSS for borders on the pane dom node. **/ int offset = 0; if (style == 2) { /* flat, the default style, nothing to do */ } else if (style == 0 || style == 1) /* lowered or raised */ @@ -183,24 +185,29 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) ); } + /** Apply the offset to the width and height. **/ + w += offset; + h += offset; + + /** Write the main CSS for the pane DOM node. **/ htrAddStylesheetItem_va(s, "\t#pn%POSmain {" - "POSITION:absolute; " - "VISIBILITY:inherit; " - "OVERFLOW:hidden; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "HEIGHT:"ht_flex_format"; " - "Z-INDEX:%POS; " - "border-radius: %INTpx;" - "%STR" + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "border-radius:%INTpx; " + "%STR " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w + offset, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h + offset, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), z, border_radius, main_bg diff --git a/centrallix/htmlgen/htdrv_textbutton.c b/centrallix/htmlgen/htdrv_textbutton.c index 92c07357b..66b7e0408 100644 --- a/centrallix/htmlgen/htdrv_textbutton.c +++ b/centrallix/htmlgen/htdrv_textbutton.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "ht_render.h" #include "obj.h" #include "cxlib/mtask.h" @@ -75,6 +76,7 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) char border_color[64]; char image_position[16]; /* top, left, right, bottom */ char image[OBJSYS_MAX_PATH]; + bool has_image; char h_align[16]; int image_width=0, image_height=0, image_margin=0; @@ -137,9 +139,15 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) /** Image source **/ if (wgtrGetPropertyValue(tree,"image",DATA_T_STRING,POD(&ptr)) != 0) + { strcpy(image, ""); + has_image = false; + } else + { strtcpy(image, ptr, sizeof(image)); + has_image = true; + } /** Image sizing **/ if (wgtrGetPropertyValue(tree,"image_width",DATA_T_INTEGER, POD(&image_width)) != 0) @@ -205,33 +213,39 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) htrAddScriptInclude(s, "/sys/js/htdrv_textbutton.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - /** Initial CSS styles **/ + /** Calculate size adjustment. **/ + const int adj = (2 * box_offset) + 1; + + /** Write CSS for the container that will hold the button. **/ htrAddStylesheetItem_va(s, "\t#tb%POSpane { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "%[HEIGHT:"ht_flex_format"; %]" - "Z-INDEX:%POS; " - "OVERFLOW:hidden; " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "z-index:%POS; " + "overflow:hidden; " "display:table; " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w-1-2*box_offset, ht_get_total_w(tree), ht_get_fl_w(tree)), - (h>=0), ht_flex(h-1-2*box_offset, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w - adj, tree), + (h >= 0), ht_flex_h(h - adj, tree), z ); - // CSS button click animation (replaces manual the JS implementation). + + /** Button click animation. **/ if (is_enabled) { htrAddStylesheetItem_va(s, "\t#tb%POSpane:active { transform: translate(1px, 1px); }\n", id ); } + + /** Write CSS for the button content, inside the border. **/ htrAddStylesheetItem_va(s, "\t#tb%POSpane .cell { " "height:100%%; " @@ -260,14 +274,20 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) bgstyle ); - /** CSS for image on the button **/ - if (image[0] && (image_width || image_height || image_margin)) + /** Write CSS for image on the button. **/ + if (has_image && (image_width != 0 || image_height != 0 || image_margin != 0)) { - htrAddStylesheetItem_va(s, "\t#tb%POSpane img { %[height:%POSpx; %]%[width:%POSpx; %]%[margin:%POSpx;%] }\n", - id, - image_height, image_height, - image_width, image_width, - image_margin, image_margin); + htrAddStylesheetItem_va(s, + "\t#tb%POSpane img { " + "%[height:%POSpx; %]" + "%[width:%POSpx; %]" + "%[margin:%POSpx; %]" + "}\n", + id, + (image_height != 0), image_height, + (image_width != 0), image_width, + (image_margin != 0), image_margin + ); } #if 00 @@ -291,30 +311,56 @@ httbtnRender(pHtSession s, pWgtrNode tree, int z) /** We need two DIVs here because of a long-outstanding Firefox bug :( **/ htrAddBodyItem_va(s, - "
" - "
" - "%[
%]" - "%[%]" + "
" + "
" + "%[
%]" + "%[%]" "%STR&HTE" - "%[%]" - "%[
%]" + "%[%]" + "%[
%]" "
" "
", id, - (image[0] && !strcmp(image_position, "top")), image, - (image[0] && !strcmp(image_position, "left")), image, + (has_image && strcmp(image_position, "top") == 0), image, + (has_image && strcmp(image_position, "left") == 0), image, text, - (image[0] && !strcmp(image_position, "right")), image, - (image[0] && !strcmp(image_position, "bottom")), image + (has_image && strcmp(image_position, "right") == 0), image, + (has_image && strcmp(image_position, "bottom") == 0), image ); /** Script initialization call. **/ //htrAddScriptInit_va(s, " tb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), span:document.getElementById(\"tb%POSspan\"), ena:%INT, c1:\"%STR&JSSTR\", c2:\"%STR&JSSTR\", dc1:\"%STR&JSSTR\", top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR'});\n", //name, id, is_enabled, fgcolor1, fgcolor2, disable_color, w, h, is_ts, name, text); - htrAddScriptInit_va(s, " tb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), ena:%INT, c1:\"%STR&JSSTR\", c2:\"%STR&JSSTR\", dc1:\"%STR&JSSTR\", top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR'});\n", - name, is_enabled, fgcolor1, fgcolor2, disable_color, w, h, is_ts, name, text); + htrAddScriptInit_va(s, + "tb_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "ena:%INT, " + "c1:'%STR&JSSTR', " + "c2:'%STR&JSSTR', " + "dc1:'%STR&JSSTR', " + "top:null, " + "bottom:null, " + "right:null, " + "left:null, " + "width:%INT, " + "height:%INT, " + "tristate:%INT, " + "name:'%STR&SYM', " + "text:'%STR&JSSTR', " + "});\n", + name, + is_enabled, + fgcolor1, + fgcolor2, + disable_color, + w, + h, + is_ts, + name, + text + ); - /** Add the event handling scripts **/ + /** Add event handlers. **/ htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tb", "tb_mousedown"); htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tb", "tb_mouseup"); htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tb", "tb_mouseover"); diff --git a/centrallix/htmlgen/htdrv_treeview.c b/centrallix/htmlgen/htdrv_treeview.c index 05a2bae8f..2e3e44cbe 100644 --- a/centrallix/htmlgen/htdrv_treeview.c +++ b/centrallix/htmlgen/htdrv_treeview.c @@ -174,18 +174,18 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s, "\t#tv%POSroot { " - "POSITION:absolute; " - "VISIBILITY:%STR; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, (show_root) ? "inherit" : "hidden", - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), z ); } @@ -221,32 +221,31 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) else { htrAddBodyItem_va(s, - "
" - "" + "" " %STR&HTE" - "
\n", - id, /** Class **/ - id, /** ID **/ + "
\n", + id, /* class */ + id, /* id */ (show_root) ? "inherit" : "hidden", - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), z, (*icon) ? icon : "/sys/images/ico02b.gif", src ); htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "tv%POSload", id, NULL, ""); - /*htrAddBodyItem_va(s, "
\n",id);*/ } /** Event handler for click-on-url **/ diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index 02d32ec6d..63247cfaf 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -390,43 +390,40 @@ int htruleRegister(char* ruletype, ...); *** *** @param size The original size of the ui element. *** @param total The total size of the ui element's container. - *** @param flex The flexibility of the ui element. It is strongly recomended + *** @param flex The flexibility of the ui element. It is strongly recommended *** to generate this with an ht_get_fl function call. *** @returns Several values to serve as parameters for a qprintf call. ***/ #define ht_flex(size, total, flex) (size), (total), (flex) /** ====[ Macros for getting total container size ]==== **/ -int ht_get_total_w__INTERNAL(pWgtrNode widget); -int ht_get_total_h__INTERNAL(pWgtrNode widget); +int ht_get_parent_w__INTERNAL(pWgtrNode widget); +int ht_get_parent_h__INTERNAL(pWgtrNode widget); -#define ht_get_total_w(widget) ht_get_total_w__INTERNAL(widget) -#define ht_get_total_h(widget) ht_get_total_h__INTERNAL(widget) - -// #define ht_get_total_w(widget) ((widget)->Parent->width - (widget)->Parent->left - (widget)->Parent->right) -// #define ht_get_total_h(widget) ((widget)->Parent->height - (widget)->Parent->top - (widget)->Parent->bottom) +#define ht_get_parent_w(widget) ht_get_parent_w__INTERNAL(widget) +#define ht_get_parent_h(widget) ht_get_parent_h__INTERNAL(widget) /** ====[ Macros for getting total flexibilities ]==== **/ /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the x direction. ***/ -#define ht_get_fl_x(widget) ((widget)->total_fl_x) +#define ht_get_fl_x(widget) ((widget)->fl_scale_x) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the y direction. ***/ -#define ht_get_fl_y(widget) ((widget)->total_fl_y) +#define ht_get_fl_y(widget) ((widget)->fl_scale_y) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the width direction. ***/ -#define ht_get_fl_w(widget) ((widget)->total_fl_w) +#define ht_get_fl_w(widget) ((widget)->fl_scale_w) /*** @param widget The widget to be queried. *** @returns The flexibility of the widget in the height direction. ***/ -#define ht_get_fl_h(widget) ((widget)->total_fl_h) +#define ht_get_fl_h(widget) ((widget)->fl_scale_h) /*** @brief A shortcut function to get the flexibility when writing the *** LEFT CSS attribute. @@ -444,22 +441,10 @@ int ht_get_total_h__INTERNAL(pWgtrNode widget); /** ====[ Macros for being lazy ]==== **/ -#define ht_flex_x(x, widget) ht_flex(x, ht_get_total_w(widget), ht_get_fl_x(widget)) -#define ht_flex_y(y, widget) ht_flex(y, ht_get_total_h(widget), ht_get_fl_y(widget)) -#define ht_flex_w(w, widget) ht_flex(w, ht_get_total_w(widget), ht_get_fl_w(widget)) -#define ht_flex_h(h, widget) ht_flex(h, ht_get_total_h(widget), ht_get_fl_h(widget)) - -#define ht_flex_format_all \ - "LEFT:"ht_flex_format"; " \ - "TOP:"ht_flex_format"; " \ - "WIDTH:"ht_flex_format"; " \ - "HEIGHT:"ht_flex_format"; " \ - -#define ht_flex_all(x, y, w, h, widget) \ - ht_flex_x(x, widget), \ - ht_flex_y(y, widget), \ - ht_flex_w(w, widget), \ - ht_flex_h(h, widget) \ +#define ht_flex_x(x, widget) ht_flex(x, ht_get_parent_w(widget), ht_get_fl_x(widget)) +#define ht_flex_y(y, widget) ht_flex(y, ht_get_parent_h(widget), ht_get_fl_y(widget)) +#define ht_flex_w(w, widget) ht_flex(w, ht_get_parent_w(widget), ht_get_fl_w(widget)) +#define ht_flex_h(h, widget) ht_flex(h, ht_get_parent_h(widget), ht_get_fl_h(widget)) // Workaround because -lm isn't passed to my editor and this was the easiest way to fix it. // This code should not appear in a pull request. If you are a code reviewer, remove this @@ -469,4 +454,3 @@ int ht_get_total_h__INTERNAL(pWgtrNode widget); #endif #endif /* _HT_RENDER_H */ - diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index d9a89b1dc..fd296ead3 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -103,9 +103,9 @@ typedef struct _WN int pre_x, pre_y, pre_width, pre_height; /** pre-layout geom. **/ int fl_x, fl_y, fl_width, fl_height;/** Flexibilities as specified by the designer **/ double fx, fy, fw, fh; /** internal flexibility calculations **/ - double total_fl_x, total_fl_y; /** Total flexiblities as calculated for this layout. */ - double total_fl_w, total_fl_h; /** Responsive CSS adjustment weights for width and height */ - int parent_w, parent_h; /** The expected size of the parent container */ + double fl_scale_x, fl_scale_y; /** Scaled x and y flexibilities calculated for this layout. */ + double fl_scale_w, fl_scale_h; /** Scaled w and h flexibilities calculated for this layout. */ + int fl_parent_w, fl_parent_h; /** The expected size of the parent container, used when it flexes. */ int min_width, min_height; /** absolute minimums **/ int x, y, width, height; /** actual geometry **/ int top, bottom, left, right; /** container offsets **/ diff --git a/centrallix/wgtr/wgtr.c b/centrallix/wgtr/wgtr.c index 5723c67eb..0f19ffc41 100755 --- a/centrallix/wgtr/wgtr.c +++ b/centrallix/wgtr/wgtr.c @@ -1306,17 +1306,18 @@ wgtrGetPropertyType(pWgtrNode widget, char* name) pObjProperty prop; ASSERTMAGIC(widget, MGK_WGTR); - if (!strcmp(name, "name")) return DATA_T_STRING; - else if (!strcmp(name, "outer_type")) return DATA_T_STRING; + if (!strcmp(name, "name") || !strcmp(name, "outer_type")) + return DATA_T_STRING; else if (!strcmp(name, "x") || !strcmp(name, "y") || !strcmp(name, "width") || !strcmp(name, "height") || !strcmp(name, "r_x") || !strcmp(name, "r_y") || !strcmp(name, "r_width") || !strcmp(name, "r_height") || !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height") || - !strcmp(name, "parent_w") || !strcmp(name, "parent_h")) + !strcmp(name, "fl_parent_w") || !strcmp(name, "fl_parent_h")) return DATA_T_INTEGER; - else if (!strcmp(name, "total_fl_x") || !strcmp(name, "total_fl_y") || - !strcmp(name, "total_fl_w") || !strcmp(name, "total_fl_h") || + else if (!strcmp(name, "fl_scale_x") || !strcmp(name, "fl_scale_y") || + !strcmp(name, "fl_scale_w") || !strcmp(name, "fl_scale_h") || !strcmp(name, "fx") || !strcmp(name, "fy") || !strcmp(name, "fw") || !strcmp(name, "fh")) return DATA_T_DOUBLE; + count = xaCount(&(widget->Properties)); for (i=0;iInteger = widget->fl_y; return 0; } if (!strcmp(name+3, "width")) { val->Integer = widget->fl_width; return 0; } if (!strcmp(name+3, "height")) { val->Integer = widget->fl_height; return 0; } - } - else if (!strncmp(name, "parent_", 7)) - { - if (!strcmp(name+7, "w")) { val->Integer = widget->parent_w; return 0; } - if (!strcmp(name+7, "h")) { val->Integer = widget->parent_h; return 0; } + if (!strcmp(name+3, "parent_w")) { val->Integer = widget->fl_parent_w; return 0; } + if (!strcmp(name+3, "parent_h")) { val->Integer = widget->fl_parent_h; return 0; } } } - if (datatype == DATA_T_DOUBLE) + else if (datatype == DATA_T_DOUBLE) { - if (!strncmp(name, "f", 1)) - { - if (!strcmp(name+1, "x")) { val->Double = widget->fx; return 0; } + if (!strncmp(name, "fl_scale_", 9)) + { + if (!strcmp(name+9, "x")) { val->Double = (double)widget->fl_scale_x; return 0; } + else if (!strcmp(name+9, "y")) { val->Double = (double)widget->fl_scale_y; return 0; } + else if (!strcmp(name+9, "w")) { val->Double = (double)widget->fl_scale_w; return 0; } + else if (!strcmp(name+9, "h")) { val->Double = (double)widget->fl_scale_h; return 0; } + } + else if (!strncmp(name, "f", 1)) + { + if (!strcmp(name+1, "x")) { val->Double = widget->fx; return 0; } else if (!strcmp(name+1, "y")) { val->Double = widget->fy; return 0; } else if (!strcmp(name+1, "w")) { val->Double = widget->fw; return 0; } else if (!strcmp(name+1, "h")) { val->Double = widget->fh; return 0; } } - else if (!strncmp(name, "total_fl_", 9)) - { - if (!strcmp(name+9, "x")) { val->Double = (double)widget->total_fl_x; return 0; } - else if (!strcmp(name+9, "y")) { val->Double = (double)widget->total_fl_y; return 0; } - else if (!strcmp(name+9, "w")) { val->Double = (double)widget->total_fl_w; return 0; } - else if (!strcmp(name+9, "h")) { val->Double = (double)widget->total_fl_h; return 0; } - } } else if (datatype == DATA_T_STRING) { @@ -1598,7 +1596,7 @@ wgtrNewNode( char* name, char* type, pObjSession s, node->fl_y = fly; node->fl_width = flwidth; node->fl_height = flheight; - node->parent_h = node->parent_w = -1; + node->fl_parent_h = node->fl_parent_w = -1; node->ObjSession = s; node->Parent = NULL; node->min_height = node->min_width = 0; @@ -2600,5 +2598,3 @@ wgtrGetNamespace(pWgtrNode widget) { return widget->Namespace; } - - From 9da89e3e91a5218427a93d9f4a7968602ca9b2d7 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:18:09 -0700 Subject: [PATCH 023/107] Update testing apps. Clean up some apps. --- centrallix-os/samples/autoscale_test.app | 36 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/centrallix-os/samples/autoscale_test.app b/centrallix-os/samples/autoscale_test.app index bcaace5c6..d5965395c 100644 --- a/centrallix-os/samples/autoscale_test.app +++ b/centrallix-os/samples/autoscale_test.app @@ -6,13 +6,13 @@ MyPage "widget/page" textcolor = "#00f8ff"; width = 1000; height = 1000; - + auto "widget/hbox" { x=100; y=50; width=900; height=750; spacing=20; row_height=300; fl_width=100; fl_height=100; - + pane0 "widget/pane" { fl_width=100; fl_height=100; width=190; height=180; bgcolor = "#9cf"; } // x=100; y=50; pane1 "widget/pane" { fl_width=100; fl_height=100; width=130; height=180; bgcolor = "#ccc"; } // x=305; y=50; pane2 "widget/pane" { fl_width=100; fl_height=100; width=80; height=320; bgcolor = "#f99"; } // x=455; y=50; @@ -24,48 +24,62 @@ MyPage "widget/page" pane8 "widget/pane" { fl_width=100; fl_height=100; width=110; height=200; bgcolor = "#fc9"; } // x=615; y=390; pane9 "widget/pane" { fl_width=100; fl_height=100; width=130; height=120; bgcolor = "#cf9"; } // x=745; y=390; } - + // pane0 "widget/pane" { x=100; y=50; width=190; height=180; bgcolor = "#9cf"; } // pane1 "widget/pane" { x=305; y=50; width=130; height=180; bgcolor = "#ccc"; } // pane2 "widget/pane" { x=455; y=50; width=80; height=320; bgcolor = "#f99"; } // pane3 "widget/pane" { x=555; y=50; width=150; height=140; bgcolor = "#9f9"; } - // pane4 "widget/pane" { x=725; y=50; width= 90; height=240; bgcolor = "#99f"; } + // pane4 "widget/pane" { x=725; y=50; width=90; height=240; bgcolor = "#99f"; } // pane5 "widget/pane" { x=80; y=390; width=230; height=100; bgcolor = "#ff9"; } // pane6 "widget/pane" { x=325; y=390; width=80; height=200; bgcolor = "#f9f"; } // pane7 "widget/pane" { x=425; y=390; width=170; height=220; bgcolor = "#9ff"; } // pane8 "widget/pane" { x=615; y=390; width=110; height=200; bgcolor = "#fc9"; } // pane9 "widget/pane" { x=745; y=390; width=130; height=120; bgcolor = "#cf9"; } - + paneA "widget/pane" { x=40; y=680; width=890; height=220; bgcolor = "#620"; } - + + b "widget/button" + { + x=80; y=700; + width=40; height=20; + + type = "text"; + text = "text button"; + bgcolor = "#aee"; + fgcolor1 = "white"; + fgcolor2 = "red"; + disable_color = "grey"; + enabled = yes; + } + // Outline the visible area. top_left0 "widget/pane" { x=0; y=0; width=10; height=10; bgcolor = "#f00"; } top_right0 "widget/pane" { x=990; y=0; width=10; height=10; bgcolor = "#ff0"; } bottom_left0 "widget/pane" { x=0; y=990; width=10; height=10; bgcolor = "#0f0"; } bottom_right0 "widget/pane" { x=990; y=990; width=10; height=10; bgcolor = "#00f"; } - + // Advance markers. top_left1 "widget/pane" { x=100; y=100; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#a00"; } top_right1 "widget/pane" { x=890; y=100; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#aa0"; } bottom_left1 "widget/pane" { x=100; y=890; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#0a0"; } bottom_right1 "widget/pane" { x=890; y=890; width=10; height=10; fl_x=25; fl_y=25; fl_width=25; fl_height=25; bgcolor = "#00a"; } - + // Interior markers. top_left2 "widget/pane" { x=250; y=250; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#700"; } top_right2 "widget/pane" { x=740; y=250; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#770"; } bottom_left2 "widget/pane" { x=250; y=740; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#070"; } bottom_right2 "widget/pane" { x=740; y=740; width=10; height=10; fl_x=100; fl_y=100; fl_width=25; fl_height=25; bgcolor = "#007"; } - + // Deep interior markers. top_left3 "widget/pane" { x=400; y=400; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#500"; } top_right3 "widget/pane" { x=590; y=400; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#550"; } bottom_left3 "widget/pane" { x=400; y=590; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#050"; } bottom_right3 "widget/pane" { x=590; y=590; width=10; height=10; fl_x=25; fl_y=25; fl_width=100; fl_height=100; bgcolor = "#005"; } - + // Center marker. center "widget/pane" { x=450; y=450; width=100; height=100; bgcolor = "orange"; centerer "widget/pane" { x=25; y=25; width=50; height=50; bgcolor = "purple"; } // Debug - } + } } \ No newline at end of file From a74257653f1b983b3e59deb1c2d9bf0684111b35 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:18:38 -0700 Subject: [PATCH 024/107] Add tools to ht_geom_dom1html. Add Math.clamp() and Math.isBetween(). Add getParentSize(), getParentW(), and getParentH(). Refactor getRelativeX/Y/W/H() to call the new getRelative(). Refactor setRelativeX/Y/W/H() to call the new setRelative(). Add fast_setRelativeX/Y(). Add setResponsiveX/Y/W/H() using a new shared setResponsive(). Add responsiveness to moveTo() with the new functions. Fix style guide mistakes. Clean up. --- centrallix-os/sys/js/ht_geom_dom1html.js | 232 ++++++++++++++++++++--- 1 file changed, 204 insertions(+), 28 deletions(-) diff --git a/centrallix-os/sys/js/ht_geom_dom1html.js b/centrallix-os/sys/js/ht_geom_dom1html.js index f137d5dba..71d29db99 100644 --- a/centrallix-os/sys/js/ht_geom_dom1html.js +++ b/centrallix-os/sys/js/ht_geom_dom1html.js @@ -11,34 +11,61 @@ // Cross browser Geometry DOM1HTML +// Add some useful functions to Math that will be needed elsewhere. +// Math.clamp = (min, val, max) => Math.min(Math.max(min, val), max); +// Math.isBetween = (lowerBound, num, upperBound) => lowerBound < num && num < upperBound; + +// Dev debug versions. +Math.clamp = (min, val, max) => + { + if (min > max || isNaN(min) || isNaN(val) || isNaN(max)) + { + console.warn(`Math.clamp(${min}, ${val}, ${max});`); + console.trace(); + } + return Math.min(Math.max(min, val), max); + } +Math.isBetween = (lowerBound, num, upperBound) => + { + if (lowerBound > upperBound || isNaN(lowerBound) || isNaN(num) || isNaN(upperBound)) + console.warn(`Math.isBetween(${lowerBound}, ${num}, ${upperBound});`); + return lowerBound < num && num < upperBound; + } + + + /*** Experimental system for turning off clipping CSS. *** The clip values are still stored and can be queried *** for legacy compatibility, but they will not output *** any clip rectangles or clip paths in the CSS or HTML. ***/ /** Ensure clipping is disabled for a layer / HTML node. **/ -function disableClippingCSS(l) { +function disableClippingCSS(l) + { console.log(`Turning off clipping for ${l.clip.obj.id}.`); l.clip.noclip = true; updateClippingCSS(l); -} + } /** Ensure clipping is enabled for a layer / HTML node. **/ -function enableClippingCSS(l) { +function enableClippingCSS(l) + { l.clip.noclip = false; updateClippingCSS(l); -} + } /** Update clipping without changing any specific values. **/ -function updateClippingCSS(l) { +function updateClippingCSS(l) + { setClipTop(l, getClipTop(l)); -} + } /** Debug function for finding clipped dom nodes. **/ -function getClipped() { +function getClipped() + { return Array - .from(Window.clipped) - .filter(id=>id) + .from(Window.clipped) + .filter(id=>id) .map(id=>document.getElementById(id)); -} + } // Clip Width function getClipWidth(l) @@ -236,43 +263,192 @@ function getpageYOffset() return window.pageYOffset; } -function getRelativeX(l) +/*** Get the size of a DOM node's parent container. + *** + *** @param l The DOM node. + *** @returns The width and height of the parent container. + ***/ +function getParentSize(l) + { + const parentRect = l.parentNode.getBoundingClientRect(); + return { width: parentRect.width, height: parentRect.height }; + } + +/*** Get the width of a DOM node's parent container. + *** + *** @param l The DOM node. + *** @returns The width of the parent container. + ***/ +function getParentW(l) + { + return getParentSize(l).width; + } + +/*** Get the height of a DOM node's parent container. + *** + *** @param l The DOM node. + *** @returns The height of the parent container. + ***/ +function getParentH(l) { - if (l.__pg_left != null) return l.__pg_left; - var left = parseInt(pg_get_style(l,'left')); - l.__pg_left = isNaN(left)?0:left; - return l.__pg_left; + return getParentSize(l).height; } -function setRelativeX(l, value) + +/*** Problem: + *** If the programmer calls setRelativeX() (or a similar function, such as moveTo() or moveBy()), + *** they might be using a value they got from the server, based on the resolution when the page + *** was first loaded. However, they might also be using a value they got dynamically by calling + *** some function to check the actual size of an element. Previously, this distinction did not + *** matter because these values would be the same. However, now that pages can be resized on the + *** client, it does matter. + ***/ + + +/*** We ignore the current value of __pg_left in the following functions even + *** though it might be correct and faster than querying the DOM. However, the + *** layout may have changed since last time, so we always requery the DOM. + ***/ +function getRelative(l, d) { - pg_set_style(l,'left',(l.__pg_left = parseInt(value))); - return l.__pg_left; + const val = parseInt(pg_get_style(l, d, NaN)); + return l['__pg_' + d] = (isNaN(val)) ? 0 : val; } -function getRelativeY(l) +function getRelativeX(l) { return getRelative(l, 'left'); } +function getRelativeY(l) { return getRelative(l, 'top'); } +function getRelativeW(l) { return getRelative(l, 'width'); } +function getRelativeH(l) { return getRelative(l, 'height'); } + +/*** Sets the location of a DOM node relative to its parent container. + *** + *** @param l The DOM node being set. (Assumed to be defined.) + *** @param value The new location. This can be a CSS string. + *** @param {'left'|'top'|'width'|'height'} d The dimension being set. + ***/ +function setRelative(l, value, d) { - if (l.__pg_top != null) return l.__pg_top; - return (l.__pg_top = parseInt(pg_get_style(l,'top'))); + /** Convert the value to a number, if possible. **/ + const parsedValue = parseInt(value); + if (!isNaN(parsedValue)) value = parsedValue; + + pg_set_style(l, d, value); + l['__pg_' + d + '_style'] = value; + return l['__pg_' + d] = parseInt(pg_get_style(l, d)); } -function setRelativeY(l, value) +function setRelativeX(l, value) { return setRelative(l, value, 'left'); } +function setRelativeY(l, value) { return setRelative(l, value, 'top'); } +function setRelativeW(l, value) { return setRelative(l, value, 'width'); } +function setRelativeH(l, value) { return setRelative(l, value, 'height'); } + +/*** Use these functions only in situations where performances is required + *** and you (a) value does not need to be parsed and (b) you do not need + *** the return value of the function. + ***/ +function fast_setRelativeX(l, value) { - pg_set_style(l,'top',(l.__pg_top = parseInt(value))); - return l.__pg_top; + pg_set_style(l, 'left', value); + l['__pg_x_style'] = value; } +function fast_setRelativeY(l, value) + { + pg_set_style(l, 'top', value); + l['__pg_y_style'] = value; + } + +/*** Sets a dimension of a DOM element using coordinates in the server + *** generated adaptive layout. It is RECOMMENDED to call a specific sub- + *** function (aka. setResponsiveX(), setResponsiveY(), etc.) instead of + *** calling this function directly to avoid passing dimension directly. + *** + *** WARNING: Ensure that any value passed is calculated ENTIRELY using + *** values from the server (e.g. widget properties) and no values from + *** real page dimensions are used, as these change when the page is + *** resized after being loaded for the first time. + *** + *** @param l The DOM node being set. (Assumed to be defined.) + *** @param value The new location in server-side px. This value must be + *** parseable as a number. + *** @param {'x'|'y'|'w'|'h'} d The letter for the dimension being set. + ***/ +function setResponsive(l, value, d) { + /** Convert the value to a number, if possible. **/ + const parsedValue = parseInt(value); + if (!isNaN(parsedValue)) value = parsedValue; + + /** Server-layout values are always numbers. **/ + if (typeof(value) !== 'number') + { + console.warn(`setResponsive(${l.id}, ?, '${d}'): Expected value to be a parseable number but got:`, value); + return value; + } + + /** The flexibility specified by the server. **/ + var fl_scale = l['__fl_scale_' + d] ?? wgtrGetServerProperty(l, 'fl_scale_' + d); + if (fl_scale == undefined || fl_scale == null) + { + /** The server did not specify a flexibility, even though one was expected. **/ + const warningMsg = 'setResponsive() - FAIL: Missing ' + ((wgtrIsNode(l)) ? 'wgtr.' : '__') + 'fl_scale_' + d; + console.warn(warningMsg, l); + fl_scale = 0; + } + + /** Inflexible elements don't need to be responsive. **/ + if (fl_scale <= 0) return setRelative(l, value, d); + + /** The parent width expected by the server in the adaptive layout. **/ + var d2 = d; + if (d2 == 'x') d2 = 'w'; + if (d2 == 'y') d2 = 'h'; + + var fl_parent = l['__fl_parent_' + d2] ?? wgtrGetServerProperty(l, 'fl_parent_' + d2); + if (fl_parent == undefined || fl_parent == null) + { + const warningMsg = 'setResponsive() - FAIL: Missing ' + ((wgtrIsNode(l)) ? 'wgtr.' : '__') + 'fl_parent_' + d2; + console.warn(warningMsg, l); + } + + /** Generate and set the CSS. **/ + const css = `calc(${value}px + (100% - ${fl_parent}px) * ${fl_scale})`; + const prop = { x:'left', y:'top', w:'width', h:'height' }[d]; + return setRelative(l, css, prop); +} +/** Call these functions instead of calling setResponsive() directly, which leads to less readable code. **/ +function setResponsiveX(l, value) { return setResponsive(l, value, 'x'); } +function setResponsiveY(l, value) { return setResponsive(l, value, 'y'); } +function setResponsiveW(l, value) { return setResponsive(l, value, 'w'); } +function setResponsiveH(l, value) { return setResponsive(l, value, 'h'); } + +/** Moves a DOM node to a location within the window. **/ function moveToAbsolute(l, x, y) { setPageX(l,x); setPageY(l,y); } -function moveTo(l, x, y) +/*** Moves a DOM node to a location inside it's parent container. + *** + *** @param l The DOM node being moved. + *** @param x The new x coordinate. Can be a CSS string (if responsive is false). + *** @param y The new y coordinate. Can be a CSS string (if responsive is false). + *** @param responsive Whether the given coordinates should be treated as + *** adaptive, 'server-side', coordinates where setResponsive() + *** should be invoked to give them responsive design. + ***/ +function moveTo(l, x, y, responsive = false) { - //pg_set_style_string(this,'position','absolute'); - setRelativeX(l,x); - setRelativeY(l,y); + if (responsive) + { + setResponsiveX(l, x); + setResponsiveY(l, x); + } + else + { + setRelativeX(l, x); + setRelativeY(l, y); + } } From 9612eb4dddbdc82eeed099feba183d18bc0ad86d Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:22:34 -0700 Subject: [PATCH 025/107] Add tools for widget development and debugging. Add support for more edge cases with undefined values to wgtrGetServerProperty(). Add the Log action (and docs). Add the ReloadPage action (and docs). Improve documentation for the Alert widget. --- centrallix-doc/Widgets/widgets.xml | 6 +++++- centrallix-os/sys/js/ht_utils_wgtr.js | 2 +- centrallix-os/sys/js/htdrv_page.js | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index b6b50afde..a8ab43f21 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -2818,13 +2818,17 @@ osrc1 "widget/osrc" - Sends an alert widget. + Sends an alert widget. Set the 'Message' to specify a text string that should appear in the alert. Closes the page. Starts a new app in a new window. + Logs data to the console (using console.log()), for testing and debugging. Set the 'Message' to specify a text string that should appear in the log. + Loads the page. + + Reloads the page in the user's browser. Note: This event forces a reload, even if the original content could be loaded without one. diff --git a/centrallix-os/sys/js/ht_utils_wgtr.js b/centrallix-os/sys/js/ht_utils_wgtr.js index 1379bacf9..085992ee4 100644 --- a/centrallix-os/sys/js/ht_utils_wgtr.js +++ b/centrallix-os/sys/js/ht_utils_wgtr.js @@ -363,7 +363,7 @@ function wgtrIsUndefined(prop) // wgtrGetServerProperty() - return a server-supplied property value function wgtrGetServerProperty(node, prop_name, def) { - var val = node.__WgtrParams[prop_name]; + var val = node?.__WgtrParams?.[prop_name]; if (typeof val == 'undefined') return def; else if (typeof val == 'object' && val && val.exp) diff --git a/centrallix-os/sys/js/htdrv_page.js b/centrallix-os/sys/js/htdrv_page.js index 1da3ba1e5..85c58c5b5 100755 --- a/centrallix-os/sys/js/htdrv_page.js +++ b/centrallix-os/sys/js/htdrv_page.js @@ -1354,6 +1354,8 @@ function pg_init(l,a,gs,ct) //SETH: ?? ia.Add("Launch", pg_launch); ia.Add("Close", pg_close); ia.Add("Alert", pg_alert); + ia.Add("Log", pg_log); + ia.Add("ReloadPage", pg_reload_page); // Events var ie = window.ifcProbeAdd(ifEvent); @@ -1435,6 +1437,16 @@ function pg_alert(aparam) alert(aparam.Message); } +function pg_log({ Message }) + { + console.log(Message); + } + +function pg_reload_page() + { + window.location.reload(); + } + function pg_reveal_cb(e) { if (e.eventName == 'ObscureOK') From ba4dd5b772d8ce4d7fbd5d9a18a0994ba5a27fd0 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:23:47 -0700 Subject: [PATCH 026/107] Add a warning when using the pane resize action, which breaks responsiveness. --- centrallix-os/sys/js/htdrv_pane.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_pane.js b/centrallix-os/sys/js/htdrv_pane.js index 9489912e7..598e51feb 100644 --- a/centrallix-os/sys/js/htdrv_pane.js +++ b/centrallix-os/sys/js/htdrv_pane.js @@ -79,12 +79,9 @@ function pn_setbackground(aparam) function pn_action_resize(aparam) { - // I think this code is a better implementation of the following lines: - // var w = (aparam.Width) ?? pg_get_style(this, 'width'); - // var h = (aparam.Height) ?? pg_get_style(this, 'height'); - - var w = aparam.Width?aparam.Width:pg_get_style(this, 'width'); - var h = aparam.Height?aparam.Height:pg_get_style(this, 'height'); + console.warn('Resize action called which will probably break responsiveness.', this, aparam); + const w = (aparam.Width) ?? pg_get_style(this, 'width'); + const h = (aparam.Height) ?? pg_get_style(this, 'height'); resizeTo(this, w, h); } From 83ea0b91a67862c1f318413c0179fe5dcdaa5588 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:28:36 -0700 Subject: [PATCH 027/107] Make the point action responsive. Add htr_action_point() to ht_render.js to handle the point action responsively. Refactor pane and window widgets to use the htr_action_point(). Improve documentation and code clarity for htutil_point(). Improve documentation for the point action. --- centrallix-doc/Widgets/widgets.xml | 2 +- centrallix-os/sys/js/ht_render.js | 35 +++++++++++++++++++++++++ centrallix-os/sys/js/ht_utils_layers.js | 15 ++++++++--- centrallix-os/sys/js/htdrv_pane.js | 4 +-- centrallix-os/sys/js/htdrv_window.js | 4 +-- 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index a8ab43f21..3dbb50a24 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -629,7 +629,7 @@ checkbox_test "widget/page" Opens the window. If the parameter IsModal is set to 1, then the window becomes modal (only the window's contents are accessible to the user until the window is closed). If the parameter NoClose is set to 1, then the close button in the upper right corner of the window becomes inactive and the window will only close via the Close, SetVisibility, and ToggleVisibility actions. - Makes the window relocate to a side using a triangle (pop over). + Creates a triangular pointer on the edge of the window to point at a given (X,Y) coordinate. Opens a window like a pop-up. diff --git a/centrallix-os/sys/js/ht_render.js b/centrallix-os/sys/js/ht_render.js index 6b80b9ace..8d0163e10 100644 --- a/centrallix-os/sys/js/ht_render.js +++ b/centrallix-os/sys/js/ht_render.js @@ -819,6 +819,41 @@ function htr_stylize_element(element, widget, prefix, defaults) ); } +/*** When a point action is invoked the first time, a ResizeObserver is also + *** created to update the location of the point element if the document is + *** resized. The parameters from invocation of this function are saved and + *** reused to update the point each time a resize occurs. + */ +function htr_action_point(aparam) + { + const updatePoint = () => + { + /** Get the parameters from the resize observer object. **/ + const { X, Y, AtWidget, BorderColor, FillColor } = this.resizeObserver.aparam; + const { p1, p2 } = htutil_point(this, X, Y, AtWidget, BorderColor, FillColor, this.point1, this.point2); + this.point1 = p1; + this.point2 = p2; + } + + if (!this.resizeObserver) + { + /*** There isn't a resize observer yet, so create a new one that will + *** update the point when the document is resized. + *** Note: ResizeObserver provides a list of resized entries to the + *** callback (updatePoint()) and we ignore it because we only care + *** that something has been resized, htutil_point() handles the rest. + ***/ + const resizeObserver = this.resizeObserver = new ResizeObserver(updatePoint); + resizeObserver.observe(document.documentElement); + } + /** I really wanted to write the code below to show off how cleaver I am, but the if statement above is far more readable. **/ + // (!this.resizeObserver) && (this.resizeObserver = new ResizeObserver(updatePoint)).observe(document.documentElement); + + /** Save the parameters, then update the point. **/ + this.resizeObserver.aparam = aparam; + updatePoint(); + } + function htr_alert(obj,maxlevels) { alert(htr_obj_to_text(obj,0,maxlevels)); diff --git a/centrallix-os/sys/js/ht_utils_layers.js b/centrallix-os/sys/js/ht_utils_layers.js index 096754eb5..600db733f 100644 --- a/centrallix-os/sys/js/ht_utils_layers.js +++ b/centrallix-os/sys/js/ht_utils_layers.js @@ -20,6 +20,12 @@ function htutil_tag_images(d,t,l,ml) } } +/*** + *** @param bc BorderColor + *** @param fc FillColor + *** @param p1 Point 1 + *** @param p2 Point 2 + ***/ function htutil_point(wthis, x, y, at, bc, fc, p1, p2) { // Determine x/y to point at @@ -105,6 +111,7 @@ function htutil_point(wthis, x, y, at, bc, fc, p1, p2) } // Set the CSS to enable the point divs + const { top: wtop, left: wleft } = $(wthis).offset(); $(p1).css ({ "position": "absolute", @@ -114,8 +121,8 @@ function htutil_point(wthis, x, y, at, bc, fc, p1, p2) "border-style": "solid", "box-sizing": "border-box", "content": "", - "top": (top + $(wthis).offset().top) + "px", - "left": (left + $(wthis).offset().left) + "px", + "top": (top + wtop) + "px", + "left": (left + wleft) + "px", "border-color": c1, "visibility": "inherit", "z-index": htr_getzindex(wthis) + 1 @@ -129,8 +136,8 @@ function htutil_point(wthis, x, y, at, bc, fc, p1, p2) "border-style": "solid", "box-sizing": "border-box", "content": "", - "top": (top + doffs.y + $(wthis).offset().top) + "px", - "left": (left + doffs.x + $(wthis).offset().left) + "px", + "top": (top + doffs.y + wtop) + "px", + "left": (left + doffs.x + wleft) + "px", "border-color": c2, "visibility": "inherit", "z-index": htr_getzindex(wthis) + 2 diff --git a/centrallix-os/sys/js/htdrv_pane.js b/centrallix-os/sys/js/htdrv_pane.js index 598e51feb..854cee0e0 100644 --- a/centrallix-os/sys/js/htdrv_pane.js +++ b/centrallix-os/sys/js/htdrv_pane.js @@ -87,9 +87,7 @@ function pn_action_resize(aparam) function pn_action_point(aparam) { - var divs = htutil_point(this, aparam.X, aparam.Y, aparam.AtWidget, aparam.BorderColor, aparam.FillColor, this.point1, this.point2); - this.point1 = divs.p1; - this.point2 = divs.p2; + htr_action_point.call(this, aparam); } function pn_init(param) diff --git a/centrallix-os/sys/js/htdrv_window.js b/centrallix-os/sys/js/htdrv_window.js index b551430f4..6cdcf0da9 100644 --- a/centrallix-os/sys/js/htdrv_window.js +++ b/centrallix-os/sys/js/htdrv_window.js @@ -152,9 +152,7 @@ function wn_init(param) function wn_action_point(aparam) { - var divs = htutil_point(this, aparam.X, aparam.Y, aparam.AtWidget, aparam.BorderColor, aparam.FillColor, this.point1, this.point2); - this.point1 = divs.p1; - this.point2 = divs.p2; + htr_action_point.call(this, aparam); } // Popup - pops up a window in the way that a menu might pop up. From c75f3e0aa355a01b451cbf51ec72876da2d1c35b Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:31:33 -0700 Subject: [PATCH 028/107] Make the autolayout widget responsive. Update al_reflow() to create responsive CSS using the setResponsive() functions. Improve documentation for al_childresize(). Optimize and clean up inefficient code. --- centrallix-os/sys/js/htdrv_autolayout.js | 61 ++++++++++++++---------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_autolayout.js b/centrallix-os/sys/js/htdrv_autolayout.js index fabace1ca..427f42849 100644 --- a/centrallix-os/sys/js/htdrv_autolayout.js +++ b/centrallix-os/sys/js/htdrv_autolayout.js @@ -9,6 +9,15 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. +/*** This function works entirely using server-side dimensions. + *** Responsive dimensions are handled elsewhere. + *** + *** @param child The child to be resized. + *** @param oldw The previous width in server-side adaptive coordinates. + *** @param oldh The previous height in server-side adaptive coordinates. + *** @param neww The new width in server-side adaptive coordinates. + *** @param newh The new height in server-side adaptive coordinates. + ***/ function al_childresize(child, oldw, oldh, neww, newh) { if (oldw != neww || oldh != newh) @@ -37,29 +46,27 @@ function al_reflow_buildlist(node, children) } } +/*** Note: + *** This function gets all its values from server properties, so its generated + *** positions are in server layout. Thus, we convert them right before they + *** assigned to allow for responsive values. + ***/ function al_reflow() { // Get configuration var width = wgtrGetServerProperty(this,"width"); var height = wgtrGetServerProperty(this,"height"); - var spacing = wgtrGetServerProperty(this,"spacing"); - if (!spacing) spacing = 0; - var cellsize = wgtrGetServerProperty(this,"cellsize"); - if (!cellsize) cellsize = -1; - var align = wgtrGetServerProperty(this,"align"); - if (!align) align = "left"; - var justify_mode = wgtrGetServerProperty(this,"justify"); - if (!justify_mode) justify_mode = "none"; + var spacing = wgtrGetServerProperty(this,"spacing",0); + // var cellsize = wgtrGetServerProperty(this,"cellsize",-1); // Unused + var align = wgtrGetServerProperty(this,"align","left"); + // var justify_mode = wgtrGetServerProperty(this,"justify","none"); // Unused var type = "vbox"; if (wgtrGetServerProperty(this,"style") == "hbox" || wgtrGetType(this) == "widget/hbox") type = "hbox"; - var column_width; - if (type == "vbox") - column_width = wgtrGetServerProperty(this,"column_width"); + var column_width, row_height; + if (type == "vbox") column_width = wgtrGetServerProperty(this,"column_width"); + else row_height = wgtrGetServerProperty(this,"row_height"); if (!column_width) column_width = width; - var row_height; - if (type == "hbox") - row_height = wgtrGetServerProperty(this,"row_height"); if (!row_height) row_height = height; // Build the child list @@ -87,10 +94,9 @@ function al_reflow() for(var i=0; i width) { if (xo > 0 && row_height > 0 && row_offset + row_height*2 + spacing <= height) @@ -108,6 +114,7 @@ function al_reflow() } else if (type == 'vbox') { + const cheight = wgtrGetServerProperty(child, "height"); if (yo + cheight > height) { if (yo > 0 && column_width > 0 && column_offset + column_width*2 + spacing <= width) @@ -148,10 +155,9 @@ function al_reflow() for(var i=0; i width) { if (xo > 0 && row_height > 0 && row_offset + row_height*2 + spacing <= height) @@ -164,17 +170,19 @@ function al_reflow() } if (child.tagName) { - setRelativeX(child, xo + xalign); - if (wgtrGetServerProperty(child,"r_y") == -1) - setRelativeY(child, row_offset); + setResponsiveX(child, xo + xalign); + const r_y = wgtrGetServerProperty(child, "r_y"); + if (r_y == -1) + setResponsiveY(child, row_offset); else - setRelativeY(child, row_offset + wgtrGetServerProperty(child,"r_y")); + setResponsiveY(child, row_offset + r_y); } xo += cwidth; xo += spacing; } else if (type == 'vbox') { + const cheight = wgtrGetServerProperty(child, "height"); if (yo + cheight > height) { if (yo > 0 && column_width > 0 && column_offset + column_width*2 + spacing <= width) @@ -187,11 +195,12 @@ function al_reflow() } if (child.tagName) { - setRelativeY(child, yo + yalign); - if (wgtrGetServerProperty(child,"r_x") == -1) - setRelativeX(child, column_offset); + setResponsiveY(child, yo + yalign); + const r_x = wgtrGetServerProperty(child, "r_x"); + if (r_x == -1) + setResponsiveX(child, column_offset); else - setRelativeX(child, column_offset + wgtrGetServerProperty(child,"r_x")); + setResponsiveX(child, column_offset + r_x); } yo += cheight; yo += spacing; From db00d6b1aaf2557273f72f85592795fff92d7796 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Fri, 6 Feb 2026 17:39:10 -0700 Subject: [PATCH 029/107] Make the tab widget responsive. Rewrite C & JS code to use responsive styling. Fix a bug that allowed multiple tabs to be selected at once. Fix spelling errors. Clean up. --- centrallix-os/sys/js/htdrv_tab.js | 549 +++++++++++----------- centrallix/htmlgen/htdrv_tab.c | 753 +++++++++++++++++++++--------- 2 files changed, 809 insertions(+), 493 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_tab.js b/centrallix-os/sys/js/htdrv_tab.js index ebab8b6b6..96fd166e8 100644 --- a/centrallix-os/sys/js/htdrv_tab.js +++ b/centrallix-os/sys/js/htdrv_tab.js @@ -9,7 +9,6 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. - // Sets the value of the current tab (but not the appearance), without // triggering on-change events. function tc_set_tab_unwatched() @@ -27,139 +26,150 @@ function tc_set_tab_unwatched() // Makes the given tab current. function tc_makecurrent() { - var t; - if (this.tabctl.tloc != 4 && htr_getzindex(this.tab) > htr_getzindex(this.tabctl)) return 0; - for(var i=0;i htr_getzindex(t)) return 0; + for(let i = 0; i < tabs.length; i++) { - t = this.tabctl.tabs[i]; - if (t != this && (t.tabctl.tloc == 4 || htr_getzindex(t.tab) > htr_getzindex(this.tab))) + const cur = tabs[i]; + if (cur !== this && (cur.tabctl.tloc === 'None' || htr_getzindex(cur.tab) > htr_getzindex(tab))) { - htr_setzindex(t, htr_getzindex(this.tabctl) - 1); - htr_setvisibility(t, 'hidden'); - if (t.tabctl.tloc != 4) + htr_setzindex(cur, htr_getzindex(t) - 1); + htr_setvisibility(cur, 'hidden'); + if (cur.tabctl.tloc !== 'None') { - htr_setzindex(t.tab, htr_getzindex(this.tabctl) - 1); - t.tab.marker_image.src = '/sys/images/tab_lft3.gif'; - moveBy(t.tab, this.tabctl.xo, this.tabctl.yo); - //setClipItem(t.tab, t.tabctl.cl, getClipItem(t.tab, t.tabctl.cl) + t.tabctl.ci); - if (this.tabctl.inactive_bgColor) htr_setbgcolor(t.tab, this.tabctl.inactive_bgColor); - if (this.tabctl.inactive_bgnd) htr_setbgimage(t.tab, this.tabctl.inactive_bgnd); + htr_setzindex(cur.tab, htr_getzindex(t) - 1); + cur.tab.marker_image.src = '/sys/images/tab_lft3.gif'; + cur.tab.classList.remove('tab_selected'); + // moveBy(cur.tab, t.xo, t.yo); + if (t.inactive_bgColor) htr_setbgcolor(cur.tab, t.inactive_bgColor); + if (t.inactive_bgnd) htr_setbgimage(cur.tab, t.inactive_bgnd); } } } - htr_setzindex(this, htr_getzindex(this.tabctl) + 1); + htr_setzindex(this, htr_getzindex(t) + 1); htr_setvisibility(this,'inherit'); - if (this.tabctl.tloc != 4) + if (tloc !== 'None') { - if (this.tabctl.main_bgColor) htr_setbgcolor(this.tab, this.tabctl.main_bgColor); - if (this.tabctl.main_bgnd) htr_setbgimage(this.tab, this.tabctl.main_bgnd); - htr_setzindex(this.tab, htr_getzindex(this.tabctl) + 1); - this.tab.marker_image.src = '/sys/images/tab_lft2.gif'; - moveBy(this.tab, -this.tabctl.xo, -this.tabctl.yo); - //setClipItem(this.tab, this.tabctl.cl, getClipItem(this.tab, this.tabctl.cl) - this.tabctl.ci); + if (t.main_bgColor) htr_setbgcolor(tab, t.main_bgColor); + if (t.main_bgnd) htr_setbgimage(tab, t.main_bgnd); + htr_setzindex(tab, htr_getzindex(t) + 1); + tab.marker_image.src = '/sys/images/tab_lft2.gif'; + tab.classList.add('tab_selected'); + // moveBy(tab, -t.xo, -t.yo); } this.setTabUnwatched(); - this.tabctl.ifcProbe(ifEvent).Activate("TabChanged", {Selected:this.tabctl.selected, SelectedIndex:this.tabctl.selected_index}); + t.ifcProbe(ifEvent).Activate("TabChanged", { Selected:t.selected, SelectedIndex:t.selected_index }); } -function tc_makenotcurrent(t) +function tc_makenotcurrent(page) { - htr_setzindex(t,htr_getzindex(t.tabctl) - 1); - htr_setvisibility(t,'hidden'); - if (t.tabctl.tloc != 4) + const { tabctl, tab } = page; + + htr_setzindex(page, htr_getzindex(tabctl) - 1); + htr_setvisibility(page, 'hidden'); + + if (tabctl.tloc !== 'None') { - htr_setzindex(t.tab,htr_getzindex(t.tabctl) - 1); - t.tab.marker_image.src = '/sys/images/tab_lft3.gif'; - moveBy(t.tab, t.tabctl.xo, t.tabctl.yo); - //setClipItem(t.tab, t.tabctl.cl, getClipItem(t.tab, t.tabctl.cl) + t.tabctl.ci); - if (t.tabctl.inactive_bgColor) htr_setbgcolor(t.tab, t.tabctl.inactive_bgColor); - if (t.tabctl.inactive_bgnd) htr_setbgimage(t.tab, t.tabctl.inactive_bgnd); + htr_setzindex(page.tab,htr_getzindex(page.tabctl) - 1); + tab.marker_image.src = '/sys/images/tab_lft3.gif'; + tab.classList.remove('tab_selected'); + // moveBy(tab, tabctl.xo, tabctl.yo); + if (tabctl.inactive_bgColor) htr_setbgcolor(tab, tabctl.inactive_bgColor); + if (tabctl.inactive_bgnd) htr_setbgimage(tab, tabctl.inactive_bgnd); } } - -// Adds a new tab to the tab control -function tc_addtab(l_tab, l_page, l, nm, type,fieldname) + +/*** Adds a new tab to the tab control. This function deals with whether or + *** not that tab is selected as LITTLE AS POSSIBLE since that piece of state + *** should be handled elsewhere with functions like tc_makenotcurrent() or + *** tc_makecurrent(). + *** + *** @param l_tab The tab being added. + *** @param l_page The page to which the tab is being added. + *** @param l The layer where this change will occur. + *** @param nm The name of the tab. + ***/ +function tc_addtab(l_tab, l_page, l, nm, type, fieldname) { - var newx; - var newy; - if (!l_tab) l_tab = new Object(); + const tabctl = this, { tabs } = tabctl, { tloc, tab_h, tab_spacing } = l; + + let x, y; + if (!l_tab) l_tab = {}; l_page.tabname = nm; l_page.type = type; l_page.fieldname = fieldname; - l_page.tabindex = this.tabs.length+1; + l_page.tabindex = tabs.length + 1; htr_init_layer(l_page,l,'tc_pn'); ifc_init_widget(l_page); - if (l.tloc != 4) + + /** Calculate the location and flexibility to render the tab. **/ + if (tloc === 'None') + { + x = 0; + y = 0; + } + else { - htr_init_layer(l_tab,l,'tc'); - if (l.tloc == 0 || l.tloc == 1) // top or bottom + htr_init_layer(l_tab, l, 'tc'); + + /** Calculate x coordinate. **/ + if (tloc === 'Top' || tloc === 'Bottom') { - if (this.tabs.length > 0) + if (tabs.length > 0) { - //alert(htr_getphyswidth(this.tabs[this.tabs.length-1])); - newx = getPageX(this.tabs[this.tabs.length-1].tab) + $(this.tabs[this.tabs.length-1].tab).outerWidth() + 1; - if (htr_getvisibility(this.tabs[this.tabs.length-1]) == 'inherit') newx += l.xo; + const previous_tab = tabs[tabs.length - 1].tab; + x = getRelativeX(previous_tab) + $(previous_tab).outerWidth() + tab_spacing; } + else if (l.tab_fl_x) + /** Copy tabctl.style.left to avoid small but noticeable inconsistencies. **/ + setRelativeX(l_tab, tabctl.style.left); else - newx = getPageX(this); + /** Math for inflexible tabs do not suffer from inconsistencies. * */ + x = getRelativeX(tabctl); } - else if (l.tloc == 2) // left - newx = getPageX(this)- htr_getviswidth(l_tab) + 0; - else if (l.tloc == 3) // right - newx = getPageX(this) + htr_getviswidth(this) + 1; + else if (tloc === 'Left') + x = getRelativeX(tabctl) - htr_getviswidth(l_tab); + else if (tloc === 'Right') + x = getRelativeX(tabctl); // + htr_getviswidth(tabctl) // Included in xtoffset (see below) - if (l.tloc == 2 || l.tloc == 3) // left or right + /** Calculate y coordinate. **/ + if (tloc === 'Left' || tloc === 'Right') { - if (this.tabs.length > 0) + if (tabs.length > 0) { - newy = getPageY(this.tabs[this.tabs.length-1].tab) + 26; - if (htr_getvisibility(this.tabs[this.tabs.length-1]) == 'inherit') newy += l.yo; + const previous_tab = tabs[tabs.length - 1].tab; + y = getRelativeY(previous_tab) + tab_h + tab_spacing; } + else if (l.tab_fl_y) + /** Copy tabctl.style.top to avoid small but noticeable inconsistencies. **/ + setRelativeY(l_tab, tabctl.style.top); else - newy = getPageY(this); + /** Math for inflexible tabs do not suffer from inconsistencies. * */ + y = getRelativeY(tabctl); } - else if (l.tloc == 1) // bottom - newy = getPageY(this)+ htr_getvisheight(this) + 1; - else // top - newy = getPageY(this) - 24; - - // Clipping - switch(l.tloc) + else if (tloc === 'Bottom') + y = getRelativeY(tabctl); // + htr_getvisheight(tabctl) // Included in ytoffset (see below) + else // Top + y = getRelativeY(tabctl) - tab_h; + + /** Apply the same tab offsets used on the server. **/ + x += l.xtoffset; + y += l.ytoffset; + + /** Space out tab away from previous tab to account for borders. **/ + if (tabs.length > 0) { - case 0: // top - $(l_tab).css('clip', 'rect(-10px, ' + ($(l_tab).outerWidth()+10) + 'px, 25px, -10px)'); - break; - case 1: // bottom - $(l_tab).css('clip', 'rect(0px, ' + ($(l_tab).outerWidth()+10) + 'px, 35px, -10px)'); - break; - case 2: // left - $(l_tab).css('clip', 'rect(-10px, ' + ($(l_tab).outerWidth()) + 'px, 35px, -10px)'); - break; - case 3: // right - $(l_tab).css('clip', 'rect(-10px, ' + ($(l_tab).outerWidth()+10) + 'px, 35px, 0px)'); - break; + switch (tloc) + { + case 'Top': case 'Bottom': x += 2; break; + case 'Left': case 'Right': y += 2; break; + } } } - else - { - newx = 0; - newy = 0; - } - if (htr_getvisibility(l_page) != 'inherit') - { - if (l.tloc != 4) - { - newx += l.xo; - newy += l.yo; - //setClipItem(l_tab, l.cl, getClipItem(l_tab, l.cl) + l.ci); - if (l.inactive_bgColor) htr_setbgcolor(l_tab, l.inactive_bgColor); - else if (l.main_bgColor) htr_setbgcolor(l_tab, l.main_bgColor); - if (l.inactive_bgnd) htr_setbgimage(l_tab, l.inactive_bgnd); - else if (l.main_bgnd) htr_setbgimage(l_tab, l.main_bgnd); - } - } - else + if (htr_getvisibility(l_page) === 'inherit') { htr_unwatch(l,"selected","tc_selection_changed"); htr_unwatch(l,"selected_index","tc_selection_changed"); @@ -167,49 +177,60 @@ function tc_addtab(l_tab, l_page, l, nm, type,fieldname) l.selected_index = l_page.tabindex; l.current_tab = l_page; l.init_tab = l_page; - pg_addsched_fn(window,"pg_reveal_event",new Array(l_page,l_page,'Reveal'), 0); + pg_addsched_fn(window,"pg_reveal_event",[l_page,l_page,'Reveal'], 0); htr_watch(l,"selected", "tc_selection_changed"); htr_watch(l,"selected_index", "tc_selection_changed"); - if (l.tloc != 4) + if (tloc !== 'None') { if (l.main_bgColor) htr_setbgcolor(l_tab, l.main_bgColor); if (l.main_bgnd) htr_setbgimage(l_tab, l.main_bgnd); } } - if (l.tloc != 4) + + if (tloc !== 'None') { - var images = pg_images(l_tab); - for(var i=0;i this.tabs.length) return o; + if (tabindex < 1 || tabindex > tabs.length) return o; // okay to change tab. - //this.tabs[tabindex-1].makeCurrent(); + //tabs[tabindex-1].makeCurrent(); if (this.selchange_schedid) pg_delsched(this.selchange_schedid); - this.selchange_schedid = pg_addsched_fn(this,"ChangeSelection1", new Array(this.tabs[tabindex-1]), 0); + this.selchange_schedid = pg_addsched_fn(this, "ChangeSelection1", new Array(tabs[tabindex - 1]), 0); return n; } -function tc_action_set_tab(aparam) +function tc_action_set_tab({ Tab, TabIndex }) { - if (aparam.Tab) this.selected = aparam.Tab; - else if (aparam.TabIndex) this.selected_index = parseInt(aparam.TabIndex); + if (Tab) this.selected = Tab; + else if (TabIndex) this.selected_index = parseInt(TabIndex); } function tc_showcontainer() @@ -263,13 +286,13 @@ function tc_showcontainer() function tc_clear_tabs(tabs) { - for(var i=0;i "+vals[j]+" \n"; else - content = "\n
 "+vals[j]+" 
\n"; - tabparent = tc_direct_parent(tabs[i]); + content = + '\n\t' + + '' + + '' + + '
 ' + vals[j] + ' 
\n'; + + tabparent = tc_direct_parent(cur_tab); if(this.tc_layer_cache && this.tc_layer_cache.length >0) newtab = this.tc_layer_cache.pop(); else newtab = htr_new_layer(null,tabparent); - pageparent = tc_direct_parent(tabs[i].tabpage) + pageparent = tc_direct_parent(cur_tab.tabpage) newpage = htr_new_layer(null,pageparent); - newtab.marker_image = tabs[i].marker_image; + newtab.marker_image = cur_tab.marker_image; newtab.marker_image.src = '/sys/images/tab_lft3.gif'; $(newtab).find('span').text(' ' + htutil_encode(vals[j]) + ' '); //htr_write_content(newtab,content); @@ -343,8 +384,6 @@ function tc_updated(p1) htr_setvisibility(newpage,'inherit'); htr_setzindex(newtab,14); this.addTab(newtab,newpage,this,vals[j],'generated',''); - //setClipWidth(newtab,htr_getphyswidth(newtab)); - //setClipHeight(newtab,26); newpage.osrcdata = vals[j]; newpage.recordnumber = j; @@ -354,8 +393,8 @@ function tc_updated(p1) if(targettab) { - this.tabs[targettab].makeCurrent(); - this.tabs[targettab].tc_visible_changed('visible','hidden','inherit'); + tabs[targettab].makeCurrent(); + tabs[targettab].tc_visible_changed('visible','hidden','inherit'); } } } @@ -367,13 +406,20 @@ function tc_init(param) var l = param.layer; htr_init_layer(l,l,'tc'); ifc_init_widget(l); - l.tabs = new Array(); + l.tabs = []; l.addTab = tc_addtab; l.current_tab = null; l.init_tab = null; - l.tloc = param.tloc; - if (tc_tabs == null) tc_tabs = new Array(); + l.do_rendering = param.do_client_rendering; + l.select_x_offset = param.select_x_offset; + l.select_y_offset = param.select_y_offset; + l.xtoffset = param.xtoffset; + l.ytoffset = param.ytoffset; + l.tab_spacing = param.tab_spacing; + l.tab_h = param.tab_h; + if (tc_tabs == null) tc_tabs = []; tc_tabs[tc_tabs.length++] = l; + l.tloc = param.tloc; // Background color/image selection... l.main_bgColor = htr_extract_bgcolor(param.mainBackground); @@ -422,144 +468,99 @@ function tc_init(param) l.ChangeSelection2 = tc_changeselection_2; l.ChangeSelection3 = tc_changeselection_3; - // Movement geometries and clipping for tabs - switch(l.tloc) - { - case 0: // top - l.xo = +1; - l.yo = +2; - l.cl = "bottom"; - l.ci = -2; - break; - case 1: // bottom - l.xo = +1; - l.yo = -2; - l.cl = "top"; - l.ci = +2; - break; - case 2: // left - l.xo = +2; - l.yo = +1; - l.cl = "right"; - l.ci = -2; - break; - case 3: // right - l.xo = -2; - l.yo = +1; - l.cl = "left"; - l.ci = +2; - break; - case 4: // none - l.xo = 0; - l.yo = 0; - l.cl = "bottom"; - l.ci = 0; - break; - } return l; } -function tc_visible_changed(prop,o,n) +/*** Idk what this does... + *** + *** @param prop Unused, for some reason. + *** @param o Unused again... + *** @param n If this is true, it makes this.tab inherit visibility. Otherwise, hidden. + *** @returns nothing... idk what this is doing. + */ +function tc_visible_changed(prop, o, n) { - var t = this.tabctl; - var xo = t.xo; - var yo = t.yo; + const { tabctl: t } = this; + const { tabs, tloc } = t; + + if (tloc === 'None') console.warn("tc_visible_changed() called on tab contol with tab_location = none."); + if(n) htr_setvisibility(this.tab, 'inherit'); else htr_setvisibility(this.tab, 'hidden'); - // which tab should be selected? - if(htr_getvisibility(t.tabs[t.selected_index-1].tab)!='inherit') + + /** This nonsense is why we need goto in js. **/ + const pickSelectedTab = () => { - //try default tab - if(htr_getvisibility(t.init_tab.tab)=='inherit') + // If a visible tab is already selected, we're done. + const selected = tabs[t.selected_index-1]; + if (htr_getvisibility(selected.tab) === 'inherit') return; + + // Try to select the initial tab. + const initial = t.init_tab; + if (htr_getvisibility(initial.tab) === 'inherit') { - // This is forced, so we skip the obscure/reveal checks - t.ChangeSelection3(t.tabs[t.init_tab.tabindex-1]); - //t.tabs[t.init_tab.tabindex-1].makeCurrent(); - } - else //otherwise find first tab not hidden - { - for(var i=0; iCapabilities.Dom0NS && !s->Capabilities.Dom0IE &&(!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS)) { mssError(1,"HTTAB","NS4 or W3C DOM Support required"); return -1; } - - /** Get an id for this. **/ + + /** Reserve the next tab widget ID. **/ id = (HTTAB.idcnt++); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) + + /** Get the tab widget name. **/ + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) return -1; + strtcpy(name, ptr, sizeof(name)); + + /** Get x, y, w, & h of this object. **/ + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) x = 0; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) y = 0; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) { - mssError(0,"HTTAB","Tab widget must have a 'width' property"); + mssError(0, "HTTAB", "Tab widget must have a 'width' property"); return -1; } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) { - mssError(0,"HTTAB","Tab widget must have a 'height' property"); + mssError(0, "HTTAB", "Tab widget must have a 'height' property"); return -1; } - - /** Drop shadow **/ - shadow_offset=0; + + /** Get drop shadow data. **/ + shadow_offset = 0; if (wgtrGetPropertyValue(tree, "shadow_offset", DATA_T_INTEGER, POD(&shadow_offset)) == 0 && shadow_offset > 0) shadow_radius = shadow_offset+1; else @@ -128,25 +125,25 @@ httabRender(pHtSession s, pWgtrNode tree, int z) } if (wgtrGetPropertyValue(tree, "shadow_angle", DATA_T_INTEGER, POD(&shadow_angle)) != 0) shadow_angle = 135; - - /** Border radius, color, and style. **/ - if (wgtrGetPropertyValue(tree,"border_radius",DATA_T_INTEGER,POD(&border_radius)) != 0) - border_radius=0; - if (wgtrGetPropertyValue(tree,"border_color",DATA_T_STRING,POD(&ptr)) != 0) + + /** Get border info (radius, color, and style). **/ + if (wgtrGetPropertyValue(tree, "border_radius", DATA_T_INTEGER, POD(&border_radius)) != 0) + border_radius = 0; + if (wgtrGetPropertyValue(tree, "border_color", DATA_T_STRING, POD(&ptr)) != 0) strcpy(border_color, "#ffffff"); else strtcpy(border_color, ptr, sizeof(border_color)); - if (wgtrGetPropertyValue(tree,"border_style",DATA_T_STRING,POD(&ptr)) != 0) + if (wgtrGetPropertyValue(tree, "border_style", DATA_T_STRING,POD(&ptr)) != 0) strcpy(border_style, "outset"); else strtcpy(border_style, ptr, sizeof(border_style)); if (!strcmp(border_style, "none") || !strcmp(border_style, "hidden")) - border_width=0; + border_width = 0; else - border_width=1; - - /** Which side are the tabs on? **/ - if (wgtrGetPropertyValue(tree,"tab_location",DATA_T_STRING,POD(&ptr)) == 0) + border_width = 1; + + /** Get tab_location. **/ + if (wgtrGetPropertyValue(tree, "tab_location", DATA_T_STRING, POD(&ptr)) == 0) { if (!strcasecmp(ptr,"top")) tloc = Top; else if (!strcasecmp(ptr,"bottom")) tloc = Bottom; @@ -163,209 +160,532 @@ httabRender(pHtSession s, pWgtrNode tree, int z) { tloc = Top; } - - /** How wide should left/right tabs be? **/ - if (wgtrGetPropertyValue(tree,"tab_width",DATA_T_INTEGER,POD(&tab_width)) != 0) + + /** Count the number of tabs. **/ + tab_count = wgtrGetMatchingChildList(tree, "widget/tabpage", children, sizeof(children) / sizeof(pWgtrNode)); + + /** Get the selected tab. **/ + if (wgtrGetPropertyType(tree, "selected") == DATA_T_STRING && + wgtrGetPropertyType(tree, "selected_index") == DATA_T_INTEGER) { - if (tloc == Right || tloc == Left) + mssError(1,"HTTAB","%s: cannot specify both 'selected' and 'selected_index'", name); + return -1; + } + if (wgtrGetPropertyValue(tree, "selected", DATA_T_STRING, POD(&ptr)) == 0) + { + /** Search for the tab with the indicated name. **/ + for (i = 0; i < tab_count; i++) { - mssError(1,"HTTAB","%s: tab_width must be specified with tab_location of left or right", name); + char* tab_name; + wgtrGetPropertyValue(children[i], "name", DATA_T_STRING, POD(&tab_name)); + if (strcmp(ptr, tab_name) == 0) + { + /** sel_idx is 1 based, but i is 0 based. **/ + sel_idx = i + 1; + break; + } + } + if (i >= tab_count) { + mssError(1, "HTTAB", "%s: cannot find tab with name '%s'", name, ptr); + + /** Attempt to give hint. **/ + if (tab_count <= 0) return -1; + char* example_tab_name; + wgtrGetPropertyValue(children[0], "name", DATA_T_STRING, POD(&example_tab_name)); + mssError(0, "HTTAB", "Hint: 'selected' should be a tab name, such as \"%s\".", example_tab_name); + + /** Fail. **/ + return -1; + } + } + else if (wgtrGetPropertyValue(tree, "selected_index", DATA_T_INTEGER, POD(&sel_idx)) == 0) + { + if (sel_idx <= 0) + { + mssError(1, "HTTAB", "Invalid value for 'selected_index': %d.", sel_idx); + if (sel_idx == 0) mssError(0, "HTTAB", "Hint: 'selected_index' is 1-based."); + return -1; + } + if (sel_idx > tab_count) + { + mssError(1, "HTTAB", + "Invalid value for 'selected_index': %d. Tab control only has %d tab%s.", + sel_idx, tab_count, (tab_count == 1) ? "" : "s" + ); return -1; } } else { - if (tab_width < 0) tab_width = 0; + /** No specified selected tab, default to the first one. **/ + sel_idx = 1; } - - /** Which tab is selected? **/ - if (wgtrGetPropertyType(tree,"selected") == DATA_T_STRING && - wgtrGetPropertyValue(tree,"selected",DATA_T_STRING,POD(&ptr)) == 0) + + /** Handle user expressions for the selected tab. **/ + htrCheckAddExpression(s, tree, name, "selected"); + htrCheckAddExpression(s, tree, name, "selected_index"); + + /** Get the background color/image. **/ + htrGetBackground(tree, NULL, s->Capabilities.Dom2CSS, main_bg, sizeof(main_bg)); + + /** Get the inactive tab color/image. **/ + if (htrGetBackground(tree, "inactive", s->Capabilities.Dom2CSS, inactive_bg, sizeof(inactive_bg)) != 0) + strcpy(inactive_bg, main_bg); + + /** Get the text color. **/ + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0 + && !strpbrk(ptr, "{};&<>\"\'")) + strtcpy(text_color, ptr, sizeof(text_color)); + else + strcpy(text_color,"black"); + + /** Get the tab spacing and tab height. **/ + /** tab_w and tab_h are left as 0 if unset to tell the front end to calculate them dynamically. **/ + int tab_spacing = 2; /* Default to a 2px gap between tabs. */ + if (wgtrGetPropertyValue(tree, "tab_spacing", DATA_T_INTEGER, POD(&tmp)) == 0) tab_spacing = tmp; + if (wgtrGetPropertyValue(tree, "tab_width", DATA_T_INTEGER, POD(&tmp)) == 0) { - strtcpy(sel,ptr, sizeof(sel)); + if (tmp <= 0) + { + mssError(1, "HTTAB", "%s: 'tab_width' expected positive nonzero int, got %d.", name, tmp); + return -1; + } + tab_w = tmp; } - else + else if (tloc == Right || tloc == Left) { - strcpy(sel,""); + mssError(1, "HTTAB", "%s: 'tab_width' must be specified for 'tab_location' of left or right", name); + return -1; } - if (wgtrGetPropertyValue(tree,"selected_index", DATA_T_INTEGER, POD(&sel_idx)) != 0) + else { - sel_idx = -1; + /** Use a default value, updated client side. */ + tab_w = 80; + is_auto_tab_w = 1; } - if (sel_idx != -1 && *sel != '\0') + if (wgtrGetPropertyValue(tree, "tab_height", DATA_T_INTEGER, POD(&tmp)) == 0) { - mssError(1,"HTTAB","%s: cannot specify both 'selected' and 'selected_index'", name); - return -1; + if (tmp <= 0) + { + mssError(1, "HTTAB", "%s: 'tab_height' expected positive nonzero int, got %d.", name, tmp); + return -1; + } + tab_h = tmp; } - - /** User requesting expression for selected tab? **/ - htrCheckAddExpression(s, tree, name, "selected"); - - /** User requesting expression for selected tab using integer index value? **/ - htrCheckAddExpression(s, tree, name, "selected_index"); - - /** Background color/image? **/ - htrGetBackground(tree, NULL, s->Capabilities.Dom2CSS, main_bg, sizeof(main_bg)); - - /** Inactive tab color/image? **/ - htrGetBackground(tree, "inactive", s->Capabilities.Dom2CSS, inactive_bg, sizeof(inactive_bg)); - - /** Text color? **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(tab_txt, ptr, sizeof(tab_txt)); else - strcpy(tab_txt,"black"); - if (strpbrk(tab_txt, "{};&<>\"\'")) - strcpy(tab_txt,"black"); - - /** Determine offset to actual tab pages **/ - switch(tloc) { - case Top: xoffset = 0; yoffset = 24; xtoffset = 0; ytoffset = 0; break; - case Bottom: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = h; break; - case Right: xoffset = 0; yoffset = 0; xtoffset = w; ytoffset = 0; break; - case Left: xoffset = tab_width; yoffset = 0; xtoffset = 0; ytoffset = 0; break; - case None: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = 0; + /** Use default value, no client side calculation available. **/ + tab_h = 24; } - - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#tc%POSbase { background-position: 0px -24px; %STR }\n", id, main_bg); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "tc%POSbase",id); - - /** Script include **/ + + /** Get macro selection translation values. **/ + /** CHANGE: Code previously used 1, but I think 0 is a better looking default. **/ + int along, out; + if (wgtrGetPropertyValue(tree, "select_translate_along", DATA_T_INTEGER, POD(&along)) != 0) along = 0; + if (wgtrGetPropertyValue(tree, "select_translate_out", DATA_T_INTEGER, POD(&out)) != 0) out = 2; + + /** Determine offset to actual tab pages and offsets for selected tabs. **/ + int select_x_offset = 0, select_y_offset = 0; + switch (tloc) + { + /*** Shift to cover boarder line: + *** Top: ytoffset +1 + *** Bottom: ytoffset -2 + *** Left: xtoffset +1 + *** Right: xtoffset -2 + ***/ + case Top: xoffset = 0; yoffset = tab_h; xtoffset = 0; ytoffset = 0; select_x_offset = -along; select_y_offset = -out; break; + case Bottom: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = h-1; select_x_offset = -along; select_y_offset = +out; break; + case Left: xoffset = tab_w; yoffset = 0; xtoffset = 0; ytoffset = 0; select_x_offset = -out; select_y_offset = -along; break; + case Right: xoffset = 0; yoffset = 0; xtoffset = w-1; ytoffset = 0; select_x_offset = +out; select_y_offset = -along; break; + case None: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = 0; select_x_offset = 0; select_y_offset = 0; break; + } + + /** Get coordinate-based selection translation values. **/ + if (wgtrGetPropertyValue(tree, "select_translate_x", DATA_T_INTEGER, POD(&tmp)) == 0) select_x_offset = tmp; + if (wgtrGetPropertyValue(tree, "select_translate_y", DATA_T_INTEGER, POD(&tmp)) == 0) select_y_offset = tmp; + + /*** Apply the opposite of the selection offset to all tabs. This + *** prevents the offset from causing the selected tab to appear + *** detached from the tab control. + ***/ + xtoffset -= select_x_offset; + ytoffset -= select_y_offset; + + /** Get the rendering type. **/ + /** Allows the developer to turn off JS client-side widget rendering for testing. **/ + int do_client_rendering = 1; + if (wgtrGetPropertyValue(tree, "rendering", DATA_T_STRING, POD(&ptr)) == 0) + { + if (strcmp(ptr, "server-side") == 0) do_client_rendering = 0; + else if (strcmp(ptr, "client-side") == 0) do_client_rendering = 1; + else + { + mssError(1, "HTTAB", "%s: Unknown value for 'rendering': %s", name, ptr); + mssError(0, "HTTAB", "HINT: Should be either 'server-side' or 'client-size'."); + return -1; + } + } + if (!do_client_rendering && tab_w == 0 && (tloc == Top || tloc == Bottom)) + { + /*** The dev has specified server-side rendering for Top/Bottom tabs + *** with dynamic width. This will probably look broken. + ***/ + mssError(1, "HTTAB", + "%s: 'rendering' value of \"server-side\" will break on tabs " + "with dynamic widths because they cannot be calculated server-side!", + name + ); + mssError(0, "HTTAB", "HINT: Specify 'tab_width' when using \"server-side\" rendering."); + } + + /** Handle DOM linkages. **/ + htrAddWgtrObjLinkage_va(s, tree, "tc%POSctrl", id); + + /** Include the htdrv_tab.js script. **/ htrAddScriptInclude(s, "/sys/js/htdrv_tab.js", 0); - - /** Add a global for the master tabs listing **/ + + /** Send globals variables to the client to avoid needing to hard code them. **/ + const int bufsiz = 96; + char* config_buf = nmSysMalloc(bufsiz); + if (config_buf == NULL) + { + mssError(1, "HTTAB", "%s: nmSysMalloc(%d) failed.", name, bufsiz); + return -1; + } + snprintf( + memset(config_buf, 0, bufsiz), bufsiz, + "{ tlocs: { Top:%d, Bottom:%d, Left:%d, Right:%d, None:%d } }", + Top, Bottom, Left, Right, None + ); + htrAddScriptGlobal(s, "tc_config", config_buf, HTR_F_VALUEALLOC); + /*** TODO: Greg - config_buf is definitely leaked because I can't + *** figure out how long it needs to remain in scope. + ***/ + + /** Add globals for the master tabs listing. **/ htrAddScriptGlobal(s, "tc_tabs", "null", 0); htrAddScriptGlobal(s, "tc_cur_mainlayer", "null", 0); - - /** Event handler for click-on-tab **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","tc","tc_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","tc","tc_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","tc","tc_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER","tc","tc_mouseover"); - + + /** Add mouse event handlers. **/ + htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tc", "tc_mousedown"); + htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tc", "tc_mouseup"); + htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tc", "tc_mousemove"); + htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tc", "tc_mouseover"); + /** Script initialization call. **/ - htrAddScriptInit_va(s," tc_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), tloc:%INT, mainBackground:\"%STR&JSSTR\", inactiveBackground:\"%STR&JSSTR\"});\n", - name, tloc, main_bg, inactive_bg); - + char tloc_name[8]; + switch (tloc) + { + case Top: strcpy(tloc_name, "Top"); break; + case Bottom: strcpy(tloc_name, "Bottom"); break; + case Left: strcpy(tloc_name, "Left"); break; + case Right: strcpy(tloc_name, "Right"); break; + case None: strcpy(tloc_name, "None"); break; + } + htrAddScriptInit_va(s, + "\ttc_init({" + "layer:wgtrGetNodeRef(ns,'%STR&SYM'), " + "tloc:'%STR', " + "mainBackground:'%STR&JSSTR', " + "inactiveBackground:'%STR&JSSTR', " + "select_x_offset:%INT, " + "select_y_offset:%INT, " + "xtoffset:%INT, " + "ytoffset:%INT, " + "tab_spacing:%INT, " + "tab_w:%INT, " + "tab_h:%INT, " + "do_client_rendering:%STR, " + "});\n", + name, + tloc_name, + main_bg, + inactive_bg, + select_x_offset, + select_y_offset, + xtoffset, + ytoffset, + tab_spacing, + (is_auto_tab_w) ? 0 : tab_w, /* 0 tells the front end that it should recalculate tab_w. */ + tab_h, + (do_client_rendering) ? "true" : "false" + ); + /** Check for tabpages within the tab control, to do the tabs at the top. **/ - tabcnt = wgtrGetMatchingChildList(tree, "widget/tabpage", children, sizeof(children)/sizeof(pWgtrNode)); if (tloc != None) { - for (i=0;i0, tab_width, - is_selected?(z+2):z, - (tloc==Bottom || tloc==Right)?0:border_radius, (tloc==Bottom || tloc==Left)?0:border_radius, (tloc==Top || tloc==Left)?0:border_radius, (tloc==Top || tloc==Right)?0:border_radius, - //(tloc==Top || tloc==Left)?0:border_radius, (tloc==Right)?0:border_radius, border_radius, (tloc==Bottom)?0:border_radius, - border_style, - (tloc!=Bottom)?1:0, (tloc!=Left)?1:0, (tloc!=Top)?1:0, (tloc!=Right)?1:0, - border_color, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color, - (tloc != Right)?"left":"right", - tab_txt, bg - ); - - htrAddBodyItem_va(s, "

%[ %STR&HTE %]%[ %STR&HTE %]

\n", - id, i+1, - tloc == Right, tabname, - is_selected?2:3, - tloc != Right, tabname - ); + case Top: case Bottom: i_offset_x = full_tab_spacing + tab_w; break; + case Right: case Left: i_offset_y = full_tab_spacing + tab_h; break; + case None:; /* Unreachable, but the compiler doesn't believe me. */ } - } - - /** h-2 and w-2 because w3c dom borders add to actual width **/ - htrAddBodyItem_va(s,"
\n", - id, h-border_width*2, w-border_width*2, x+xoffset, y+yoffset, z+1, - border_style, border_color, - (tloc==Top || tloc==Left)?0:border_radius, (tloc==Right)?0:border_radius, border_radius, (tloc==Bottom)?0:border_radius, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color + + /** Calculate tab flex information. **/ + /*** fl_x/y is enough flex to line up with the left/top of the tab + *** control. However, if the tab box changes size, tabs on the + *** right/bottom need to flex enough to handle that, too. + ***/ + double tab_fl_x = ht_get_fl_x(tree), tab_fl_y = ht_get_fl_y(tree); + if (tloc == Right) tab_fl_x += ht_get_fl_w(tree); + else if (tloc == Bottom) tab_fl_y += ht_get_fl_h(tree); + + /** Inject tab_fl values for client-side rendering. **/ + htrAddScriptInit_va(s, + "{" + "const node = wgtrGetNodeRef(ns,'%STR&SYM'); " + "node.tab_fl_x = %DBL; " + "node.tab_fl_y = %DBL; " + "}\n", + name, + tab_fl_x, + tab_fl_y + ); + + /** Loop over each tab. **/ + for (i = 0; i < tab_count; i++) + { + pWgtrNode tab = children[i]; + + /** Check if the tab is selected. **/ + int is_selected = (i == sel_idx - 1); + + /** Get type. **/ + wgtrGetPropertyValue(tab, "type", DATA_T_STRING, POD(&page_type)); + if (type == NULL || strcmp(type, "dynamic") != 0) strcpy(page_type, "static"); + + /** Use the tab title, defaulting to the tab name if it isn't specified. **/ + if (wgtrGetPropertyValue(tab, "title", DATA_T_STRING, POD(&tabname)) != 0) + wgtrGetPropertyValue(tab, "name", DATA_T_STRING, POD(&tabname)); + + /** Write tab CSS styles. **/ + int tab_x = (x + xtoffset) + (i_offset_x * i); + int tab_y = (y + ytoffset) + (i_offset_y * i); + htrAddStylesheetItem_va(s, + "\t#tc%POStab%POS { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[width:%POSpx; %]" /* Tab width has 0 flexibility. */ + "%[height:%POSpx; %]" /* Tab height has 0 flexibility. */ + "overflow:hidden; " + "z-index:%POS; " + "cursor:default; " + "border-radius:" + "%POSpx " + "%POSpx " + "%POSpx " + "%POSpx; " + "border-style:%STR&CSSVAL; " + "border-width:%POSpx %POSpx %POSpx %POSpx; " + "border-color:%STR&CSSVAL; " + "box-shadow:%DBLpx %DBLpx %POSpx %STR&CSSVAL; " + "text-align:%STR&CSSVAL; " + "color:%STR&CSSVAL; " + "font-weight:bold; " + "background-position: %INTpx %INTpx; " + "%STR " + "}\n", + id, i + 1, + ht_flex(tab_x, ht_get_parent_w(tree), tab_fl_x), // left + ht_flex(tab_y, ht_get_parent_h(tree), tab_fl_y), // top + (!is_auto_tab_w), tab_w, /* Tab width has 0 flexibility. */ + (tab_h > 0), tab_h, /* Tab height has 0 flexibility. */ + (is_selected) ? (z + 2) : z, + (tloc == Bottom || tloc == Right) ? 0 : border_radius, + (tloc == Bottom || tloc == Left) ? 0 : border_radius, + (tloc == Top || tloc == Left) ? 0 : border_radius, + (tloc == Top || tloc == Right) ? 0 : border_radius, + border_style, + (tloc != Bottom) ? 1 : 0, (tloc != Left) ? 1 : 0, (tloc != Top) ? 1 : 0, (tloc != Right) ? 1 : 0, + border_color, + sin(shadow_angle * M_PI/180) * shadow_offset, cos(shadow_angle * M_PI/180) * (-shadow_offset), shadow_radius, shadow_color, + (tloc != Right) ? "left" : "right", + text_color, + tab_x + 1, tab_y, + (is_selected) ? main_bg : inactive_bg ); - - /** Check for tabpages within the tab control entity, this time to do the pages themselves **/ - for (i=0;i" + "

" + "%[ %STR&HTE %]" + "" + "%[ %STR&HTE %]" + "

" + "
\n", + id, i + 1, (is_selected), + (tloc == Right), tabname, + (is_selected) ? 2 : 3, tab_h, + (tloc != Right), tabname + ); + } + } + + /** Write tab control CSS and HTML. **/ + htrAddStylesheetItem_va(s, + "#tc%POSctrl {" + "position:absolute; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "border-width:1px; " + "border-style:%STR&CSSVAL; " + "border-color:%STR&CSSVAL; " + "border-radius:" + "%POSpx " + "%POSpx " + "%POSpx " + "%POSpx; " + "box-shadow:%DBLpx %DBLpx %POSpx %STR&CSSVAL; " + "background-position:%INTpx %INTpx; " + "%STR " + "}\n", + id, + ht_flex_x(x + xoffset, tree), + ht_flex_y(y + yoffset, tree), + ht_flex_w(w - border_width * 2, tree), + ht_flex_h(h - border_width * 2, tree), + z + 1, + border_style, + border_color, + (tloc==Top || tloc==Left) ? 0 : border_radius, + (tloc==Right) ? 0 : border_radius, + border_radius, + (tloc==Bottom) ? 0 : border_radius, + sin(shadow_angle * M_PI/180) * shadow_offset, cos(shadow_angle * M_PI/180) * (-shadow_offset), shadow_radius, shadow_color, + x + xoffset, y + yoffset, + main_bg + ); + htrAddBodyItem_va(s, "
\n", id); + + /** Check for tab pages within the tab control entity, this time to do the pages themselves. **/ + for (i = 0; i < tab_count; i++) { - tabpage_obj = children[i]; - - htrCheckNSTransition(s, tree, tabpage_obj); - + pWgtrNode tab_page_tree = children[i]; + + /** Handle namespace transition. **/ + htrCheckNSTransition(s, tree, tab_page_tree); + /** First, render the tabpage and add stuff for it **/ - wgtrGetPropertyValue(tabpage_obj,"name",DATA_T_STRING,POD(&ptr)); - is_selected = (i+1 == sel_idx || (!*sel && i == 0) || !strcmp(sel,ptr)); - if(wgtrGetPropertyValue(tabpage_obj,"type",DATA_T_STRING,POD(&type)) != 0) - strcpy(page_type,"static"); - else if(!strcmp(type,"static") || !strcmp(type,"dynamic")) - strcpy(page_type,type); - else - strcpy(page_type,"static"); - strcpy(fieldname,""); - if(!strcmp(page_type,"dynamic")) - { - if(wgtrGetPropertyValue(tabpage_obj,"fieldname",DATA_T_STRING,POD(&field)) == 0) - strtcpy(fieldname,field,sizeof(fieldname)); - } - + wgtrGetPropertyValue(tab_page_tree,"name",DATA_T_STRING,POD(&ptr)); + + /** Check if the tab is selected. **/ + int is_selected = (i == sel_idx - 1); + + /** Set type. **/ + wgtrGetPropertyValue(tab_page_tree, "type", DATA_T_STRING, POD(&page_type)); + if (type == NULL || strcmp(type, "dynamic") != 0) strcpy(page_type, "static"); + + /** Set feildname. **/ + if (strcmp(page_type, "dynamic") == 0 && + wgtrGetPropertyValue(tab_page_tree, "fieldname", DATA_T_STRING, POD(&field)) == 0) + strtcpy(fieldname, field, sizeof(fieldname)); + else strcpy(fieldname, ""); + /** Add script initialization to add a new tabpage **/ if (tloc == None) - htrAddScriptInit_va(s," wgtrGetNodeRef('%STR&SYM', '%STR&SYM').addTab(null,wgtrGetContainer(wgtrGetNodeRef(\"%STR&SYM\",\"%STR&SYM\")),wgtrGetNodeRef('%STR&SYM','%STR&SYM'),'%STR&JSSTR','%STR&JSSTR','%STR&JSSTR');\n", + { + htrAddScriptInit_va(s, + "\twgtrGetNodeRef('%STR&SYM', '%STR&SYM')" + ".addTab(null, " + "wgtrGetContainer(wgtrGetNodeRef('%STR&SYM', '%STR&SYM')), " + "wgtrGetNodeRef('%STR&SYM', '%STR&SYM'), " + "'%STR&JSSTR', '%STR&JSSTR', '%STR&JSSTR'" + ");\n", + wgtrGetNamespace(tree), name, + wgtrGetNamespace(tab_page_tree), ptr, wgtrGetNamespace(tree), name, - wgtrGetNamespace(tabpage_obj), ptr, wgtrGetNamespace(tree), name, ptr,page_type,fieldname); + ptr, page_type, fieldname + ); + } else - htrAddScriptInit_va(s," wgtrGetNodeRef('%STR&SYM','%STR&SYM').addTab(htr_subel(wgtrGetParentContainer(wgtrGetNodeRef('%STR&SYM','%STR&SYM')),\"tc%POStab%POS\"),wgtrGetContainer(wgtrGetNodeRef(\"%STR&SYM\",\"%STR&SYM\")),wgtrGetNodeRef('%STR&SYM','%STR&SYM'),'%STR&JSSTR','%STR&JSSTR','%STR&JSSTR');\n", + { + htrAddScriptInit_va(s, + "\twgtrGetNodeRef('%STR&SYM','%STR&SYM')" + ".addTab(" + "htr_subel(" + "wgtrGetParentContainer(wgtrGetNodeRef('%STR&SYM', '%STR&SYM')), " + "'tc%POStab%POS'" + "), " + "wgtrGetContainer(wgtrGetNodeRef('%STR&SYM', '%STR&SYM')), " + "wgtrGetNodeRef('%STR&SYM', '%STR&SYM'), " + "'%STR&JSSTR', '%STR&JSSTR', '%STR&JSSTR'" + ");\n", wgtrGetNamespace(tree), name, wgtrGetNamespace(tree), name, - id, i+1, wgtrGetNamespace(tabpage_obj), ptr, wgtrGetNamespace(tree), name, ptr,page_type,fieldname); - - /** Add named global for the tabpage **/ - subnptr = nmSysStrdup(ptr); - htrAddWgtrObjLinkage_va(s, tabpage_obj, "tc%POSpane%POS", id, i+1); - htrAddWgtrCtrLinkage_va(s, tabpage_obj, "htr_subel(_parentobj, \"tc%POSpane%POS\")", id, i+1); - - /** Add DIV section for the tabpage. **/ - htrAddBodyItem_va(s,"
\n", - id,i+1,is_selected?"inherit":"hidden",w-2,z+2); - - /** Now look for sub-items within the tabpage. **/ - for (j=0;jChildren));j++) - htrRenderWidget(s, xaGetItem(&(tabpage_obj->Children), j), z+3); - - htrAddBodyItem(s, "
\n"); - - nmSysFree(subnptr); - - /** Add the visible property **/ - htrCheckAddExpression(s, tabpage_obj, ptr, "visible"); - - htrCheckNSTransitionReturn(s, tree, tabpage_obj); + id, i + 1, + wgtrGetNamespace(tab_page_tree), ptr, + wgtrGetNamespace(tree), name, + ptr, page_type, fieldname + ); + } + + /** Add named global for the tabpage. **/ + htrAddWgtrObjLinkage_va(s, tab_page_tree, "tc%POSpane%POS", id, i+1); + htrAddWgtrCtrLinkage_va(s, tab_page_tree, "htr_subel(_parentobj, \"tc%POSpane%POS\")", id, i + 1); + + /** Add DIV section to contane the tabpage. **/ + htrAddBodyItem_va(s, + "
\n", + id, i + 1, + (is_selected) ? "inherit" : "hidden", + z + 2 + ); + + /** Handle sub-items within the tabpage. **/ + for (int j = 0; j < xaCount(&(tab_page_tree->Children)); j++) + htrRenderWidget(s, xaGetItem(&(tab_page_tree->Children), j), z+3); + + /** Close the tab page container. */ + htrAddBodyItem(s, "
\n"); + + /** Add the visible property. **/ + htrCheckAddExpression(s, tab_page_tree, ptr, "visible"); + + /** Handle namespace transition. **/ + htrCheckNSTransitionReturn(s, tree, tab_page_tree); } - - /** Need to do other subwidgets (connectors, etc.) now **/ - htrRenderSubwidgets(s, tree, z+1); - + + /** Handle other subwidgets (connectors, etc.). **/ + htrRenderSubwidgets(s, tree, z + 1); + /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); - + htrAddBodyItem(s, "
\n"); + return 0; } @@ -385,25 +705,20 @@ httabInitialize() { pHtDriver drv; - /** Allocate the driver **/ + /** Tab Control Driver. **/ drv = htrAllocDriver(); - if (!drv) return -1; - - /** Fill in the structure. **/ - strcpy(drv->Name,"DHTML Tab Control Driver"); - strcpy(drv->WidgetName,"tab"); + if (drv == NULL) return -1; + strcpy(drv->Name, "DHTML Tab Control Driver"); + strcpy(drv->WidgetName, "tab"); drv->Render = httabRender; - /*xaAddItem(&(drv->PseudoTypes), "tabpage");*/ - - /** Register. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); + /** Tab Page Driver. **/ drv = htrAllocDriver(); - if (!drv) return -1; - strcpy(drv->Name,"DHTML Tab Page Driver"); - strcpy(drv->WidgetName,"tabpage"); + if (drv == NULL) return -1; + strcpy(drv->Name, "DHTML Tab Page Driver"); + strcpy(drv->WidgetName, "tabpage"); drv->Render = httabRender_page; htrRegisterDriver(drv); htrAddSupport(drv, "dhtml"); From 7cd1fd74f147132900f1adf93fd0b080c129fd7a Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 10:50:40 -0700 Subject: [PATCH 030/107] Make the clock widget responsive. Remove the moveable attribute. Simplify clock widget event code. Clean up cl_get_time(). --- centrallix-os/samples/clock.app | 3 - centrallix-os/sys/js/htdrv_clock.js | 62 ++++++-------------- centrallix/htmlgen/htdrv_clock.c | 88 +++++++++++++++++++++-------- 3 files changed, 80 insertions(+), 73 deletions(-) diff --git a/centrallix-os/samples/clock.app b/centrallix-os/samples/clock.app index 1a2567ab3..a17ceb2af 100644 --- a/centrallix-os/samples/clock.app +++ b/centrallix-os/samples/clock.app @@ -14,7 +14,6 @@ main "widget/page" shadowx = 2; shadowy = 2; size=1; - moveable="true"; bold="true"; } clock2 "widget/clock" @@ -22,7 +21,6 @@ main "widget/page" background="/sys/images/fade_pixelate_01.gif"; x=15; y=55; width=80; height=20; fgcolor1="white"; - moveable="true"; bold="true"; } clock3 "widget/clock" @@ -33,7 +31,6 @@ main "widget/page" fgcolor1="orange"; fgcolor2="#666666"; // size=3; - moveable="true"; bold="true"; } } diff --git a/centrallix-os/sys/js/htdrv_clock.js b/centrallix-os/sys/js/htdrv_clock.js index d7bec6027..612fdf452 100644 --- a/centrallix-os/sys/js/htdrv_clock.js +++ b/centrallix-os/sys/js/htdrv_clock.js @@ -24,7 +24,6 @@ function cl_init(param){ l.contentLayer = c1; l.hiddenLayer = c2; l.shadowed = param.shadowed; - l.moveable = param.moveable; l.bold = param.bold; l.fgColor1 = param.foreground1; l.fgColor2 = param.foreground2; @@ -57,13 +56,14 @@ function cl_init(param){ } function cl_get_time(l) { - var t = new Date(); - var time = new Object(); - time.hrs = t.getHours(); - time.mins = t.getMinutes(); - time.secs = t.getSeconds(); - time.msecs = t.getMilliseconds(); - time.formated = cl_format_time(l,time); + const t = new Date(); + const time = { + hrs: t.getHours(), + mins: t.getMinutes(), + secs: t.getSeconds(), + msecs: t.getMilliseconds(), + }; + time.formated = cl_format_time(l, time); return time; } @@ -110,46 +110,16 @@ function cl_format_time(l,t) { return timef; } -function cl_mouseup(e) { - if (e.kind == 'cl') { - cn_activate(e.mainlayer, 'MouseUp'); - if (e.mainlayer.moveable) cl_move = false; - } - return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; -} - -function cl_mousedown(e) { - if (e.kind == 'cl') { - cn_activate(e.mainlayer, 'MouseDown'); - if (e.mainlayer.moveable) { - cl_move = true; - cl_xOffset = e.pageX - getPageX(e.mainlayer); - cl_yOffset = e.pageY - getPageY(e.mainlayer); - } - } - return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; -} - -function cl_mouseover(e) { - if (e.kind == 'cl') cn_activate(e.mainlayer, 'MouseOver'); - return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; -} - -function cl_mouseout(e) { - if (e.kind == 'cl') cn_activate(e.mainlayer, 'MouseOut'); - return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; -} - -function cl_mousemove(e) { - if (e.kind == 'cl') { - cn_activate(e.mainlayer, 'MouseMove'); - if (e.mainlayer.moveable && cl_move) - moveToAbsolute(e.mainlayer, - e.pageX - cl_xOffset, - e.pageY - cl_yOffset); - } +// Handle events by passing them on to Centrallix. +function cl_event(e, name) { + if (e && e.kind === 'cl') cn_activate(e.mainlayer, name); return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; } +function cl_mouseup(e) { return cl_event(e, 'MouseUp'); } +function cl_mousedown(e) { return cl_event(e, 'MouseDown'); } +function cl_mouseover(e) { return cl_event(e, 'MouseOver'); } +function cl_mouseout(e) { return cl_event(e, 'MouseOut'); } +function cl_mousemove(e) { return cl_event(e, 'MouseMove'); } // Load indication if (window.pg_scripts) pg_scripts['htdrv_clock.js'] = true; diff --git a/centrallix/htmlgen/htdrv_clock.c b/centrallix/htmlgen/htdrv_clock.c index 26d070321..4799127c0 100644 --- a/centrallix/htmlgen/htdrv_clock.c +++ b/centrallix/htmlgen/htdrv_clock.c @@ -64,7 +64,6 @@ htclRender(pHtSession s, pWgtrNode tree, int z) int shadowx = 0; int shadowy = 0; int size = 0; - int moveable = 0; int bold = 0; int showsecs = 1; int showampm = 1; @@ -142,10 +141,6 @@ htclRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"size",DATA_T_INTEGER,POD(&ptr)) == 0) size = (intptr_t)ptr; - /** Movable? **/ - if (wgtrGetPropertyValue(tree,"moveable",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"true")) - moveable = 1; - /** Show Seconds **/ if (wgtrGetPropertyValue(tree,"seconds",DATA_T_STRING,POD(&ptr)) == 0 && (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no"))) showsecs = 0; @@ -160,19 +155,39 @@ htclRender(pHtSession s, pWgtrNode tree, int z) else fieldname[0]='\0'; - /** Write Style header items. **/ - htrAddStylesheetItem_va(s,"\t#cl%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#cl%POScon1 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+2); - htrAddStylesheetItem_va(s,"\t#cl%POScon2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+2); - - /** Write named global **/ + /** Write style headers. **/ + htrAddStylesheetItem_va(s, + "\t#cl%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z + ); + htrAddStylesheetItem_va(s, + "\t.cl%POScon { " + "position:absolute; " + "left:0px; " + "top:0px; " + "width:100%%; " + "z-index:%POS; " + "}\n", + id, + 0, + 0, + z + 2 + ); + + /** Setup linkage **/ htrAddWgtrObjLinkage_va(s, tree, "cl%POSbase",id); - /** Other global variables **/ - htrAddScriptGlobal(s, "cl_move", "false", 0); - htrAddScriptGlobal(s, "cl_xOffset", "null", 0); - htrAddScriptGlobal(s, "cl_yOffset", "null", 0); - /** Javascript include files **/ htrAddScriptInclude(s, "/sys/js/htdrv_clock.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); @@ -185,21 +200,46 @@ htclRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "cl", "cl_mousemove"); /** Script initialization call. **/ - htrAddScriptInit_va(s, " cl_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), c1:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"cl%POScon1\"), c2:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"cl%POScon2\"), fieldname:\"%STR&JSSTR\", background:\"%STR&JSSTR\", shadowed:%POS, foreground1:\"%STR&JSSTR\", foreground2:\"%STR&JSSTR\", fontsize:%INT, moveable:%INT, bold:%INT, sox:%INT, soy:%INT, showSecs:%INT, showAmPm:%INT, milTime:%INT});\n", + htrAddScriptInit_va(s, + "cl_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "c1:htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'cl%POScon1'), " + "c2:htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'cl%POScon2'), " + "fieldname:'%STR&JSSTR', " + "background:'%STR&JSSTR', " + "shadowed:%POS, " + "foreground1:'%STR&JSSTR', " + "foreground2:'%STR&JSSTR', " + "fontsize:%INT, " + "bold:%INT, " + "sox:%INT, " + "soy:%INT, " + "showSecs:%INT, " + "showAmPm:%INT, " + "milTime:%INT, " + "});\n", name, name, id, name, id, - fieldname, main_bg, shadowed, - fgcolor1, fgcolor2, - size, moveable, bold, - shadowx, shadowy, - showsecs, showampm, miltime); + fieldname, + main_bg, + shadowed, + fgcolor1, + fgcolor2, + size, + bold, + shadowx, + shadowy, + showsecs, + showampm, + miltime + ); /** HTML body
element for the base layer. **/ htrAddBodyItem_va(s, "
\n",id); htrAddBodyItem_va(s, "
\n",main_bg,w,h); - htrAddBodyItem_va(s, "
\n",id); - htrAddBodyItem_va(s, "
\n",id); + htrAddBodyItem_va(s, "
\n", id, id); + htrAddBodyItem_va(s, " \n", id, id); htrAddBodyItem(s, "
\n"); /** Check for more sub-widgets **/ From b0b15536f152043cf6790f54a501dd8ef3245fef Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 11:05:09 -0700 Subject: [PATCH 031/107] Debug and clean up the component widget. Fix a bug where the container div could block mouse events. Add print failure warning when component-decl uses an empty gname value. Add TODOs to improve error handling once dups util code is available. Add comments. Improve formatting. Clean up. --- centrallix/htmlgen/htdrv_component.c | 147 +++++++++++++++-------- centrallix/htmlgen/htdrv_componentdecl.c | 35 +++++- 2 files changed, 128 insertions(+), 54 deletions(-) diff --git a/centrallix/htmlgen/htdrv_component.c b/centrallix/htmlgen/htdrv_component.c index 14988d1a4..c1460c2cf 100644 --- a/centrallix/htmlgen/htdrv_component.c +++ b/centrallix/htmlgen/htdrv_component.c @@ -185,7 +185,6 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) char sbuf[128]; pStruct params = NULL; pStruct old_params = NULL; - int i; char* path; int is_toplevel; int old_is_dynamic = 0; @@ -275,14 +274,51 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) params = htcmp_internal_CreateParams(s, tree); /** Any params have expressions? **/ - if (params) + if (params != NULL) { - for(i=0;inSubInf;i++) + for (int i = 0; i < params->nSubInf; i++) { + /** TODO: Israel - Add error handling. **/ htrCheckAddExpression(s, tree, name, params->SubInf[i]->Name); } } + + /** Write styles for the enclosing div. **/ + htrAddStylesheetItem_va(s, + "\t#cmp%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:visible; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ); + + /** Write enclosing div event CSS. **/ + htrAddStylesheetItem_va(s, + "\t#cmp%POSbase { " + "pointer-events:none; " + "}\n" + "\t#cmp%POSbase > * { " + "pointer-events:auto; " + "}\n", + id, id + ); + + /** Write enclosing div to isolate children. **/ + htrAddWgtrObjLinkage_va(s, tree, "cmp%POSbase", id); + htrAddBodyItem_va(s, "
\n", id); + /** If static mode, load the component **/ if (is_static) { @@ -305,13 +341,27 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Init component **/ htrAddScriptInit_va(s, - " cmp_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), is_static:true, allow_multi:false, auto_destroy:false, width:%INT, height:%INT, xpos:%INT, ypos:%INT});\n", - name, w,h,x,y); + "cmp_init({ " + "node:wgtrGetNodeRef(ns,\"%STR&SYM\"), " + "is_static:true, " + "allow_multi:false, " + "auto_destroy:false, " + "width:%INT, " + "height:%INT, " + "xpos:%INT, " + "ypos:%INT, " + "});\n", + name, + w, + h, + x, + y + ); /** Are there any templates we should use **/ memcpy(&wgtr_params, s->ClientInfo, sizeof(wgtr_params)); memset(wgtr_params.Templates, 0, sizeof(wgtr_params.Templates)); - for(i=0;i\n", id); /** Check param references **/ htcmp_internal_CheckReferences(cmp_tree, params, s->Namespace->DName); @@ -392,9 +418,6 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Switch the namespace back **/ htrLeaveNamespace(s); - /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); - /** End Init component **/ htrAddScriptInit_va(s, " cmp_endinit(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", name); @@ -411,41 +434,68 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) s->IsDynamic = old_is_dynamic; old_is_dynamic = 0; } + /** Dynamic mode, component is loaded by the client. **/ else { /** Init component **/ htrAddScriptInit_va(s, - " cmp_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), is_top:%POS, is_static:false, allow_multi:%POS, auto_destroy:%POS, path:\"%STR&JSSTR\", loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), \"cmp%POS\"), width:%INT, height:%INT, xpos:%INT, ypos:%INT});\n", - name, is_toplevel, allow_multi, auto_destroy, cmp_path, - name, id, - w, h, x, y); + "cmp_init({ " + "node:wgtrGetNodeRef(ns, '%STR&SYM'), " + "is_top:%POS, " + "is_static:false, " + "allow_multi:%POS, " + "auto_destroy:%POS, " + "path:'%STR&JSSTR', " + "loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'cmp%POS'), " + "width:%INT, " + "height:%INT, " + "xpos:%INT, " + "ypos:%INT, " + "});\n", + name, + is_toplevel, + allow_multi, + auto_destroy, + cmp_path, + name, id, + w, + h, + x, + y + ); - /** Add template paths **/ - for(i=0;inSubInf;i++) + for (int i = 0; i < params->nSubInf; i++) { - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").AddParam(\"%STR&SYM\",%[null%]%[\"%STR&HEX\"%]);\n", - name, params->SubInf[i]->Name, !params->SubInf[i]->StrVal, params->SubInf[i]->StrVal, - params->SubInf[i]->StrVal); + htrAddScriptInit_va(s, + "wgtrGetNodeRef(ns, '%STR&SYM').AddParam('%STR&SYM', %[null%]%['%STR&HEX'%]);\n", + name, params->SubInf[i]->Name, !params->SubInf[i]->StrVal, params->SubInf[i]->StrVal, params->SubInf[i]->StrVal + ); } } /** Dynamic mode -- load from client **/ htrAddWgtrCtrLinkage(s, tree, "_parentctr"); htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "cmp%POS", id, NULL, ""); - htrAddStylesheetItem_va(s,"\t#cmp%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n", id); + htrAddStylesheetItem_va(s,"\t#cmp%POS { position:absolute; visibility:hidden; left:0px; top:0px; width:0px; height:0px; z-index:0;}\n", id); } htrRenderSubwidgets(s, tree, z+1); + + /** End the enclosing div. **/ + htrAddBodyItem(s, "
\n"); rval = 0; @@ -507,4 +557,3 @@ htcmpInitialize() return 0; } - diff --git a/centrallix/htmlgen/htdrv_componentdecl.c b/centrallix/htmlgen/htdrv_componentdecl.c index 546b5cbb3..abd3f0998 100644 --- a/centrallix/htmlgen/htdrv_componentdecl.c +++ b/centrallix/htmlgen/htdrv_componentdecl.c @@ -89,7 +89,7 @@ htcmpdRender(pHtSession s, pWgtrNode tree, int z) int i, j; int rval = 0; int is_visual = 1; - char gbuf[256]; + char gbuf[256] = ""; char* gname; char* xptr; char* yptr; @@ -184,11 +184,37 @@ htcmpdRender(pHtSession s, pWgtrNode tree, int z) gname=""; htrAddWgtrCtrLinkage(s, tree, "_parentctr"); } + + /** Warning message. **/ + if (gname[0] == '\0') + { + fprintf(stderr, + "Warning: No value specified for gname, which is required to " + "be a valid symbol. Expect a printing failure.\n" + ); + } /** Init component **/ - htrAddScriptInit_va(s, " cmpd_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {vis:%POS, gns:%[\"%STR&SYM\"%]%[null%], gname:'%STR&SYM'%[, expe:'%STR&SYM'%]%[, expa:'%STR&SYM'%]%[, expp:'%STR&SYM'%]%[, applyhint:'%STR&SYM'%]});\n", - name, is_visual, *gbuf, gbuf, !*gbuf, gname, *expose_events_for, expose_events_for, *expose_actions_for, expose_actions_for, - *expose_props_for, expose_props_for, *apply_hints_to, apply_hints_to); + htrAddScriptInit_va(s, + "cmpd_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "vis:%POS, " + "gns:%['%STR&SYM'%]%[null%], " + "gname:'%STR&SYM', " + "%[expe:'%STR&SYM', %]" + "%[expa:'%STR&SYM', %]" + "%[expp:'%STR&SYM', %]" + "%[applyhint:'%STR&SYM', %]" + "});\n", + name, + is_visual, + (gbuf[0] != '\0'), gbuf, (gbuf[0] == '\0'), + gname, + (expose_events_for[0] != '\0'), expose_events_for, + (expose_actions_for[0] != '\0'), expose_actions_for, + (expose_props_for[0] != '\0'), expose_props_for, + (apply_hints_to[0] != '\0'), apply_hints_to + ); + #if 0 for (i=0;iChildren));i++) { @@ -427,4 +453,3 @@ htcmpdInitialize() return 0; } - From aac68d9db876d99b0ac2dfeb76693b3210211311 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 11:17:25 -0700 Subject: [PATCH 032/107] Make the scrollpane widget responsive. Rewrite scrollpane widget C code. Add mouse wheel scrolling to scrollpanes. Add settings to control scrolling in htdrv_scrollpane.js. Add ability to use any widget inside a scrollpane. Add ScrollTo action to scrollpanes. Add Scroll, Wheel, and Click events to scrollpanes. Fix scroll events supposedly existing despite having no code to implement them. Move JS globals to be declared in JS, closer to where they are used. Refactor JS code to use more subfunctions leading to cleaner, clearer logic. Improve event filtering to allow wheel events from children inside the scollpane. Improve code consistency. --- centrallix-doc/Widgets/widgets.xml | 14 +- centrallix-os/sys/js/htdrv_scrollpane.js | 790 ++++++++++++++++------- centrallix/htmlgen/htdrv_scrollpane.c | 531 ++++++++------- 3 files changed, 866 insertions(+), 469 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index 3dbb50a24..a6d917ce4 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -3394,21 +3394,31 @@ $Version=2$ - Scrolls to a specific location determined by the scroll bar. + Scrolls to a specific location determined by the scroll bar. Specify the 'Percent' attribute to indicate how far to scroll in decimal representation (so 1.00 is 100%, aka. the bottom of the page). Specify 'Offset' how many pixels the content should be offset from the top (specify 100 to scroll the first 100 px of content off the top of the scroll pane). Specify 'RangeStart' and 'RangeEnd' to scroll to within the pixel range (using the same units as offset). Keep in mind that this action will trigger a scroll event to occur. + + This event occurs any time the user scrolls the scroll pane. This includes scrolling by clicking the scroll buttons, clicking on the scroll bar, dragging the scroll thumb, turning the scroll wheel, or when the ScrollTo action is used. This event does not occur when the scroll pane moves because the contained content changed in length, or when the scroll pane is forced to scroll because the available visible area was resized. This event will never occur if the content within the scroll pane is shorter than the available visible area because then the content cannot be scrolled. + This event provides the :Percent attribute, a number from 0 to 100 (the same as the ScrollTo action above) representing the percentage that the user has now scrolled down the page as of the event occuring. This event also provides :Change, representing how much the user's scroll location has changed in the same unit as above (although this value will be negative if the user scrolled up). + + This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. + + This event occurs when the user moves the scroll wheel while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. - This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. + This event occurs when the user moves the mouse pointer while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. This event occurs when the user moves the mouse pointer off of the widget. This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. This event occurs when the user releases the mouse button on the widget. + + Note: The Click, Wheel, MouseDown, and MouseUp events provide several pieces of useful information, including :shiftKey, :ctrlKey, :altKey, and :metaKey, which are 1 if the respective key is held down and 0 otherwise. These events also provide :button, a number representing the button number that the user used to execute the event (which appears to always be 0 for wheel). diff --git a/centrallix-os/sys/js/htdrv_scrollpane.js b/centrallix-os/sys/js/htdrv_scrollpane.js index ccbf0dba5..c83dba939 100644 --- a/centrallix-os/sys/js/htdrv_scrollpane.js +++ b/centrallix-os/sys/js/htdrv_scrollpane.js @@ -9,315 +9,615 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -function sp_init(param) + +/** ===== Config ===== **/ + +/** Whether to ignore zooming (ctrl + scroll) for scrolling events. **/ +const sp_ignore_zoom = true; + +/*** The overall scroll speed percentage modifier applied any time the scroll + *** wheel is used, in decimal representation (so 1.00 is 100%). + ***/ +const sp_scroll_mod = 1.00; + +/*** The scroll speed percentage modifier applied for fast scrolling (aka. + *** when using the scroll wheel while holding the alt key), using decimal + *** representation (so 1.00 is 100%). + *** + *** Note: Chromium-based browsers often use alt to scroll horizontally, so + *** fast scrolling may not function there. + ***/ +const sp_fast_scroll_mod = 8.00; + +/*** The scroll percentage modifier applied for inverted scrolling (aka. when + *** using the scroll wheel while holding down the shift key), using decimal + *** representation (so 1.00 is 100%). + *** + *** Set this to a positive number to disable inverted scrolling. + ***/ +const sp_inverted_scroll_mod = -1.00; + + + +/** ===== Globals ===== **/ + +/*** If a drag is in progress, stores the target image for the drag. Equals + *** undefined if no drag is in progress. + ***/ +let sp_drag_img = undefined; + +/*** If a button is pressed, stores the target image for the button so that it + *** can be reset when it is no longer pressed. Equals undefined if no button + *** is currently being pressed. + ***/ +let sp_button_img = undefined; + +/*** Stores the mainlayer that the mouse is currently over. Equals undefined if + *** the mouse is not over a scroll pane. + ***/ +let sp_target_mainlayer = undefined; + +/*** True if a click might be in progress. + *** + *** A click requires the user to generate a MouseDown event and a MouseUp + *** event in succession that are both on the scroll pane widget. + ***/ +let sp_click_in_progress = false; + + +/** Event handler to updates the thumb when the scroll pane resizes. **/ +const sp_handle_resize = ({ target: layer }) => layer.UpdateThumb(); +const sp_resize_observer = new ResizeObserver((entries) => entries.forEach(sp_handle_resize)); + +function sp_init({ layer: pane, area_name, thumb_name }) { - var l = param.layer; - var alayer=null; - var tlayer=null; - var ml; - var img; - var i; - if(cx__capabilities.Dom0NS) - { - var layers = pg_layers(l); - for(i=0;i maxBottom) maxBottom = bottom; + } + area.content_height = Math.max(0, Math.round(maxBottom - top)); + + /** Watch for changes in content height. **/ + if (cx__capabilities.Dom0IE) + { + area.runtimeStyle.clip.pane = pane; + // how to watch this in IE? + area.runtimeStyle.clip.onpropertychange = sp_WatchHeight; } else - { - alayer.clip.pane = l; - alayer.clip.watch("height",sp_WatchHeight); - } + { + area.clip.pane = pane; + area.clip.watch("height", sp_WatchHeight); + } + + /** Watch for changes in the size of the scroll pane. **/ + sp_resize_observer.observe(pane); } -/** @returns The height of the scrollpane content (including content outside the visibile area). **/ -// Replaces getClipHeight(area) + getClipTop(area) -function sp_get_content_height(area) { - return getClipHeight(area) + getClipTop(area); -} +/** ========== Getter functions ========== **/ +/** Functions to compute common values needed often in this code. **/ -/** @returns The height of visible area in the scrollpane. **/ -// Replaces getClipHeight(pane) -function sp_get_total_height(pane) { - return parseInt(window.getComputedStyle(pane).height); -} +/** @returns The height of content inside the scroll pane (even if not all of it is visible). **/ +function sp_get_content_height(area) + { + return area.content_height; + } -/** @returns The height of visible area in the scroll bar. **/ -function sp_get_sb_height(pane) { - return sp_get_total_height(pane) - (3*18); -} +/** @returns The height of visible area available to the scroll pane. **/ +function sp_get_available_height(pane) + { + return parseInt(getComputedStyle(pane).height); + } -function sp_action_scrollto(aparam) +/** @returns The height of the content outside the available visible area of the scroll pane. **/ +function sp_get_nonvisible_height(pane) { - var h = sp_get_content_height(this.area); // height of content - var ch = sp_get_total_height(this); - var d = h-ch; // height of non-visible content (max scrollable distance) - if (d < 0) d=0; - if (typeof aparam.Percent != 'undefined') - { - if (aparam.Percent < 0) aparam.Percent = 0; - else if (aparam.Percent > 100) aparam.Percent = 100; - setRelativeY(this.area, -d*aparam.Percent/100); - } - else if (typeof aparam.Offset != 'undefined') - { - if (aparam.Offset < 0) aparam.Offset = 0; - else if (aparam.Offset > d) aparam.Offset = d; - setRelativeY(this.area, -aparam.Offset); - } - else if (typeof aparam.RangeStart != 'undefined' && typeof aparam.RangeEnd != 'undefined') - { - var ny = -getRelativeY(this.area); - if (ny + ch < aparam.RangeEnd) ny = aparam.RangeEnd - ch; - if (ny > aparam.RangeStart) ny = aparam.RangeStart; - if (ny < 0) ny = 0; - if (ny > d) ny = d; - setRelativeY(this.area, -ny); - } - this.UpdateThumb(h); + return sp_get_content_height(pane.area) - sp_get_available_height(pane); } -function sp_WatchHeight(property, oldvalue, newvalue) +/** @returns The height of visible area available to the scroll bar. **/ +function sp_get_scrollbar_height(pane) { - if (cx__capabilities.Dom0IE) - { - newvalue = htr_get_watch_newval(window.event); - } - - // make sure region not offscreen now - newvalue += getClipTop(this.pane.area); - if (getRelativeY(this.pane.area) + newvalue < sp_get_total_height(this.pane)) { - setRelativeY(this.pane.area, sp_get_total_height(this.pane) - newvalue); + /** The up and down buttons and thumb are each 18px. **/ + return sp_get_available_height(pane) - (3*18); } - if (newvalue < sp_get_total_height(this.pane)) setRelativeY(this.pane.area, 0); - this.pane.UpdateThumb(newvalue); - newvalue -= getClipTop(this.pane.area); - this.bottom = this.top + newvalue; /* ns seems to unlink bottom = top + height if you modify clip obj */ - return newvalue; + +/** @returns The distance down that the scroll pane has been scrolled. **/ +function sp_get_scroll_dist(area) + { + return -getRelativeY(area); } -function sp_UpdateThumb(h) + +/*** Update the scroll pane to handle the height of its contained content + *** (aka. its child widgets) changing. + *** + *** @param property Unused + *** @param old_value The old height of the child content (unused). + *** @param new_value The new height of the child content. + ***/ +function sp_WatchHeight(property, old_value, new_value) { - /** 'this' is a spXpane **/ - if(!h) - { /** if h is supplied, it is the soon-to-be clip.height of the spXarea **/ - h=sp_get_content_height(this.area); // height of content - } - var d=h-sp_get_total_height(this); // height of non-visible content (max scrollable distance) - var v=sp_get_sb_height(this); - if(d<=0) - setRelativeY(this.thum, 18); - else - setRelativeY(this.thum, 18+v*(-getRelativeY(this.area)/d)); + const { pane } = this; + const { area } = pane; + + /** Handle legacy Internet Explorer behavior. **/ + if (cx__capabilities.Dom0IE) + new_value = htr_get_watch_newval(window.event); + + /** Update the internal content height value. **/ + area.content_height = new_value; + + /** Get the available height of the visible area. **/ + const available_height = sp_get_available_height(pane); + + /** Scroll to the top if the content is now smaller than the visible area. **/ + if (getRelativeY(area) + new_value < available_height) + setRelativeY(area, available_height - new_value); + if (new_value < available_height) setRelativeY(area, 0); + + /** Update the scroll thumb. **/ + pane.UpdateThumb(); + + return new_value; } -function do_mv() +/** Called when the ScrollTo action is used. **/ +function sp_action_ScrollTo({ Percent, Offset, RangeStart, RangeEnd }) { + const pane = this; + const available = sp_get_available_height(pane); + const nonvisible_height = sp_get_nonvisible_height(pane); // Height of non-visible content. + + /** Ignore the action if all content is visible. **/ + if (nonvisible_height <= 0) return; + + /** Calculate the new location to scroll to. **/ + const new_scroll_height = + (Offset !== undefined) ? Offset : + (Percent !== undefined) ? Math.clamp(0, Percent / 100, 1) * nonvisible_height : + (RangeStart !== undefined && RangeEnd !== undefined) ? + Math.clamp(RangeEnd - available, sp_get_scroll_dist(pane.area), RangeStart) : + 0; /* Fallback default value. */ + + /** Scroll to the new location. **/ + sp_scroll_to(pane, new_scroll_height); + } + +/** Recalculate and update the location of the scroll thumb. **/ +function sp_update_thumb() + { + const pane = this; + const { area, thumb } = pane; - var ti=sp_target_img; - /** not sure why, but it's getting called with a null sp_target_img sometimes... **/ - if(!ti) + /** Get the height of nonvisible content. **/ + const nonvisible_height = sp_get_nonvisible_height(pane); + + /** Handle the case where all content is visible. **/ + if (nonvisible_height <= 0) { + /** All we need to do is to move the scroll thumb to the top. **/ + setRelativeY(thumb, 18); return; } - var h=sp_get_content_height(ti.area); // height of content - var d=h-sp_get_total_height(ti.pane); // height of non-visible content (max scrollable distance) - var incr=sp_mv_incr; - if(d<0) - incr=0; - if (ti.kind=='sp') + + /** Calculate where the scroll thumb should be based on the scroll progress. **/ + let scroll_dist = sp_get_scroll_dist(area); + if (scroll_dist > nonvisible_height) { - var scrolled = -getRelativeY(ti.area); // distance scrolled already - if(incr > 0 && scrolled+incr>d) - incr=d-scrolled; + /** Scroll down to fill the new space at the bottom of the scroll pane. **/ + setRelativeY(area, -nonvisible_height); + scroll_dist = nonvisible_height; + } + const progress = scroll_dist / nonvisible_height; + const progress_scaled = 18 + sp_get_scrollbar_height(pane) * progress; + + /** Set the scroll thumb to the calculated location. **/ + setRelativeY(thumb, progress_scaled); + } - /** if we've scrolled down less than we want to go up, go up the distance we went down **/ - if(incr < 0 && scrolled<-incr) - incr=-scrolled; +/*** Scroll the scroll pane to the specified `scroll_height`. + *** + *** @param pane The affected scroll pane DOM node. + *** @param scroll_height The new height, in pixels, that the content should + *** be scrolled to as a result of this scroll. + ***/ +function sp_scroll_to(pane, scroll_height) + { + /** Ignore undefined target pane. **/ + if (!pane) return; + + /** Don't scroll if there's no content to scroll. **/ + const nonvisible_height = sp_get_nonvisible_height(pane); + if (nonvisible_height < 0) return; + + /** Save the current scroll amount for later. **/ + const scroll_height_old = sp_get_scroll_dist(pane.area); + + /** Clamp the scroll height within the bounds of the scroll bar. **/ + const scroll_height_new = Math.clamp(0, scroll_height, nonvisible_height); + + /** Update the content. **/ + setRelativeY(pane.area, -scroll_height_new); + pane.UpdateThumb(); + + /** Construct the param for the centrallix 'Scroll' event. **/ + const percent_old = (scroll_height_old / nonvisible_height) * 100; + const percent_new = (scroll_height_new / nonvisible_height) * 100; + const param = { Percent: percent_new, Change: percent_new - percent_old }; + + /** Schedule the scroll event to allow the page to repaint first. **/ + setTimeout(() => cn_activate(pane, 'Scroll', param), 0); + } - /*var layers = pg_layers(ti.pane); - for(var i=0;i getPageY(ti.pane)+18+v) new_y=getPageY(ti.pane)+18+v; - if (new_y < getPageY(ti.pane)+18) new_y=getPageY(ti.pane)+18; - setPageY(ti.thum,new_y); - var h=sp_get_content_height(ti.area); - var d=h-sp_get_total_height(ti.pane); - if (d<0) d=0; - var yincr = (((getRelativeY(ti.thum)-18)/v)*-d) - getRelativeY(ti.area); - moveBy(ti.area, 0, yincr); - return EVENT_HALT | EVENT_PREVENT_DEFAULT_ACTION; - } + /** Trigger Centrallix events. **/ + const pane = sp_get_pane(e); + if (pane) cn_activate(pane, 'MouseMove'); + + /** Monitor events on other DOM nodes to detect MouseOut. **/ + if (!pane && sp_target_mainlayer) + { + /** Mouse out has occurred, ending the mouse over. **/ + cn_activate(sp_target_mainlayer, 'MouseOut'); + sp_target_mainlayer = undefined; + } + + /** Check if a drag is in progress (aka. the drag target exists and is a scroll thumb). **/ + const target_img = sp_drag_img; + if (target_img && target_img.kind === 'sp' && target_img.name === 't') + { + const { pane } = target_img; + + /** Get drag_dist: the distance that the scroll bar should move. **/ + const page_y = getPageY(target_img.thumb); + const drag_dist = e.pageY - page_y; + + /** Scale drag_dist to the distance that the content should move. **/ + const scrollbar_height = sp_get_scrollbar_height(pane); + const nonvisible_height = sp_get_nonvisible_height(pane); + const content_drag_dist = (drag_dist / scrollbar_height) * nonvisible_height; + + /** Scroll the content by the required amount to reach the new mouse location. **/ + sp_scroll(target_img.pane, content_drag_dist); + + /** Event handled. **/ + return EVENT_HALT | EVENT_PREVENT_DEFAULT_ACTION; + } + + /** Continue the event. **/ return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; } +/*** Handles mouse up events anywhere on the page. + *** + *** @param e The event that has occurred. + *** @returns An event result (see ht_render.js). + ***/ function sp_mouseup(e) { - if (sp_mv_timeout != null) - { - clearTimeout(sp_mv_timeout); - sp_mv_timeout = null; - sp_mv_incr = 0; - } - if (sp_target_img != null) - { - if (sp_target_img.name != 'b') - pg_set(sp_target_img,'src',htutil_subst_last(sp_target_img.src,"b.gif")); - sp_target_img = null; - } - if (e.kind == 'sp') cn_activate(e.mainlayer, 'MouseUp'); + /** Trigger Centrallix events. **/ + if (e.kind === 'sp') + { + const params = sp_get_event_params(e); + cn_activate(e.mainlayer, 'MouseUp', params); + if (sp_click_in_progress) + cn_activate(e.mainlayer, 'Click', params); + } + + /** A click is no longer in progress. **/ + sp_click_in_progress = false; + + /** Check for an active drag. **/ + if (sp_drag_img) + { + /** End the drag. **/ + sp_drag_img = undefined; + } + + /** Check for a pressed button. **/ + if (sp_button_img) + { + /** Reset the pressed button. **/ + pg_set(sp_button_img, 'src', htutil_subst_last(sp_button_img.src, "b.gif")); + sp_button_img = undefined; + } + + /** Continue the event. **/ return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; } +/*** Handles mouse over events anywhere on the page. + *** + *** @param e The event that has occurred. + *** @returns An event result (see ht_render.js). + ***/ function sp_mouseover(e) { - if (e.kind == 'sp') - { - if (!sp_cur_mainlayer) - { - cn_activate(e.mainlayer, 'MouseOver'); - sp_cur_mainlayer = e.mainlayer; - } - } + /** Check for mouse over on an sp element. **/ + if (sp_target_mainlayer && e.kind === 'sp') + { + /** Begin a mouse over. **/ + cn_activate(e.mainlayer, 'MouseOver'); + sp_target_mainlayer = e.mainlayer; + } + + /** Continue the event. **/ return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; } -// Load indication +/** Indicate loading is complete. **/ if (window.pg_scripts) pg_scripts['htdrv_scrollpane.js'] = true; diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index e5c061d16..2a4b36154 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -1,15 +1,3 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ @@ -42,6 +30,19 @@ /* layer. Can contain most objects, except for framesets. */ /************************************************************************/ +#include +#include +#include +#include + +#include "cxlib/mtask.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxlib/xhash.h" +#include "ht_render.h" +#include "obj.h" + /** globals **/ static struct @@ -57,267 +58,351 @@ int htspaneRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; - char name[64]; - int x,y,w,h; - int id, i; - int visible = 1; - char bcolor[64] = ""; - char bimage[64] = ""; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE &&!(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) + + if (!(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS) && !s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE) { - mssError(1,"HTSPANE","Netscape DOM or W3C DOM1 HTML and DOM2 CSS support required"); + mssError(1, "HTSPANE", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS or IE DOM or Netscape DOM required."); return -1; } - - /** Get an id for this. **/ - id = (HTSPANE.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) + + /** Get an id for this scrollpane. **/ + const int id = (HTSPANE.idcnt++); + + /** Get name. **/ + char name[64]; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have an 'x' property"); + mssError(1, "HTSPANE", "widget/scrollpane must have a 'name' property."); return -1; } - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) + strtcpy(name, ptr, sizeof(name)); + + /** Get x, y, w, & h. **/ + int x, y, w, h; + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'y' property"); + mssError(1, "HTSPANE", "widget/scrollpane must have an 'x' property."); return -1; } - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'width' property"); + mssError(1, "HTSPANE", "widget/scrollpane must have a 'y' property."); return -1; } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'height' property"); + mssError(1, "HTSPANE", "widget/scrollpane must have a 'width' property."); return -1; } - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Check background color **/ - if (wgtrGetPropertyValue(tree,"bgcolor",DATA_T_STRING,POD(&ptr)) == 0) + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) { - strtcpy(bcolor,ptr,sizeof(bcolor)); + mssError(1, "HTSPANE", "widget/scrollpane must have a 'height' property."); + return -1; } - if (wgtrGetPropertyValue(tree,"background",DATA_T_STRING,POD(&ptr)) == 0) + + /** Get the background color or image. **/ + char background_color[64] = ""; + if (wgtrGetPropertyValue(tree, "bgcolor", DATA_T_STRING,POD(&ptr)) == 0) { - strtcpy(bimage,ptr,sizeof(bimage)); + strtcpy(background_color, ptr, sizeof(background_color)); } - - /** Marked not visible? **/ - if (wgtrGetPropertyValue(tree,"visible",DATA_T_STRING,POD(&ptr)) == 0) + char background_image[64] = ""; + if (wgtrGetPropertyValue(tree, "background", DATA_T_STRING, POD(&ptr)) == 0) { - if (!strcmp(ptr,"false")) visible = 0; + strtcpy(background_image, ptr, sizeof(background_image)); } - - /** Ok, write the style header items. **/ - if (s->Capabilities.Dom0NS) + + /** Get visibility. **/ + int visible = 1; + if (wgtrGetPropertyValue(tree, "visible", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddStylesheetItem_va(s, - "\t#sp%POSpane { " - "POSITION:absolute; " - "VISIBILITY:%STR; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "HEIGHT:"ht_flex_format"; " - "OVERFLOW: clip; " - "Z-INDEX:%POS; " - "}\n", - id, - (visible) ? "inherit" : "hidden", - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), - z - ); - htrAddStylesheetItem_va(s, - "\t#sp%POSarea { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:0px; " - "TOP:0px; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " - "}\n", - id, - ht_flex(w - 18, ht_get_total_w(tree), 1.0), - z + 1 - ); - htrAddStylesheetItem_va(s, - "\t#sp%POSthum { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:18px; " - "WIDTH:18px; " - "Z-INDEX:%POS; " - "}\n", - id, - ht_flex(w - 18, tree->width, 1.0), - z + 1 - ); + if (strcmp(ptr, "false") == 0) visible = 0; } - - /** Write globals for internal use **/ - htrAddScriptGlobal(s, "sp_target_img", "null", 0); - htrAddScriptGlobal(s, "sp_click_x","0",0); - htrAddScriptGlobal(s, "sp_click_y","0",0); - htrAddScriptGlobal(s, "sp_thum_y","0",0); - htrAddScriptGlobal(s, "sp_mv_timeout","null",0); - htrAddScriptGlobal(s, "sp_mv_incr","0",0); - htrAddScriptGlobal(s, "sp_cur_mainlayer","null",0); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "sp%POSpane",id); + + /** DOM Linkages. **/ htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, \"sp%POSarea\")",id); - + htrAddWgtrObjLinkage_va(s, tree, "sp%POSpane",id); + + /** Include scrollpane script and its dependencies. **/ htrAddScriptInclude(s, "/sys/js/htdrv_scrollpane.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - - htrAddScriptInit_va(s," sp_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), aname:\"sp%POSarea\", tname:\"sp%POSthum\"});\n", name,id,id); - - /** HTML body
elements for the layers. **/ - if(s->Capabilities.Dom0NS) + + /** Init the JS scripts. **/ + htrAddScriptInit_va(s, + "\tsp_init({" + "layer: wgtrGetNodeRef(ns, '%STR&SYM'), " + "area_name: 'sp%POSarea', " + "thumb_name: 'sp%POSthumb', " + "});\n", + name, + id, + id + ); + + /** Write html and styles. **/ + if (s->Capabilities.Dom1HTML) { - htrAddBodyItem_va(s,"
", id, *bcolor, bcolor, *bimage, bimage, w); - htrAddBodyItem(s, "
"); - htrAddBodyItem_va(s,"",h-36); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem_va(s,"
\n
",id,id,w-2,h-2); - } - else if(s->Capabilities.Dom1HTML) - { - //htrAddStylesheetItem_va(s,"\t#sp%dpane { POSITION:absolute; VISIBILITY:%s; LEFT:%dpx; TOP:%dpx; WIDTH:%dpx; HEIGHT:%dpx; clip:rect(0px,%dpx,%dpx,0px); Z-INDEX:%d; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); - //htrAddStylesheetItem_va(s,"\t#sp%darea { HEIGHT: %dpx; WIDTH:%dpx; }\n",id, h, w-18); - //htrAddStylesheetItem_va(s,"\t#sp%dthum { POSITION:absolute; VISIBILITY:inherit; LEFT:%dpx; TOP:18px; WIDTH:18px; Z-INDEX:%dpx; }\n",id,w-18,z+1); + /** Write the scrollpane. **/ htrAddBodyItem_va(s, - "
\n", id, (visible) ? "inherit" : "hidden", - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), z ); - htrAddBodyItem_va(s,"", id); - htrAddBodyItem_va(s,"", id); - htrAddBodyItem_va(s,"", id); + + /** Write shared CSS for the following UI elements. **/ htrAddStylesheetItem_va(s, - "\t#sp%POSup { " - "POSITION:absolute; " - "LEFT:"ht_flex_format"; " - "TOP:0px; " + "\t.sp%POS_scroll { " + "position:absolute; " + "left:"ht_flex_format"; " "}\n", id, ht_flex(w - 18, tree->width, 1.0) ); - htrAddStylesheetItem_va(s, - "\t#sp%POSbar { " - "POSITION:absolute; " - "LEFT:"ht_flex_format"; " - "TOP:18px; " - "WIDTH:18px; " - "HEIGHT:"ht_flex_format"; " - "}\n", + + /** Write the up button. **/ + htrAddBodyItem_va(s, + "", + id, + id + ); + + /** Write the scroll bar. **/ + htrAddBodyItem_va(s, + "", + id, id, - ht_flex(w - 18, tree->width, 1.0), ht_flex(h - 36, tree->height, 1.0) ); - htrAddStylesheetItem_va(s, - "\t#sp%POSdown { " - "POSITION:absolute; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "}\n", + + /** Write the down button. **/ + htrAddBodyItem_va(s, + "", + id, id, - ht_flex(w - 18, tree->width, 1.0), ht_flex(h - 18, tree->height, 1.0) ); + + /** Write the scroll thumb. **/ htrAddBodyItem_va(s, - "
" - "" - "
\n", + "" + "
\n", + id, id, - ht_flex(w - 18, tree->width, 1.0), z + 1 ); + + /** Write the scroll area. **/ htrAddBodyItem_va(s, - "
", id, - ht_flex(w - 18, ht_get_total_w(tree), 1.0), - ht_flex(h, ht_get_total_h(tree), 1.0), + ht_flex(w - 18, ht_get_parent_w(tree), 1.0), + ht_flex(h, ht_get_parent_h(tree), 1.0), z + 1 ); } + else if (s->Capabilities.Dom0NS) + { + /** Write CSS for everything. **/ + htrAddStylesheetItem_va(s, + "\t#sp%POSpane { " + "position:absolute; " + "visibility:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "overflow:clip; " + "z-index:%POS; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSarea { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:0px; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex(w - 18, ht_get_parent_w(tree), 1.0), + z + 1 + ); + htrAddStylesheetItem_va(s, + "\t#sp%POSthumb { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:18px; " + "width:18px; " + "z-index:%POS; " + "}\n", + id, + ht_flex(w - 18, tree->width, 1.0), + z + 1 + ); + + /** Write the scrollpane. **/ + htrAddBodyItem_va(s, + "
" + "", + id, + *background_color, background_color, + *background_image, background_image, + w + ); + + htrAddBodyItem_va(s, + /** Write the up button. **/ + "" + + /** Write the scroll bar. **/ + "" + + /** Write the down button. **/ + "" + + /** Close the scrollpane table (see above). **/ + "
" + "" + "
" + "" + "
" + "" + "
\n", + h - 36 + ); + + /** Write the scroll thumb. **/ + htrAddBodyItem_va(s, + "
" + "" + "
\n", + id + ); + + /** Write the scroll area. **/ + htrAddBodyItem_va(s, + "
" + "
", + id, + w - 2, + h - 2 + ); + } else { - mssError(1,"HTSPNE","Browser not supported"); + mssError(1, "HTSPNE", "Browser not supported!!"); } - - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","sp","sp_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","sp","sp_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","sp","sp_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sp","sp_mouseover"); - - /** Do subwidgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+2); - - /** Finish off the last
**/ - if(s->Capabilities.Dom0NS) + + /** Render children/subwidgets. **/ + for (int i = 0; i < xaCount(&(tree->Children)); i++) + htrRenderWidget(s, xaGetItem(&(tree->Children), i), z + 2); + + /** Close the final
s. **/ + if (s->Capabilities.Dom1HTML) { - htrAddBodyItem(s,"
\n"); + htrAddBodyItem(s,"
\n"); } - else if(s->Capabilities.Dom1HTML) + else if (s->Capabilities.Dom0NS) { - htrAddBodyItem(s,"
\n"); + htrAddBodyItem(s,"
\n"); } else { - mssError(1,"HTSPNE","browser not supported"); + mssError(1, "HTSPNE", "Browser not supported!!"); } - + + /** Add the event handling scripts **/ + htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "sp", "sp_mousedown"); + htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "sp", "sp_mousemove"); + htrAddEventHandlerFunction(s, "document", "MOUSEUP", "sp", "sp_mouseup"); + htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sp", "sp_mouseover"); + htrAddEventHandlerFunction(s, "document", "WHEEL", "sp", "sp_wheel"); + return 0; } @@ -328,29 +413,31 @@ int htspaneInitialize() { pHtDriver drv; - - /** Allocate the driver **/ + + /** Allocate the driver **/ drv = htrAllocDriver(); if (!drv) return -1; - + /** Fill in the structure. **/ - strcpy(drv->Name,"HTML ScrollPane Widget Driver"); + strcpy(drv->Name,"HTML Widget/scrollpane Driver"); strcpy(drv->WidgetName,"scrollpane"); drv->Render = htspaneRender; - + /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - + htrAddEvent(drv, "Scroll"); + htrAddEvent(drv, "Click"); + htrAddEvent(drv, "Wheel"); + htrAddEvent(drv, "MouseUp"); + htrAddEvent(drv, "MouseDown"); + htrAddEvent(drv, "MouseOver"); + htrAddEvent(drv, "MouseOut"); + htrAddEvent(drv, "MouseMove"); + /** Register. **/ htrRegisterDriver(drv); - + htrAddSupport(drv, "dhtml"); HTSPANE.idcnt = 0; - + return 0; } From c079d6e4580ffce867311684ad346a4c37389d81 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 11:22:43 -0700 Subject: [PATCH 033/107] Make the button widget responsive. Replace JS animation with a faster, responsive CSS implementation. Remove unnecessary clip rectangles. Fix style issues. --- centrallix-os/sys/js/htdrv_button.js | 3 -- centrallix/htmlgen/htdrv_button.c | 70 ++++++++++++++-------------- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_button.js b/centrallix-os/sys/js/htdrv_button.js index 6be26b436..2efb27773 100644 --- a/centrallix-os/sys/js/htdrv_button.js +++ b/centrallix-os/sys/js/htdrv_button.js @@ -262,7 +262,6 @@ function gb_setmode(layer,mode) pg_set(layer.img, 'src', newsrc); if(type=='image' || type=='textoverimage') return; } - moveTo(layer,layer.orig_x,layer.orig_y); if(cx__capabilities.Dom2CSS) { layer.style.setProperty('border-width','0px',null); @@ -294,7 +293,6 @@ function gb_setmode(layer,mode) pg_set(layer.img, 'src', newsrc); if(type=='image' || type=='textoverimage' ) return; } - moveTo(layer,layer.orig_x,layer.orig_y); if(cx__capabilities.Dom2CSS) { layer.style.setProperty('border-width','1px',null); @@ -337,7 +335,6 @@ function gb_setmode(layer,mode) pg_set(layer.img, 'src', newsrc); if(type=='image' || type=='textoverimage') return; } - moveTo(layer,layer.orig_x+1,layer.orig_y+1); if(cx__capabilities.Dom2CSS) { layer.style.setProperty('border-width','1px',null); diff --git a/centrallix/htmlgen/htdrv_button.c b/centrallix/htmlgen/htdrv_button.c index 1779b54be..3e3f7c577 100644 --- a/centrallix/htmlgen/htdrv_button.c +++ b/centrallix/htmlgen/htdrv_button.c @@ -152,12 +152,12 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s, "\t#gb%POSpane { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), @@ -165,6 +165,14 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), z ); + + /** Button click animation. **/ + if (is_enabled) { + htrAddStylesheetItem_va(s, + "\t#tb%POSpane:active { transform: translate(1px, 1px); }\n", + id + ); + } htrAddScriptGlobal(s, "gb_cur_img", "null", 0); htrAddScriptGlobal(s, "gb_current", "null", 0); @@ -208,12 +216,12 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s, "\t#gb%POSpane2 { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:%POS; " - "TOP:%POS; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "left:%POS; " + "top:%POS; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, 0, @@ -337,26 +345,18 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s, "\t#gb%POSpane { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "OVERFLOW:hidden; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "CLIP:rect(" - "0, " - ht_flex_format", " - ht_flex_format", " - "0" - "); " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(w-1-2*box_offset+2*clip_offset, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(h-1-2*box_offset+2*clip_offset, tree->Parent->height, ht_get_fl_h(tree)), z ); htrAddStylesheetItem_va(s,"\t#gb%POSpane2, #gb%POSpane3 { height: "ht_flex_format";}\n",id,id,ht_flex(h-3,tree->Parent->height,ht_get_fl_h(tree))); @@ -366,20 +366,18 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s, "\t#gb%POSpane { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "OVERFLOW:hidden; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "CLIP:rect(0, "ht_flex_format", auto, 0);" - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), - ht_flex(w-1-2*box_offset+2*clip_offset, tree->Parent->width, ht_get_fl_w(tree)), z ); } From 00a75594196c43c7ff702563b285e0f16530e3b5 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 11:44:29 -0700 Subject: [PATCH 034/107] Make the chart widget responsive. Update page generation to produce responsive HTML and CSS. Add JS attributes to tell ChartJS how to resize the widget. --- centrallix-os/sys/js/htdrv_chart.js | 6 +++ centrallix/htmlgen/htdrv_chart.c | 68 ++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_chart.js b/centrallix-os/sys/js/htdrv_chart.js index 3b6d0abe3..87d7cb9ef 100755 --- a/centrallix-os/sys/js/htdrv_chart.js +++ b/centrallix-os/sys/js/htdrv_chart.js @@ -387,6 +387,12 @@ function cht_init(params) { this.update_soon = false; //see cht_object_available chart_wgt.ChartJsInit(); + + // Set ChartJS options. + const { options } = chart_wgt.chart; + options.responsive = false; + options.maintainAspectRatio = false; + options.resizeDelay = 10; } // Load indication diff --git a/centrallix/htmlgen/htdrv_chart.c b/centrallix/htmlgen/htdrv_chart.c index 073627a20..d421643cf 100644 --- a/centrallix/htmlgen/htdrv_chart.c +++ b/centrallix/htmlgen/htdrv_chart.c @@ -323,27 +323,54 @@ htchtScriptInclude(pHtSession session) void htchtGenHTML(pHtSession session, pWgtrNode tree, int z) { - char buf[32]; - - htchtGetCanvasId(tree, buf, sizeof(buf)); - - htrAddBodyItem_va(session,"
\n", - buf, - buf, - htchtGetWidth(tree), - htchtGetHeight(tree) + /** Get id. **/ + char id[32]; + htchtGetCanvasId(tree, id, sizeof(id)); + + /** Get layout data. **/ + const int x = htchtGetX(tree); + const int y = htchtGetY(tree); + const int w = htchtGetWidth(tree); + const int h = htchtGetHeight(tree); + + /** Write style rules for the container div. **/ + htrAddStylesheetItem_va(session, + "\t#%STR&SYMdiv { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z ); - - htrAddBodyItem(session,"

CHART HERE

\n"); - htrAddBodyItem(session,"
\n"); - - htrAddStylesheetItem_va(session, "\t#%STR&SYMdiv { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; } \n", - buf, - htchtGetX(tree), - htchtGetY(tree), - htchtGetWidth(tree), - htchtGetHeight(tree), - z + + /** Write the canvas HTML. **/ + /*** Israel: Dark magic and sorcery beyond my comprehension somehow + *** cause "CHART HERE" to render as the label for the chart. + ***/ + htrAddBodyItem_va(session, + "
" + "" + "

CHART HERE

" + "
" + "
", + id, + id, + w, + h ); } @@ -405,4 +432,3 @@ htchtInitialize() return 0; } - From 30df51aca1358f8dbd4c121162b515e2d3bb78aa Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 11:46:20 -0700 Subject: [PATCH 035/107] Make the HTML widget responsive. Improve page generation methodology, leading to responsive pages. Improve C code formatting. --- centrallix-os/sys/js/htdrv_html.js | 2 + centrallix/htmlgen/htdrv_html.c | 119 ++++++++++++++++------------- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_html.js b/centrallix-os/sys/js/htdrv_html.js index 3289d0b61..1300a79a3 100644 --- a/centrallix-os/sys/js/htdrv_html.js +++ b/centrallix-os/sys/js/htdrv_html.js @@ -300,6 +300,8 @@ function ht_init(param) //l.watch('source', ht_sourcechanged); pg_resize(l.parentLayer); + disableClippingCSS(l.parentLayer); + return l; } diff --git a/centrallix/htmlgen/htdrv_html.c b/centrallix/htmlgen/htdrv_html.c index 0b41d7b35..509438673 100644 --- a/centrallix/htmlgen/htdrv_html.c +++ b/centrallix/htmlgen/htdrv_html.c @@ -101,76 +101,52 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) { /** Only give x and y if supplied. **/ if (x < 0 || y < 0) - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(w,ht_get_total_w(tree),ht_get_fl_w(tree)),z+1); - } - else { htrAddStylesheetItem_va(s, - "\t#ht%POSpane { " - "POSITION:absolute; " + "\t#ht%POSpane, #ht%POSpane, #ht%POSfader { " + "POSITION:relative; " "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " "WIDTH:"ht_flex_format"; " "Z-INDEX:%POS; " "}\n", - id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + id, id, id, + ht_flex_w(w, tree), z ); + } + else + { htrAddStylesheetItem_va(s, - "\t#ht%POSpane2 { " + "\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { " "POSITION:absolute; " - "VISIBILITY:hidden; " + "VISIBILITY:inherit; " "LEFT:"ht_flex_format"; " "TOP:"ht_flex_format"; " "WIDTH:"ht_flex_format"; " "Z-INDEX:%POS; " "}\n", - id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), + id, id, id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), z ); - htrAddStylesheetItem_va(s, - "\t#ht%POSfader { " - "POSITION:absolute; " - "VISIBILITY:hidden; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " - "}\n", - id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - z+1 - ); } if (s->Capabilities.CSS1) { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { overflow:hidden; }\n",id); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { overflow:hidden; }\n",id); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { overflow:hidden; }\n",id); + htrAddStylesheetItem_va(s,"\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { overflow:hidden; }\n", id, id, id); htrAddStylesheetItem_va(s,"\t#ht%POSloader { overflow:hidden; visibility:hidden; position:absolute; top:0px; left:0px; width:0px; height:0px; }\n", id); } /** Write named global **/ htrAddWgtrObjLinkage_va(s, tree, "ht%POSpane",id); - htrAddScriptGlobal(s, "ht_fadeobj", "null", 0); - + htrAddScriptGlobal(s, "ht_fadeobj", "null", 0); + htrAddScriptInclude(s, "/sys/js/htdrv_html.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - + /** Event handler for click-on-link. **/ htrAddEventHandlerFunction(s, "document","CLICK","ht","ht_click"); htrAddEventHandlerFunction(s,"document","MOUSEOVER","ht","ht_mouseover"); @@ -178,16 +154,53 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s,"document","MOUSEMOVE","ht", "ht_mousemove"); htrAddEventHandlerFunction(s,"document","MOUSEDOWN","ht", "ht_mousedown"); htrAddEventHandlerFunction(s,"document","MOUSEUP", "ht", "ht_mouseup"); - - /** Script initialization call. **/ - if (s->Capabilities.Dom0NS) - htrAddScriptInit_va(s," ht_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSpane2\"), faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSfader\"), source:\"%STR&JSSTR\", width:%INT, height:%INT, loader:null});\n", - name, name, id, name, id, - src, w,h); + + /** Script initialization call. **/ + if (s->Capabilities.Dom1HTML) + { + htrAddScriptInit_va(s, + "ht_init({" + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'ht%POSpane2'), " + "faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'ht%POSfader'), " + "source:'%STR&JSSTR', " + "width:%INT, " + "height:%INT, " + "loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'ht%POSloader')" + "});\n", + name, + name, id, + name, id, + src, + w, + h, + name, id + ); + } + else if (s->Capabilities.Dom0NS) + { + htrAddScriptInit_va(s, + "ht_init({" + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'ht%POSpane2'), " + "faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), 'ht%POSfader'), " + "source:'%STR&JSSTR', " + "width:%INT, " + "height:%INT, " + "loader:null" + "});\n", + name, + name, id, + name, id, + src, + w, + h + ); + } else - htrAddScriptInit_va(s," ht_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSpane2\"), faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSfader\"), source:\"%STR&JSSTR\", width:%INT, height:%INT, loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), \"ht%POSloader\")});\n", - name, name, id, name, id, - src, w,h, name, id); + { + mssError(1, "HTHTML", "Browser not supported!!"); + } /** HTML body
element for the layer. **/ htrAddBodyItem_va(s,"
",id); @@ -214,14 +227,14 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) } /** If source is an objectsystem entry... **/ - if (src[0] && strncmp(src,"http:",5) && strncmp(src,"debug:",6)) + if (src[0] && strncmp(src, "http:", 5) != 0 && strncmp(src, "debug:", 6) != 0) { content_obj = objOpen(s->ObjSession,src,O_RDONLY,0600,"text/html"); if (content_obj) { - while((cnt = objRead(content_obj, sbuf, 159,0,0)) > 0) + while ((cnt = objRead(content_obj, sbuf, 159, 0, 0)) > 0) { - sbuf[cnt]=0; + sbuf[cnt] = 0; htrAddBodyItem(s, sbuf); } objClose(content_obj); From a44cbd0e795fe7a1fa354c3d1e6bc846eef49aa1 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:03:39 -0700 Subject: [PATCH 036/107] Make checkbox widget responsive. Update C to generate responsive CSS rules. Add an error for unexpected 'checked' values. --- centrallix/htmlgen/htdrv_checkbox.c | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/centrallix/htmlgen/htdrv_checkbox.c b/centrallix/htmlgen/htdrv_checkbox.c index c2f699e91..d5f164716 100644 --- a/centrallix/htmlgen/htdrv_checkbox.c +++ b/centrallix/htmlgen/htdrv_checkbox.c @@ -90,12 +90,29 @@ int htcbRender(pHtSession s, pWgtrNode tree, int z) { /** Write named global **/ htrAddWgtrObjLinkage_va(s, tree, "cb%INTmain", id); - - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#cb%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:13px; WIDTH:13px; Z-INDEX:%POS; }\n",id,x,y,z); + + /** Write style header. **/ + htrAddStylesheetItem_va(s, + "\t#cb%POSmain { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "height:13px; " + "width:13px; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + z + ); + + /** Include scripts. **/ htrAddScriptInclude(s,"/sys/js/htdrv_checkbox.js",0); htrAddScriptInclude(s,"/sys/js/ht_utils_hints.js",0); + /** Register event handlers. **/ htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "checkbox", "checkbox_mousedown"); htrAddEventHandlerFunction(s, "document","MOUSEUP", "checkbox", "checkbox_mouseup"); htrAddEventHandlerFunction(s, "document","MOUSEOVER", "checkbox", "checkbox_mouseover"); @@ -105,9 +122,9 @@ int htcbRender(pHtSession s, pWgtrNode tree, int z) { /** Script initialization call. **/ htrAddScriptInit_va(s," checkbox_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), fieldname:\"%STR&JSSTR\", checked:%INT, enabled:%INT, form:\"%STR&JSSTR\"});\n", name, fieldname,checked,enabled,form); - /** HTML body
element for the layers. **/ + /** Write HTML. **/ htrAddBodyItemLayerStart(s, 0, "cb%POSmain", id, NULL); - switch(checked) + switch (checked) { case 1: htrAddBodyItem_va(s," \n",!enabled); @@ -118,8 +135,10 @@ int htcbRender(pHtSession s, pWgtrNode tree, int z) { case -1: /* null */ htrAddBodyItem_va(s," \n",!enabled); break; + default: + fprintf(stderr, "Unexpected value %d for 'checked'.", checked); + break; } - htrAddBodyItemLayerEnd(s, 0); /** Check for more sub-widgets **/ From cc4d154ae9150fe9295ac3df423342decb22e3e5 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:06:09 -0700 Subject: [PATCH 037/107] Update docs. Add docs for new tab functionality. Add docs for colstep_mode attribute on tables. Fix style issues in docs for tables. Clarify docs for label style. --- centrallix-doc/Widgets/widgets.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index a6d917ce4..1a0104512 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -2323,7 +2323,7 @@ MyButton "widget/imagebutton" The color (named or #numeric) of the text in the label when the user hovers the mouse over the label. - (e.g. bold). + 'bold' for bold text, 'italic' for italic text. The text that the label is to display. @@ -3481,7 +3481,7 @@ MyScrollPane "widget/scrollpane"

The tab pages are containers, and as such, controls of various kinds, including other tab controls, can be placed inside the tab pages.

-

Tab pages are added to a tab control by including widgets of type "widget/tabpage" within the "widget/tab" widget in the structure file that defines the application. Any controls to appear inside a particular tab page should be placed inside their respective "widget/tabpage" widgets in the structure file.Only widgets of type "widget/tabpage" should be placed inside a "widget/tab", with the exception of nonvisuals such as connectors.

+

Tab pages are added to a tab control by including widgets of type "widget/tabpage" within the "widget/tab" widget in the structure file that defines the application. Any controls to appear inside a particular tab page should be placed inside their respective "widget/tabpage" widgets in the structure file.Only widgets of type "widget/tabpage" should be placed inside a "widget/tab", except nonvisuals such as connectors.

Tab pages also have a 'visible' property which allows them to be hidden and revealed. This is used if the type is set to dynamic, but can be used manually as well.

@@ -3516,6 +3516,8 @@ MyScrollPane "widget/scrollpane" The location of the tabs: "top" (default), "bottom", "left", "right", or "none". The width of the tabs in pixels. This is optional for tab_locations of "top", "bottom", and "none". + + The height of the tabs in pixels. This is optional for all tab_locations. Defaults to 24px. The color of the text to be used on the tabs to identify them. @@ -3525,7 +3527,15 @@ MyScrollPane "widget/scrollpane" X-coordinate of the upper left corner of the tab control, relative to the container. - Y-coordinate of the upper left corner of the control, relative to its container. + 'client-side' or 'server-side'. This property is intended for developers (although it can give a very small performance boost). A value of "server-side" turns off JS rendering on the client. This does not work for dynamic width tabs (aka. top or bottom tabs with no 'tab_width' property). Defaults to "client-side". + + The amount to translate a selected tab along the side of the tab control. Defaults to 0px. + + The amount to translate a selected tab out and away from the side of the tab control. Defaults to 2px. + + The amount to translate a selected tab in the x direction. If set, overrides the value derived from select_translate_along and/or select_translate_out. + + The amount to translate a selected tab in the y direction. If set, overrides the value derived from select_translate_along and/or select_translate_out. @@ -3609,9 +3619,9 @@ myTabControl "widget/tab" -

A table widget is used to display data in a tabular format. It consists of a header row with column labels, followed by any number of rows containing data.The header may have a different color or image scheme than the rows, and the rows may or may not be configured to alternate between two colors or background images.

+

A table widget is used to display data in a tabular format. It consists of a header row with column labels, followed by any number of rows containing data. The header may have a different color or image scheme than the rows, and the rows may or may not be configured to alternate between two colors or background images.

- Table widgets come in three different flavors: static, dynamicpage, and dynamicrow.Static table widgets are built on the server and write their data directly into the container in which they reside, which is usually a scrollpane widget. Dynamicpage table widgets load their data once they initialize on the client, by activating a query through an ObjectSource nonvisual widget.Dynamicpage table widgets do not support modification, but can be reloaded through an ObjectSource at will.Dynamicrow table widgets, on the other hand, display each row as an individual layer, and thus are modifiable on the client. Dynamicrow table widgets also load their contents through an ObjectSource widget query.As of the time of writing of this document, only static mode and dynamicrow mode were supported. + Table widgets come in three different flavors: static, dynamicpage, and dynamicrow. Static table widgets are built on the server and write their data directly into the container in which they reside, which is usually a scrollpane widget. Dynamicpage table widgets load their data once they initialize on the client, by activating a query through an ObjectSource nonvisual widget. Dynamicpage table widgets do not support modification, but can be reloaded through an ObjectSource at will. Dynamicrow table widgets, on the other hand, display each row as an individual layer, and thus are modifiable on the client. Dynamicrow table widgets also load their contents through an ObjectSource widget query. As of the time of writing of this document, only static mode and dynamicrow mode were supported.

Table widgets allow the selection (keyboard, mouse, and data focus) of individual rows.

@@ -3636,6 +3646,8 @@ myTabControl "widget/tab" The vertical spacing between cells in the table, in pixels. Default is 1. The width of the column separation lines in pixels. Default is 1. + + Either 'full' or 'header'. Default is 'full'. Either "rows" (default) or "properties". In "properties" mode, the table displays one row per attribute, and so only displays the current record in the objectsource. In "rows" mode, the table displays one row per record in the objectsource. From 0b6a8a30a75bfa1deebb9f96287d59e306d11a72 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:09:36 -0700 Subject: [PATCH 038/107] Add new content for testing. Add tab_features.app to show off the new tab features. Add tab_adventure.app to stress test using tabs for a text adventure. Add scrollpane_test.app to test the new scrollpane events and actions. Add several HTML examples for use when testing HTML. --- centrallix-os/samples/scrollpane_test.app | 262 +++++++++ centrallix-os/samples/tab_adventure.app | 413 +++++++++++++ centrallix-os/samples/tab_features.app | 679 ++++++++++++++++++++++ 3 files changed, 1354 insertions(+) create mode 100644 centrallix-os/samples/scrollpane_test.app create mode 100644 centrallix-os/samples/tab_adventure.app create mode 100644 centrallix-os/samples/tab_features.app diff --git a/centrallix-os/samples/scrollpane_test.app b/centrallix-os/samples/scrollpane_test.app new file mode 100644 index 000000000..31db45c6d --- /dev/null +++ b/centrallix-os/samples/scrollpane_test.app @@ -0,0 +1,262 @@ +$Version=2$ +test "widget/page" + { + title = "Test App"; + bgcolor = "#ffffff"; + + x = 0; y = 0; + width = 500; height = 500; + + // Description: + // This page is intended for testing scroll pane functionality, including + // the associated events, actions, etc. It specifically tests the scroll + // pane when used on an HTML widget, allowing us to load long HTML pages + // to test scrolling large amounts of content. + + pane "widget/pane" + { + x = 10; y = 10; + width = 480; height = 480; + + scroll "widget/scrollpane" + { + x = 0; y = 20; + width = 300; height = 300; + bgcolor = "#c0f1ba"; + + // Content. + // html "widget/html" + // { + // x = 0; y = 0; + // width = 300; height = 300; + + // mode = "dynamic"; + // source = "/samples/html_example2_long.html"; + // } + + // Vertical content. + spacer "widget/pane" + { + x = 0; y = 0; + width = 0; height = 2000; + fl_height = 0; + style = flat; + } + a1 "widget/pane" + { + x = 10; y = 0; + width = 250; height = 500; + + bgcolor = "#e6b2c4"; + } + a2 "widget/pane" + { + x = 20; y = 500; + width = 200; height = 500; + + bgcolor = "#e6d2c4"; + } + a3 "widget/pane" + { + x = 30; y = 1000; + width = 150; height = 500; + + bgcolor = "#e6b2c4"; + } + a4 "widget/pane" + { + x = 40; y = 1500; + width = 100; height = 500; + + bgcolor = "#e6d2c4"; + } + + // Horizontal Content. + // Unused because there is no horizontal scrollpane. + // spacer "widget/pane" + // { + // x = 0; y = 0; + // width = 2000; height = 0; + // fl_height = 0; + // style = flat; + // } + // a1 "widget/pane" + // { + // x = 0; y = 10; + // width = 500; height = 250; + + // bgcolor = "#e6b2c4"; + // } + // a2 "widget/pane" + // { + // x = 500; y = 20; + // width = 500; height = 200; + + // bgcolor = "#e6d2c4"; + // } + // a3 "widget/pane" + // { + // x = 1000; y = 30; + // width = 500; height = 150; + + // bgcolor = "#e6b2c4"; + // } + // a4 "widget/pane" + // { + // x = 1500; y = 40; + // width = 500; height = 100; + + // bgcolor = "#e6d2c4"; + // } + + // Ads, testing the Scroll event. + adv "widget/variable" { type = integer; value = runclient(0); } + ad1v "widget/variable" { type = integer; value = runclient(:adv:value); } + ad1c "widget/connector" + { + event = Scroll; + target = test; + action = Alert; + event_condition = runclient(:ad1v:value == 0 and :Percent > 30 and :Change > 0); + Message = runclient("Advertisement!!"); + } + ad1d "widget/connector" + { + event = Scroll; + target = ad1v; + action = SetValue; + event_condition = runclient(:ad1v:value == 0 and :Percent > 30 and :Change > 0); + Value = runclient(1); + } + + ad2v "widget/variable" { type = integer; value = runclient(:adv:value); } + ad2c "widget/connector" + { + event = Scroll; + target = test; + action = Alert; + event_condition = runclient(:ad2v:value == 0 and :Percent > 40 and :Change > 0); + Message = runclient("Advertisement 2!!"); + } + ad2d "widget/connector" + { + event = Scroll; + target = ad2v; + action = SetValue; + event_condition = runclient(:ad2v:value == 0 and :Percent > 40 and :Change > 0); + Value = runclient(1); + } + + ad3v "widget/variable" { type = integer; value = runclient(:adv:value); } + ad3c "widget/connector" + { + event = Scroll; + target = test; + action = Alert; + event_condition = runclient(:ad3v:value == 0 and :Percent > 50 and :Change > 0); + Message = runclient("Advertisement 3!!"); + } + ad3d "widget/connector" + { + event = Scroll; + target = ad3v; + action = SetValue; + event_condition = runclient(:ad3v:value == 0 and :Percent > 50 and :Change > 0); + Value = runclient(1); + } + + // Log events for debugging. + debug_Scroll "widget/connector" + { + event = Scroll; + target = test; + action = Log; + Message = runclient("Scroll " + :Percent + " " + :Change); + } + debug_Wheel "widget/connector" + { + event = Wheel; + target = test; + action = Log; + Message = runclient("Wheel: ctrlKey=" + :ctrlKey + " shiftKey=" + :shiftKey + " altKey=" + :altKey + " metaKey=" + :metaKey + " button=" + :button); + } + debug_Click "widget/connector" + { + event = Click; + target = test; + action = Log; + Message = runclient("Click: ctrlKey=" + :ctrlKey + " shiftKey=" + :shiftKey + " altKey=" + :altKey + " metaKey=" + :metaKey + " button=" + :button); + } + debug_MouseDown "widget/connector" + { + event = MouseDown; + target = test; + action = Log; + Message = runclient("MouseDown: ctrlKey=" + :ctrlKey + " shiftKey=" + :shiftKey + " altKey=" + :altKey + " metaKey=" + :metaKey + " button=" + :button); + } + debug_MouseUp "widget/connector" + { + event = MouseUp; + target = test; + action = Log; + Message = runclient("MouseUp: ctrlKey=" + :ctrlKey + " shiftKey=" + :shiftKey + " altKey=" + :altKey + " metaKey=" + :metaKey + " button=" + :button); + } + debug_MouseOver "widget/connector" + { + event = MouseOver; + target = test; + action = Log; + Message = runclient("MouseOver"); + } + debug_MouseOut "widget/connector" + { + event = MouseOut; + target = test; + action = Log; + Message = runclient("MouseOut"); + } + debug_MouseMove "widget/connector" + { + event = MouseMove; + target = test; + action = Log; + Message = runclient("MouseMove"); + } + } + + button1 "widget/textbutton" + { + x = 5; y = 5; + width = 75; height = 30; + font_size = 18; + + bgcolor="#0c0447"; + text = "To 45%"; + + button1c "widget/connector" + { + event = Click; + target = scroll; + action = ScrollTo; + Percent = 45; + } + } + button2 "widget/textbutton" + { + x = 5; y = 40; + width = 75; height = 30; + font_size = 18; + + bgcolor="#241672"; + text = "To 100px"; + + button2c "widget/connector" + { + event = Click; + target = scroll; + action = ScrollTo; + Offset = 100; + } + } + } + } \ No newline at end of file diff --git a/centrallix-os/samples/tab_adventure.app b/centrallix-os/samples/tab_adventure.app new file mode 100644 index 000000000..a05481949 --- /dev/null +++ b/centrallix-os/samples/tab_adventure.app @@ -0,0 +1,413 @@ +$Version=2$ +FourTabs "widget/page" { + title = "Tab Adventure"; + bgcolor = "#c0c0c0"; + x=0; y=0; width=250; height=300; + + MainTab "widget/tab" { + x = 0; y = 0; width=250; height=300; + tab_location = none; + + bgcolor = "#05091dff"; + inactive_bgcolor = "#01010aff"; + selected_index = 1; + + Scene1 "widget/tabpage" { + height=300; + fl_width = 100; + + Scene1Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="The Beginning"; } + Scene1Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You wake up surounded by the ruins of a village. Houses are burned, walls are colapsed, and roves are caved in. + The house you're 'in' doesn't even have a roof, and barely half a wall is still standing. + "; + } + Scene1Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene1Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Get up and investigate the village"; + Scene1Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=2; } + } + + Scene1Option2 "widget/textbutton" { + x = 70; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Look around the 'house'"; + Scene1Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=3; } + } + } + + Scene2 "widget/tabpage" { + height=300; + + Scene2Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Exploring"; } + Scene2Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You get up and begin to walk around the village. Everything is in pretty bad shape, but a lot of brick fireplaces + were able to survive decently well. + "; + } + Scene2Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene2Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Leave the village"; + Scene2Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=6; } + } + + Scene2Option2 "widget/textbutton" { + x = 70; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Investigate a fireplace"; + Scene2Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=7; } + } + + Scene2Option3 "widget/textbutton" { + x = 130; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Go back to the first house where you woke up."; + Scene2Option3C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=3; } + } + } + + Scene3 "widget/tabpage" { + height=300; + + Scene3Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Look around the house"; } + Scene3Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You look around the house where you woke up. + "; + } + Scene3Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text=""; } + + Scene3Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "..."; + Scene3Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=4; } + } + } + + Scene4 "widget/tabpage" { + height=300; + + Scene4Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Food!"; } + Scene4Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You find some scraps of food and realize that you're famished! + The food looks pretty old, though. Maybe it's not a good idea... + "; + } + Scene4Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene4Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Eat the food"; + Scene4Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=5; } + } + + Scene4Option2 "widget/textbutton" { + x = 70; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Leave it and check out the village"; + Scene4Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=2; } + } + } + + Scene5 "widget/tabpage" { + height=300; + + Scene5Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Bad food..."; } + Scene5Text1 "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text="You eat the food."; + } + Scene5Text2 "widget/label" { + x=10; y=90; width=250; height=80; + font_size=18; fgcolor="#da2d2dff"; + text="OH NO!!"; + } + Scene5Text3 "widget/label" { + x=10; y=120; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text="You should not have done that. You feel very sick."; + } + Scene5Ask "widget/label" { x=10; y=150; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text=". . ."; } + + Scene5Option1 "widget/textbutton" { + x = 10; y = 190; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = ". . ."; + Scene5Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=15; } + } + } + + Scene6 "widget/tabpage" { + height=300; + + Scene6Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="FREEDOM"; } + Scene6Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You leave the village. As you get further away from the depressing place, you begin to run. + You sprint across planes and through vallies, never looking back or missing the acursed ruins you left behind. + After a while, though, you suddenly fall off some kind of edge and plumit until everything goes black... + "; + } + + Scene6Option1 "widget/textbutton" { + x = 10; y = 150; width = 75; height = 30; font_size=18; bgcolor="#1a1066ff"; + text = "WHAT?! The writer made the world THAT small?"; + Scene6Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=15; } + } + } + + Scene7 "widget/tabpage" { + height=300; + + Scene7Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Fireplaces"; } + Scene7Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + There's several firepalces around, so you have a few options. + "; + } + + Scene7Option1 "widget/textbutton" { + x = 10; y = 120; width = 50; height = 24; font_size=18; bgcolor="#0c0447ff"; + text = "Sturdy Fireplace"; + Scene7Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=8; } + } + + Scene7Option2 "widget/textbutton" { + x = 10; y = 150; width = 50; height = 24; font_size=18; bgcolor="#0c0447ff"; + text = "Leaning Fireplace"; + Scene7Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=10; } + } + + Scene7Option3 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 24; font_size=18; bgcolor="#0c0447ff"; + text = "Crumbling Fireplace"; + Scene7Option3C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=9; } + } + } + + Scene8 "widget/tabpage" { + height=300; + + Scene8Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="'Sturdy' Fireplace"; } + Scene8Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You approach the sturdy fireplace and begin to investigate it. Then, you find a loose brick! + However, as you slowly pull it out, the entire fireplace suddenly topples over onto you. + Guess it wasn't so sturdy after all!! + "; + } + + Scene8Option1 "widget/textbutton" { + x = 10; y = 150; width = 75; height = 30; font_size=18; bgcolor="#660009ff"; + text = "The end"; + Scene8Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=15; } + } + } + + Scene9 "widget/tabpage" { + height=300; + + Scene9Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Crumbling Fireplace"; } + Scene9Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You approach the crumbling fireplace and realize it's the one in the house where you woke up! + "; + } + + Scene9Option1 "widget/textbutton" { + x = 10; y = 150; width = 75; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Neat!"; + Scene9Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=3; } + } + } + + Scene10 "widget/tabpage" { + height=300; + + Scene10Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Leaning Fireplace"; } + Scene10Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + As you investigate the leaning fireplace, you find a loose brick. You carefully slide + the brick out, and behind it you pull out a golden scroll that seems to call to you. + "; + } + Scene10Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene10Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Read it"; + Scene10Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=11; } + } + + Scene10Option2 "widget/textbutton" { + x = 70; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Yikes! Probably cursed. Put it down."; + Scene10Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=2; } + } + } + + Scene11 "widget/tabpage" { + height=300; + + Scene11Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="Leaning Fireplace: Last Chance"; } + Scene11Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You know, the scroll probably is cursed. + Reading it might not be a good idea! + "; + } + Scene11Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene11Option7 "widget/textbutton" { + x = 82; y = 230; width = 25; height = 15; font_size=6; fgcolor="#fcc885"; bgcolor="#580251"; + text = "Read it anyway!"; + Scene11Option7C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=12; } + } + + Scene11Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 25; font_size=18; bgcolor="#0c0447ff"; + text = "Ok, I'll go investigate the village"; + Scene11Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=2; } + } + + Scene11Option2 "widget/textbutton" { + x = 70; y = 180; width = 50; height = 25; font_size=18; bgcolor="#0c0447ff"; + text = "Ok, I'll look around the house where I woke up."; + Scene11Option2C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=3; } + } + + Scene11Option3 "widget/textbutton" { + x = 130; y = 180; width = 50; height = 25; font_size=18; bgcolor="#0c0447ff"; + text = "Ok, I'll go to the sturdy fireplace."; + Scene11Option3C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=8; } + } + + Scene11Option4 "widget/textbutton" { + x = 10; y = 220; width = 50; height = 25; font_size=18; bgcolor="#0c0447ff"; + text = "Ok, I'll leave the village."; + Scene11Option4C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=6; } + } + + Scene11Option5 "widget/textbutton" { + x = 70; y = 220; width = 50; height = 25; font_size=18; bgcolor="#1f0757bb"; + text = "Ok, knock myself out."; + Scene11Option5C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=1; } + } + + Scene11Option6 "widget/textbutton" { + x = 130; y = 220; width = 50; height = 25; font_size=18; bgcolor="#3e0655ff"; + text = "Ok, I'll die."; + Scene11Option6C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=15; } + } + } + + Scene12 "widget/tabpage" { + height=300; + + Scene12Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="You really shouldn't read that!"; } + Scene12Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + What? How did you do that! You're not supposed to do that! + "; + } + + Scene12Option1 "widget/textbutton" { + x = 10; y = 120; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "JUST LET ME READ THE SCROLL!!!"; + Scene12Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=13; } + } + } + + + Scene13 "widget/tabpage" { + height=300; + + Scene13Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="VICTORY"; } + Scene13Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + Ok... fine. You found the golden scroll and won the game. + Look, I had to make it at least a little bit difficult for you! + "; + } + + Scene13Option1 "widget/textbutton" { + x = 10; y = 120; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Yay!"; + Scene13Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=14; } + } + } + + + Scene14 "widget/tabpage" { + height=300; + + Scene14Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#fff1df"; text="The End"; } + Scene14Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + Ok... fine. You found the golden scroll and won the game. + Just thought I'd see if I could trick you there hahahah. + "; + } + + Scene14Text2 "widget/label" { + x=10; y=120; width=250; height=80; + font_size=18; fgcolor="#d9e97dff"; + text=" + Thank you for playing! + "; + } + } + + Scene15 "widget/tabpage" { + height=300; + + Scene15Title "widget/label" { x=10; y=10; width=250; height=32; font_size=32; fgcolor="#ff001c"; text="DEATH"; } + Scene15Text "widget/label" { + x=10; y=50; width=250; height=80; + font_size=18; fgcolor="#fff1df"; + text=" + You died... Let's call this a learning experience. + Better luck next time. + "; + } + Scene15Ask "widget/label" { x=10; y=130; width=250; height=32; font_size=18; fgcolor="#ffc67cff"; text="What do you do?"; } + + Scene15Option1 "widget/textbutton" { + x = 10; y = 180; width = 50; height = 30; font_size=18; bgcolor="#0c0447ff"; + text = "Try again?"; + Scene15Option1C "widget/connector" { event=Click; target=MainTab; action=SetTab; TabIndex=1; } + } + + Scene15Option2 "widget/textbutton" { x = 70; y = 180; width = 50; height = 30; font_size=18; bgcolor="#470404ff"; text = "Give up"; } + } + } +} diff --git a/centrallix-os/samples/tab_features.app b/centrallix-os/samples/tab_features.app new file mode 100644 index 000000000..bcde25340 --- /dev/null +++ b/centrallix-os/samples/tab_features.app @@ -0,0 +1,679 @@ +$Version=2$ +TabFeatures "widget/page" + { + x = 0; y = 0; + width = 500; height = 500; + + title = "Tab Features Demonstrated"; + bgcolor = "#b0b0b0"; + + tloc "widget/parameter" { type = "string"; default = "top"; } + w "widget/parameter" { type = "integer"; default = 220; } + h "widget/parameter" { type = "integer"; default = 70; } + + tloc_label "widget/label" + { + x = 2; y = 1; + width = 50; height = 20; + font_size = 16; + + text = "Tab Location:"; + } + + button_top "widget/textbutton" + { + x = 55; y = 1; + width = 27; height = 18; + font_size = 10; + + text = "Top"; + bgcolor="#0c0447ff"; + + button_top_c "widget/connector" + { + event = Click; + target = TabFeatures; + action = LoadPage; + Source = "TabFeatures.app?tloc=top&w=220&h=70"; + } + } + + button_bottom "widget/textbutton" + { + x = 85; y = 1; + width = 27; height = 18; + font_size = 10; + + text = "Bottom"; + bgcolor="#0c0447ff"; + + button_bottom_c "widget/connector" + { + event = Click; + target = TabFeatures; + action = LoadPage; + Source = "TabFeatures.app?tloc=bottom&w=220&h=70"; + } + } + + button_left "widget/textbutton" + { + x = 115; y = 1; + width = 27; height = 18; + font_size = 10; + + text = "Left"; + bgcolor="#0c0447ff"; + + button_left_c "widget/connector" + { + event = Click; + target = TabFeatures; + action = LoadPage; + Source = "TabFeatures.app?tloc=left&w=140&h=90"; + } + } + + button_right "widget/textbutton" + { + x = 145; y = 1; + width = 27; height = 18; + font_size = 10; + + text = "Right"; + bgcolor="#0c0447ff"; + + button_right_c "widget/connector" + { + event = Click; + target = TabFeatures; + action = LoadPage; + Source = "TabFeatures.app?tloc=right&w=140&h=90"; + } + } + + t1 "widget/tab" + { + x = 0; y = 20; + height = 500; width = 500; + + bgcolor = "#c0c0c0"; + inactive_bgcolor = "#a8a8a8"; + selected_index = 2; + tab_location = top; + select_translate_out = 1; + + t11 "widget/tabpage" + { + title = "Spacing Rant"; + + t11l0 "widget/label" { x=20; y=20; width=200; height=40; font_size=24; text="Spacing Rant"; } + + t11t1 "widget/tab" + { + x = 20; y = 60; + width = runserver(:this:w); height = 100; + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; // "/sys/images/4Color.png" + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + + t11t11 "widget/tabpage" { title = "Tab 1"; t11t11l "widget/label" { x=10; y=10; width=100; height=32; style=bold; text="10px X 10px"; } } + t11t12 "widget/tabpage" { title = "Tab 2"; t11t12l "widget/label" { x=20; y=30; width=100; height=32; style=bold; text="20px X 30px"; } } + t11t13 "widget/tabpage" { title = "Tab 3"; t11t13l "widget/label" { x=30; y=50; width=100; height=32; style=bold; text="30px X 50px"; } } + t11t14 "widget/tabpage" { title = "Tab 4"; t11t14l "widget/label" { x=40; y=70; width=100; height=32; style=bold; text="40px X 70px"; } } + + t11t1c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t11t2; + TabIndex = runclient(:t11t1:selected_index); + } + } + + t11t2 "widget/tab" + { + x = 260; y = 60; + width = runserver(:this:w); height = 100; + tab_location = runserver(:this:tloc); + tab_width = 80; + + bgcolor = "#c0c0c0"; + inactive_bgcolor = "#b8b8b8"; + selected = t11t22; + + t11t21 "widget/tabpage" { title = "Tab 1"; t11t21l "widget/label" { x=10; y=10; width=100; height=32; style=bold; text="10px X 10px"; } } + t11t22 "widget/tabpage" { title = "Tab 2"; t11t22l "widget/label" { x=20; y=30; width=100; height=32; style=bold; text="20px X 30px"; } } + t11t23 "widget/tabpage" { title = "Tab 3"; t11t23l "widget/label" { x=30; y=50; width=100; height=32; style=bold; text="30px X 50px"; } } + t11t24 "widget/tabpage" { title = "Tab 4"; t11t24l "widget/label" { x=40; y=70; width=100; height=32; style=bold; text="40px X 70px"; } } + + t11t2c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t11t1; + TabIndex = runclient(:t11t2:selected_index); + } + } + + t11p "widget/pane" + { + x = 20; y = 180; width=runserver(:this:w); height=100; + + t11p1l "widget/label" { x=10; y=10; width=100; height=32; style=bold; text="10px X 10px"; } + t11p2l "widget/label" { x=20; y=30; width=100; height=32; style=bold; text="20px X 30px"; } + t11p3l "widget/label" { x=30; y=50; width=100; height=32; style=bold; text="30px X 50px"; } + t11p4l "widget/label" { x=40; y=70; width=100; height=32; style=bold; text="40px X 70px"; } + } + } + + t12 "widget/tabpage" + { + title = "Tab Spacing"; + + t12l0 "widget/label" { x=20; y=20; width=400; height=40; font_size=24; text="Tab Spacing"; } + + t12l1 "widget/label" { x=30; y=75; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Default (2px)"; } + t12t1 "widget/tab" + { + x = 20; y = 100; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + + t12t11 "widget/tabpage" { title = "First"; t12t11l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t12t12 "widget/tabpage" { title = "Looong Tab"; t12t12l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t12t13 "widget/tabpage" { title = "S"; t12t13l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t12t14 "widget/tabpage" { title = "Last Tab"; t12t14l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t12t1c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t2; + TabIndex = runclient(:t12t1:selected_index); + } + } + + t12l2 "widget/label" { x=270; y=75; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="No Tab Spacing"; } + t12t2 "widget/tab" + { + x = 260; y = 100; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t12t22; + tab_spacing = 0; + + t12t21 "widget/tabpage" { title = "First"; t12t21l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t12t22 "widget/tabpage" { title = "Looong Tab"; t12t22l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t12t23 "widget/tabpage" { title = "S"; t12t23l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t12t24 "widget/tabpage" { title = "Last Tab"; t12t24l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t12t2c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t3; + TabIndex = runclient(:t12t2:selected_index); + } + } + + t12l3 "widget/label" { x=30; y=215; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Tab Spacing 8px"; } + t12t3 "widget/tab" + { + x = 20; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + tab_spacing = 8; + + t12t31 "widget/tabpage" { title = "First"; t12t31l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t12t32 "widget/tabpage" { title = "Looong Tab"; t12t32l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t12t33 "widget/tabpage" { title = "S"; t12t33l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t12t34 "widget/tabpage" { title = "Last Tab"; t12t34l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t12t3c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t4; + TabIndex = runclient(:t12t3:selected_index); + } + } + + t12l4 "widget/label" { x=270; y=215; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Tab Spacing 16px"; } + t12t4 "widget/tab" + { + x = 260; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t12t42; + tab_spacing = 16; + + t12t41 "widget/tabpage" { title = "First"; t12t41l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t12t42 "widget/tabpage" { title = "Looong Tab"; t12t42l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t12t43 "widget/tabpage" { title = "S"; t12t43l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t12t44 "widget/tabpage" { title = "Last Tab"; t12t44l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t12t4c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t5; + TabIndex = runclient(:t12t4:selected_index); + } + } + + t12l5 "widget/label" { x=30; y=355; width=60; height=20; fl_width = 1; font_size=16; style="bold"; text="Tab Spacing -8px"; } + t12l5a "widget/label" { x=90; y=357; width=runserver(:this:w-80); height=15; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not recomended)"; } + t12t5 "widget/tab" + { + x = 20; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + tab_spacing = -8; + + t12t51 "widget/tabpage" { title = "First"; t12t51l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t12t52 "widget/tabpage" { title = "Looong Tab"; t12t52l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t12t53 "widget/tabpage" { title = "S"; t12t53l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t12t54 "widget/tabpage" { title = "Last Tab"; t12t54l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t12t5c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t6; + TabIndex = runclient(:t12t5:selected_index); + } + } + + t12l6 "widget/label" { x=270; y=355; width=70; height=20; fl_width = 1; font_size=16; style="bold"; text="Tab Spacing -16px"; } + t12l6a "widget/label" { x=345; y=357; width=runserver(:this:w-70); height=15; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not recomended)"; } + t12t6 "widget/tab" + { + x = 260; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t12t62; + tab_spacing = -16; + + t12t61 "widget/tabpage" { title = "First"; t12t61l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t12t62 "widget/tabpage" { title = "Looong Tab"; t12t62l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t12t63 "widget/tabpage" { title = "S"; t12t63l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t12t64 "widget/tabpage" { title = "Last Tab"; t12t64l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t12t6c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t12t2; + TabIndex = runclient(:t12t6:selected_index); + } + } + } + + t13 "widget/tabpage" + { + title = "Tab Height"; + + t13l0 "widget/label" { x=20; y=20; width=400; height=40; font_size=24; text="Tab Height"; } + + t13l1 "widget/label" { x=30; y=75; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Default (24px)"; } + t13t1 "widget/tab" + { + x = 20; y = 100; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + + t13t11 "widget/tabpage" { title = "First"; t13t11l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t13t12 "widget/tabpage" { title = "Looong Tab"; t13t12l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t13t13 "widget/tabpage" { title = "S"; t13t13l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t13t14 "widget/tabpage" { title = "Last Tab"; t13t14l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t13t1c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t13t3; + TabIndex = runclient(:t13t1:selected_index); + } + } + + t13l2 "widget/label" { x=270; y=75; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="No Tab Height"; } + t13l2a "widget/label" + { + x = 270; y = 110; + width = runserver(:this:w-20); height = runserver(:this:h - 20); + font_size = 24; style = bold; fgcolor = "red"; + text = "Not allowed"; + } + + t13l2b "widget/label" + { + x = 270; y = 140; + width = runserver(:this:w-20); height = 30; + font_size = 18; style = italic; fgcolor = "red"; + text = "Because I hate fun."; + } + + t13l3 "widget/label" { x=30; y=215; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Tab Height 32px"; } + t13t3 "widget/tab" + { + x = 20; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + tab_height = 32; + + t13t31 "widget/tabpage" { title = "First"; t13t31l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t13t32 "widget/tabpage" { title = "Looong Tab"; t13t32l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t13t33 "widget/tabpage" { title = "S"; t13t33l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t13t34 "widget/tabpage" { title = "Last Tab"; t13t34l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t13t3c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t13t4; + TabIndex = runclient(:t13t3:selected_index); + } + } + + t13l4 "widget/label" { x=270; y=215; width=runserver(:this:w-20); height=40; font_size=16; style="bold"; text="Tab Height 48px"; } + t13t4 "widget/tab" + { + x = 260; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t13t42; + tab_height = 48; + + t13t41 "widget/tabpage" { title = "First"; t13t41l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t13t42 "widget/tabpage" { title = "Looong Tab"; t13t42l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t13t43 "widget/tabpage" { title = "S"; t13t43l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t13t44 "widget/tabpage" { title = "Last Tab"; t13t44l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t13t4c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t13t5; + TabIndex = runclient(:t13t4:selected_index); + } + } + + t13l5 "widget/label" { x=30; y=355; width=60; height=20; font_size=16; style="bold"; text="Tab Height 16px"; } + t13l5a "widget/label" { x=90; y=357; width=runserver(:this:w-80); height=15; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not supported)"; } + t13t5 "widget/tab" + { + x = 20; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + tab_height = 16; + + t13t51 "widget/tabpage" { title = "First"; t13t51l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t13t52 "widget/tabpage" { title = "Looong Tab"; t13t52l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t13t53 "widget/tabpage" { title = "S"; t13t53l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t13t54 "widget/tabpage" { title = "Last Tab"; t13t54l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t13t5c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t13t6; + TabIndex = runclient(:t13t5:selected_index); + } + } + + t13l6 "widget/label" { x=270; y=355; width=60; height=20; font_size=16; style="bold"; text="Tab Spacing 8px"; } + t13l6a "widget/label" { x=335; y=357; width=runserver(:this:w-80); height=15; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not supported)"; } + t13t6 "widget/tab" + { + x = 260; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t13t62; + tab_height = 8; + + t13t61 "widget/tabpage" { title = "First"; t13t61l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t13t62 "widget/tabpage" { title = "Looong Tab"; t13t62l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t13t63 "widget/tabpage" { title = "S"; t13t63l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t13t64 "widget/tabpage" { title = "Last Tab"; t13t64l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t13t6c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t13t1; + TabIndex = runclient(:t13t6:selected_index); + } + } + } + + + t14 "widget/tabpage" + { + title = "Selection Offsets"; + + t14l0 "widget/label" { x=20; y=20; width=400; height=40; font_size=24; text="Selection Offsets"; } + + t14l1 "widget/label" { x=30; y=75; width=220; height=40; font_size=16; style="bold"; text="Default"; } + t14t1 "widget/tab" + { + x = 20; y = 100; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + + t14t11 "widget/tabpage" { title = "First"; t14t11l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t14t12 "widget/tabpage" { title = "Looong Tab"; t14t12l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t14t13 "widget/tabpage" { title = "S"; t14t13l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t14t14 "widget/tabpage" { title = "Last Tab"; t14t14l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t14t1c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t2; + TabIndex = runclient(:t14t1:selected_index); + } + } + + t14l2 "widget/label" { x=270; y=75; width=220; height=40; font_size=16; style="bold"; text="No Translation"; } + t14t2 "widget/tab" + { + x = 260; y = 100; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t14t22; + select_translate_along = 0; + select_translate_out = 0; + + t14t21 "widget/tabpage" { title = "First"; t14t21l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t14t22 "widget/tabpage" { title = "Looong Tab"; t14t22l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t14t23 "widget/tabpage" { title = "S"; t14t23l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t14t24 "widget/tabpage" { title = "Last Tab"; t14t24l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t14t2c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t3; + TabIndex = runclient(:t14t2:selected_index); + } + } + + t14l3 "widget/label" { x=30; y=215; width=220; height=40; font_size=16; style="bold"; text="Along 8"; } + t14t3 "widget/tab" + { + x = 20; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + select_translate_along = 8; + select_translate_out = 0; + + t14t31 "widget/tabpage" { title = "First"; t14t31l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t14t32 "widget/tabpage" { title = "Looong Tab"; t14t32l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t14t33 "widget/tabpage" { title = "S"; t14t33l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t14t34 "widget/tabpage" { title = "Last Tab"; t14t34l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t14t3c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t4; + TabIndex = runclient(:t14t3:selected_index); + } + } + + t14l4 "widget/label" { x=270; y=215; width=220; height=40; font_size=16; style="bold"; text="Out 8"; } + t14t4 "widget/tab" + { + x = 260; y = 240; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t14t42; + select_translate_along = 0; + select_translate_out = 8; + + t14t41 "widget/tabpage" { title = "First"; t14t41l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t14t42 "widget/tabpage" { title = "Looong Tab"; t14t42l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t14t43 "widget/tabpage" { title = "S"; t14t43l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t14t44 "widget/tabpage" { title = "Last Tab"; t14t44l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t14t4c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t5; + TabIndex = runclient(:t14t4:selected_index); + } + } + + t14l5 "widget/label" { x=30; y=355; width=30; height=40; font_size=16; style="bold"; text="Along -8"; } + t14l5a "widget/label" { x=67; y=357; width=100; height=40; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not recomended)"; } + t14t5 "widget/tab" + { + x = 20; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected_index = 2; + select_translate_along = -8; + select_translate_out = 0; + + t14t51 "widget/tabpage" { title = "First"; t14t51l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab One"; } } + t14t52 "widget/tabpage" { title = "Looong Tab"; t14t52l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Two"; } } + t14t53 "widget/tabpage" { title = "S"; t14t53l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Three"; } } + t14t54 "widget/tabpage" { title = "Last Tab"; t14t54l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Tab Four"; } } + + t14t5c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t6; + TabIndex = runclient(:t14t5:selected_index); + } + } + + t14l6 "widget/label" { x=270; y=355; width=30; height=40; font_size=16; style="bold"; text="Out -8"; } + t14l6a "widget/label" { x=307; y=357; width=100; height=40; font_size=12; style="bold"; fgcolor="red"; text="(Negative values not recomended)"; } + t14t6 "widget/tab" + { + x = 260; y = 380; + width = runserver(:this:w); height = runserver(:this:h); + tab_location = runserver(:this:tloc); + tab_width = 80; + + background = "/sys/images/slate2.gif"; + inactive_background = "/sys/images/slate2_dark.gif"; + selected = t14t62; + select_translate_along = 0; + select_translate_out = -8; + + t14t61 "widget/tabpage" { title = "First"; t14t61l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label One"; } } + t14t62 "widget/tabpage" { title = "Looong Tab"; t14t62l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Two"; } } + t14t63 "widget/tabpage" { title = "S"; t14t63l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Three"; } } + t14t64 "widget/tabpage" { title = "Last Tab"; t14t64l "widget/label" { x=10; y=10; width=100; height=32; font_size=18; fgcolor="#00065f"; text="Label Four"; } } + + t14t6c "widget/connector" + { + event = TabChanged; + action = SetTab; + target = t14t1; + TabIndex = runclient(:t14t6:selected_index); + } + } + } + } + } \ No newline at end of file From 44499c35b83e866e50cf29614a2c478bf1284352 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:11:27 -0700 Subject: [PATCH 039/107] Fix a bug in the textbutton widget that broke responsiveness. --- centrallix-os/sys/js/htdrv_textbutton.js | 1 - 1 file changed, 1 deletion(-) diff --git a/centrallix-os/sys/js/htdrv_textbutton.js b/centrallix-os/sys/js/htdrv_textbutton.js index d053453b1..bca2a6595 100644 --- a/centrallix-os/sys/js/htdrv_textbutton.js +++ b/centrallix-os/sys/js/htdrv_textbutton.js @@ -205,7 +205,6 @@ function tb_setmode(layer,mode) switch(mode) { case 0: /* no point no click */ - moveTo(layer,layer.orig_x,layer.orig_y); $(layer).find(".cell").css({'border-style':'solid', 'border-color':'transparent'}); /*if(cx__capabilities.Dom2CSS) { From b291e5a51dba3ca16918b2b02e1d2e0b8dc80018 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:18:56 -0700 Subject: [PATCH 040/107] Make datetime widget responsive. Update C to generate responsive CSS rules. Replace JS .w and .h values with getters that return the current height and width. Add a JS resize handler that updates open datetime dropdowns when the page is resized. --- centrallix-os/sys/js/htdrv_datetime.js | 25 ++++++++++++-- centrallix/htmlgen/htdrv_datetime.c | 45 ++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_datetime.js b/centrallix-os/sys/js/htdrv_datetime.js index c333f6446..88535c015 100644 --- a/centrallix-os/sys/js/htdrv_datetime.js +++ b/centrallix-os/sys/js/htdrv_datetime.js @@ -163,7 +163,6 @@ function dt_init(param){ htr_init_layer(c2,l,'dt'); //dt_tag_images(l.document, 'dt', l); htutil_tag_images(l,'dt',l,l); - l.w = w; l.h = h; l.bg = htr_extract_bgcolor(bg); l.ubg = bg; l.fg = param.foreground; @@ -200,7 +199,29 @@ function dt_init(param){ else l.form = wgtrFindContainer(l,"widget/form"); if (l.form) l.form.Register(l); - pg_addarea(l, -1, -1, getClipWidth(l)+3, getClipHeight(l)+3, 'dt', 'dt', 3); + + // Setup getters widths and heights. + l.__defineGetter__('w', () => parseInt(getComputedStyle(l).width)); + l.__defineGetter__('h', () => parseInt(getComputedStyle(l).height)); + + /** Setup the hover area. **/ + l.area = pg_addarea(l, -1, -1, l.w + 3, l.h + 3, 'dt', 'dt', 3); + l.area.__defineGetter__('width', () => l.w + 3); + l.area.__defineGetter__('height', () => l.h + 3); + + // Resize date selection dropdown automatically. + const resize_handler = (layer) => + { + if (layer.PaneLayer && htr_getvisibility(layer.PaneLayer) === 'inherit') + { + dt_collapse(layer); + dt_expand(layer); + } + }; + const resize_observer = new ResizeObserver(entries => { + for (const entry of entries) resize_handler(entry.target); + }); + resize_observer.observe(l); // Events ifc_init_widget(l); diff --git a/centrallix/htmlgen/htdrv_datetime.c b/centrallix/htmlgen/htdrv_datetime.c index 703ccbf5f..2161cda7b 100644 --- a/centrallix/htmlgen/htdrv_datetime.c +++ b/centrallix/htmlgen/htdrv_datetime.c @@ -203,10 +203,42 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) else strcpy(fgcolor,"black"); - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#dt%POSbtn { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; cursor:default; border:1px outset #e0e0e0; %STR }\n",id,x,y,w,h,z, bgcolor); - htrAddStylesheetItem_va(s,"\t#dt%POScon1 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:1px; TOP:1px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,w-20,h-2,z+1); - htrAddStylesheetItem_va(s,"\t#dt%POScon2 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:1px; TOP:1px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,w-20,h-2,z+1); + /** Write style headers. **/ + htrAddStylesheetItem_va(s, + "\t#dt%POSbtn { " + "overflow:hidden; " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "cursor:default; " + "border:1px outset #e0e0e0; " + "%STR " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z, + bgcolor + ); + htrAddStylesheetItem_va(s, + "\t.dt%POScon { " + "overflow:hidden; " + "position:absolute; " + "left:1px; " + "top:1px; " + "width:calc(100%% - 20px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "}\n", + id, + z + 1 + ); /** Write named global **/ htrAddScriptGlobal(s, "dt_current", "null", 0); @@ -246,8 +278,8 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) htrAddBodyItem_va(s," \n",w-2); htrAddBodyItem(s, " \n"); htrAddBodyItem(s, "\n");*/ - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
\n",id); + htrAddBodyItem_va(s,"
\n", id, id); + htrAddBodyItem_va(s,"\n", id, id); htrAddBodyItem(s, "
\n"); /** Add the event handling scripts **/ @@ -301,4 +333,3 @@ htdtInitialize() return 0; } - From 32c24e3994df6bc2897f5f2eb5f724e0f169bbef Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:19:38 -0700 Subject: [PATCH 041/107] Make dropdown widget responsive. Clean up responsive CSS style generation in the C code. Replace numbers for dropdown modes with #define macros. Replace JS .w and .h values with getters that return the current height and width. Add a JS resize handler that updates open dropdowns when the page is resized. Add dd_remove_pane() to delete DOM nodes for the dropdown pane. Fix a DOM node leak in dd_colapse() by calling dd_remove_pane(). Add another resize observer to ensure that HidLayer responds properly. --- centrallix-os/sys/js/htdrv_dropdown.js | 78 +++++++++++++++- centrallix/htmlgen/htdrv_dropdown.c | 120 +++++++++++++++---------- 2 files changed, 147 insertions(+), 51 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_dropdown.js b/centrallix-os/sys/js/htdrv_dropdown.js index b3a23a8db..b3e0734c1 100644 --- a/centrallix-os/sys/js/htdrv_dropdown.js +++ b/centrallix-os/sys/js/htdrv_dropdown.js @@ -390,6 +390,9 @@ function dd_collapse(l) //pg_resize_area(l.area,getClipWidth(l)+1,getClipHeight(l)+1, -1, -1); htr_setvisibility(l.PaneLayer, 'hidden'); dd_current = null; + + /** Remove the pane to avoid leaking DOM nodes. **/ + dd_remove_pane(l); } } @@ -511,6 +514,17 @@ function dd_select_item(l,i,from) moveTo(l.HidLayer, 2, ((l.h-2) - pg_parah)/2); resizeTo(l.HidLayer, l.w, l.h); + // Set up responsive handling. + if (!l.HidLayer.resize_observer) + { + const { HidLayer } = l; + const resize_observer = HidLayer.resize_observer = new ResizeObserver(_ => { + moveTo(HidLayer, 2, ((l.h - 2) - pg_parah) / 2); + resizeTo(HidLayer, l.w, l.h); + }); + resize_observer.observe(l); + } + htr_setvisibility(l.HidLayer, 'inherit'); setClipWidth(l.HidLayer, l.w-21); htr_setvisibility(l.VisLayer, 'hidden'); @@ -775,6 +789,41 @@ function dd_create_pane(l) return p; } +/** Quick and dirty function to remove panes without leaking DOM nodes. **/ +function dd_remove_pane(l) + { + if (!l || !l.PaneLayer) return; + + const p = l.PaneLayer; + + // Function for removing elements. + const remove_node = (n) => { if (n && n.parentNode) n.parentNode.removeChild(n); } + + // Remove item DOM nodes if present. + if (l.Items && l.Items.length) + { + for (let i = 0; i < l.Items.length; i++) + { + remove_node(l.Items[i]); + l.Items[i] = null; + } + l.Items.length = 0; + l.Items = null; + } + + // Remove scrollbar/thumb layers if present. + remove_node(p.ScrLayer); + remove_node(p.BarLayer); + remove_node(p.TmbLayer); + + // Remove the pane root. + remove_node(p); + + // Clear references. + p.ScrLayer = p.BarLayer = p.TmbLayer = p.Items = p.mainlayer = null; + l.PaneLayer = null; + } + /// REPLACE ITEMS IN DROPDOWN function dd_add_items(l,ary) @@ -1194,7 +1243,6 @@ function dd_init(param) l.NumDisplay = param.numDisplay; l.Mode = param.mode; l.SQL = param.sql; - l.popup_width = param.popup_width?param.popup_width:param.width; l.VisLayer = param.c1; l.HidLayer = param.c2; htr_init_layer(l.VisLayer, l, 'ddtxt'); @@ -1244,7 +1292,6 @@ function dd_init(param) l.getfocushandler = dd_getfocus; l.bg = param.background; l.hl = param.highlight; - l.w = param.width; l.h = param.height; l.fieldname = param.fieldname; l.enabled = 'full'; if (l.Mode != 3) @@ -1268,11 +1315,34 @@ function dd_init(param) else if (imgs[i].src.substr(-13,5) == 'white') imgs[i].upimg = true; } - l.area = pg_addarea(l, -1, -1, getClipWidth(l)+3, - getClipHeight(l)+3, 'dd', 'dd', 3); if (l.form) l.form.Register(l); l.init_items = false; + // Setup getters widths and heights. + const width_ratio = param.popup_width / param.width; + l.__defineGetter__('w', () => parseInt(getComputedStyle(l).width)); + l.__defineGetter__('h', () => parseInt(getComputedStyle(l).height)); + l.__defineGetter__('popup_width', () => l.w * width_ratio); + + /** Setup the hover area. **/ + l.area = pg_addarea(l, -1, -1, l.w + 3, l.h + 3, 'dd', 'dd', 3); + l.area.__defineGetter__('width', () => l.w + 3); + l.area.__defineGetter__('height', () => l.h + 3); + + // Resize dropdown automatically. + const resize_handler = (layer) => + { + if (layer.PaneLayer && htr_getvisibility(layer.PaneLayer) === 'inherit') + { + dd_collapse(layer); + dd_expand(layer); + } + }; + const resize_observer = new ResizeObserver(entries => { + for (const entry of entries) resize_handler(entry.target); + }); + resize_observer.observe(l); + // Events var ie = l.ifcProbeAdd(ifEvent); ie.Add("MouseDown"); diff --git a/centrallix/htmlgen/htdrv_dropdown.c b/centrallix/htmlgen/htdrv_dropdown.c index 7f16bcbe5..f2af95d00 100644 --- a/centrallix/htmlgen/htdrv_dropdown.c +++ b/centrallix/htmlgen/htdrv_dropdown.c @@ -46,6 +46,12 @@ static struct { int idcnt; } HTDD; +/** Dropdown modes. **/ +#define HTDD_STATIC 0 +#define HTDD_DYNAMIC_SERVER 1 +#define HTDD_DYNAMIC 2 +#define HTDD_DYNAMIC_CLIENT HTDD_DYNAMIC +#define HTDD_OBJECTSOURCE 3 /* htddRender - generate the HTML code for the page. @@ -144,7 +150,7 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; strtcpy(name,ptr,sizeof(name)); - /** Ok, write the style header items. **/ + /** Write basic element CSS. **/ htrAddStylesheetItem_va(s, "\t#dd%POSbtn { " "OVERFLOW:hidden; " @@ -160,10 +166,10 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { "border:1px outset #e0e0e0; " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w, ht_get_total_w(tree), ht_get_fl_w(tree)), - ht_flex(h, ht_get_total_h(tree), ht_get_fl_h(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), z, bgstr ); @@ -171,35 +177,18 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { htrAddStylesheetItem_va(s,"\t#dd%POSbtn { color: %STR&CSSVAL; }\n",id,textcolor); } htrAddStylesheetItem_va(s, - "\t#dd%POScon1 { " - "OVERFLOW:hidden; " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:1px; " - "TOP:1px; " - "WIDTH:1024px; " - "HEIGHT:"ht_flex_format"; " - "Z-INDEX:%POS; " + "\t.dd%POScon { " + "overflow:hidden; " + "position:absolute; " + "left:1px; " + "top:1px; " + "width:1024px; " + "height:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, - ht_flex(h-2, h, 0.0), - z+1 - ); - /** I have no idea why we need dd#con2. It's hidden by default. **/ - htrAddStylesheetItem_va(s, - "\t#dd%POScon2 { " - "OVERFLOW:hidden; " - "POSITION:absolute; " - "VISIBILITY:hidden; " - "LEFT:1px; " - "TOP:1px; " - "WIDTH:1024px; " - "HEIGHT:"ht_flex_format"; " - "Z-INDEX:%POS; " - "}\n", - id, - ht_flex(h-2, h, 0.0), - z+1 + ht_flex(h - 2, h, 0.0), + z + 1 ); htrAddScriptGlobal(s, "dd_current", "null", 0); @@ -228,20 +217,20 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { /** Get the mode (default to 1, dynamicpage) **/ - mode = 0; + mode = HTDD_STATIC; if (wgtrGetPropertyValue(tree,"mode",DATA_T_STRING,POD(&ptr)) == 0) { - if (!strcmp(ptr,"static")) mode = 0; - else if (!strcmp(ptr,"dynamic_server")) mode = 1; - else if (!strcmp(ptr,"dynamic")) mode = 2; - else if (!strcmp(ptr,"dynamic_client")) mode = 2; - else if (!strcmp(ptr,"objectsource")) mode = 3; + if (strcmp(ptr, "static") == 0) mode = HTDD_STATIC; + else if (strcmp(ptr, "dynamic_server") == 0) mode = HTDD_DYNAMIC_SERVER; + else if (strcmp(ptr, "dynamic") == 0) mode = HTDD_DYNAMIC; + else if (strcmp(ptr, "dynamic_client") == 0) mode = HTDD_DYNAMIC_CLIENT; + else if (strcmp(ptr, "objectsource") == 0) mode = HTDD_OBJECTSOURCE; else { - mssError(1,"HTDD","Dropdown widget has not specified a valid mode."); + mssError(1, "HTDD", "Invalid dropdown widget 'mode' value: \"%s\"", ptr); return -1; } } - sql = 0; + sql = NULL; if (wgtrGetPropertyValue(tree,"sql",DATA_T_STRING,POD(&sql)) != 0 && mode != 0 && mode != 3) { mssError(1, "HTDD", "SQL parameter was not specified for dropdown widget"); return -1; @@ -249,7 +238,43 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { htrCheckAddExpression(s,tree,name,"sql"); /** Script initialization call. **/ - htrAddScriptInit_va(s," dd_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), c1:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"dd%POScon1\"), c2:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"dd%POScon2\"), background:'%STR&JSSTR', highlight:'%STR&JSSTR', fieldname:'%STR&JSSTR', numDisplay:%INT, mode:%INT, sql:'%STR&JSSTR', width:%INT, height:%INT, form:'%STR&JSSTR', osrc:'%STR&JSSTR', qms:%INT, ivs:%INT, popup_width:%INT});\n", name, name, id, name, id, bgstr, hilight, fieldname, num_disp, mode, sql?sql:"", w, h, form, osrc, query_multiselect, invalid_select_default, pop_w); + htrAddScriptInit_va(s, + "dd_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "c1:htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'dd%POScon1'), " + "c2:htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'dd%POScon2'), " + "background:'%STR&JSSTR', " + "highlight:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "numDisplay:%INT, " + "mode:%INT, " + "sql:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "osrc:'%STR&JSSTR', " + "qms:%INT, " + "ivs:%INT, " + "width:%INT, " + "height:%INT, " + "popup_width:%INT, " + "});\n", + name, + name, id, + name, id, + bgstr, + hilight, + fieldname, + num_disp, + mode, + (sql != NULL) ? sql : "", + form, + osrc, + query_multiselect, + invalid_select_default, + w, + ht_get_fl_x(tree), + h, + pop_w + ); /** HTML body
element for the layers. **/ htrAddBodyItem_va(s,"
\n" @@ -265,12 +290,12 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { htrAddBodyItem_va(s," \n",w-2); htrAddBodyItem(s, " \n"); htrAddBodyItem(s, "\n");*/ - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
\n",id); + htrAddBodyItem_va(s,"
\n", id, id); + htrAddBodyItem_va(s,"\n", id, id); htrAddBodyItem(s, "
\n"); /* Read and initialize the dropdown items */ - if (mode == 1) { + if (mode == HTDD_DYNAMIC_SERVER) { /** The result set from this SQL query can take two forms: positional or named. ** For Positional, the params are: label, value, selected, group, hidden. ** For Named, the above names can appear in any order. @@ -362,7 +387,7 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { objQueryClose(qy); } } - else if(mode==3) { + else if (mode == HTDD_OBJECTSOURCE) { /* get objects from form */ } @@ -373,7 +398,9 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { subtree = xaGetItem(&(tree->Children), i); if (!strcmp(subtree->Type, "widget/dropdownitem")) subtree->RenderFlags |= HT_WGTF_NOOBJECT; - if (!strcmp(subtree->Type,"widget/dropdownitem") && mode == 0) + + /** Write JS to render dropdown items in static mode. **/ + if (!strcmp(subtree->Type,"widget/dropdownitem") && mode == HTDD_STATIC) { if (wgtrGetPropertyValue(subtree,"label",DATA_T_STRING,POD(&ptr)) != 0) { @@ -459,4 +486,3 @@ int htddInitialize() { return 0; } - From a7d995881db4939455395bfc6826f8a81de231ef Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:22:10 -0700 Subject: [PATCH 042/107] Make editbox widget responsive. Clean up responsive CSS style generation in the C code. Simplify JS to prevent it breaking the responsive CSS. --- centrallix-os/sys/js/htdrv_editbox.js | 4 +- centrallix/htmlgen/htdrv_editbox.c | 68 +++++++++++++++++---------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_editbox.js b/centrallix-os/sys/js/htdrv_editbox.js index 160ce0bdf..df6579be7 100644 --- a/centrallix-os/sys/js/htdrv_editbox.js +++ b/centrallix-os/sys/js/htdrv_editbox.js @@ -162,8 +162,10 @@ function eb_setdesc(txt) ({ "z-index":"-1", "color":this.desc_fgcolor?this.desc_fgcolor:"#808080", - "top":($(this).height() - $(this.DescLayer).height())/2 + "px", + "top":"0px", "left":(this.input_width() + ((this.content || this.has_focus)?4:0) + 5) + "px", + "height":"100%", + "align-content":"center", "visibility":"inherit", "white-space":"nowrap", }); diff --git a/centrallix/htmlgen/htdrv_editbox.c b/centrallix/htmlgen/htdrv_editbox.c index ef40c8d8a..f4b85c397 100644 --- a/centrallix/htmlgen/htdrv_editbox.c +++ b/centrallix/htmlgen/htdrv_editbox.c @@ -149,34 +149,36 @@ htebRender(pHtSession s, pWgtrNode tree, int z) box_offset = 0; /** Ok, write the style header items. **/ + const int base_w = w - (2 * box_offset); htrAddStylesheetItem_va(s, "\t#eb%POSbase { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, - ht_flex(x, ht_get_total_w(tree), ht_get_fl_x(tree)), - ht_flex(y, ht_get_total_h(tree), ht_get_fl_y(tree)), - ht_flex(w - 2 * box_offset, ht_get_total_w(tree), ht_get_fl_w(tree)), + ht_flex(x, ht_get_parent_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_parent_h(tree), ht_get_fl_y(tree)), + ht_flex(base_w, ht_get_parent_w(tree), ht_get_fl_w(tree)), z ); htrAddStylesheetItem_va(s, "\t#eb%POScon1 { " - "VISIBILITY:inherit; " - "LEFT:5px; " - "TOP:0px; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "position:absolute; " + "visibility:inherit; " + "left:5px; " + "top:0px; " + "width:calc(100%% - 10px); " + "height:100%%; " "border:none; " + "z-index:%POS; " "}\n", id, - ht_flex(w - 10, ht_get_total_w(tree), ht_get_fl_w(tree)), - z+1 + z + 1 ); /** Write named global **/ @@ -200,10 +202,28 @@ htebRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s, "document","PASTE", "eb", "eb_paste"); /** Script initialization call. **/ - htrAddScriptInit_va(s, " eb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), c1:document.getElementById(\"eb%POScon1\"), form:\"%STR&JSSTR\", fieldname:\"%STR&JSSTR\", isReadOnly:%INT, mainBackground:\"%STR&JSSTR\", tooltip:\"%STR&JSSTR\", desc_fgcolor:\"%STR&JSSTR\", empty_desc:\"%STR&JSSTR\"});\n", - name, id, - form, fieldname, is_readonly, main_bg, - tooltip, descfg, descr); + htrAddScriptInit_va(s, + "eb_init({ " + "layer:wgtrGetNodeRef(ns,'%STR&SYM'), " + "c1:document.getElementById('eb%POScon1'), " + "form:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "isReadOnly:%INT, " + "mainBackground:'%STR&JSSTR', " + "tooltip:'%STR&JSSTR', " + "desc_fgcolor:'%STR&JSSTR', " + "empty_desc:'%STR&JSSTR', " + "});\n", + name, + id, + form, + fieldname, + is_readonly, + main_bg, + tooltip, + descfg, + descr + ); /** HTML body
element for the base layer. **/ htrAddBodyItem_va(s, "
\n",id); @@ -214,12 +234,12 @@ htebRender(pHtSession s, pWgtrNode tree, int z) else htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: gray white white gray; %STR }\n",id, main_bg); if (h >= 0) + { htrAddStylesheetItem_va(s, - "\t#eb%POSbase { height:"ht_flex_format"; }\n" - "\t#eb%POScon1 { height:"ht_flex_format"; }\n", - id, ht_flex(h - 2 * box_offset, ht_get_total_h(tree), ht_get_fl_h(tree)), - id, ht_flex(h - 2 * box_offset - 2, ht_get_total_h(tree), ht_get_fl_h(tree)) + "\t#eb%POSbase { height:"ht_flex_format"; }\n", + id, ht_flex(h - (2 * box_offset), ht_get_parent_h(tree), ht_get_fl_h(tree)) ); + } //htrAddBodyItem_va(s, "
 
\n", w-2, h-2); //htrAddBodyItem_va(s, "
\n",id); From bf0987e58ad347f8a26bc5dc422fb5a6b472c486 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:22:35 -0700 Subject: [PATCH 043/107] Make formstatus widget responsive. --- centrallix/htmlgen/htdrv_formstatus.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/centrallix/htmlgen/htdrv_formstatus.c b/centrallix/htmlgen/htdrv_formstatus.c index 33258e3c1..b4b0d504c 100644 --- a/centrallix/htmlgen/htdrv_formstatus.c +++ b/centrallix/htmlgen/htdrv_formstatus.c @@ -89,7 +89,22 @@ int htfsRender(pHtSession s, pWgtrNode tree, int z) { htrAddWgtrObjLinkage_va(s, tree, "fs%POSmain", id); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#fs%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:13px; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); + htrAddStylesheetItem_va(s, + "\t#fs%POSmain { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:13px; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z + ); htrAddScriptInclude(s, "/sys/js/htdrv_formstatus.js", 0); From 615994b62f23c8430885f027616f03d92b78220a Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:24:39 -0700 Subject: [PATCH 044/107] Make imagebutton widget responsive. Update C generation to create responsive CSS for image button widgets. Disable unnecessary clipping in the imagebutton widget. --- centrallix-os/sys/js/htdrv_imagebutton.js | 1 + centrallix/htmlgen/htdrv_imagebutton.c | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/centrallix-os/sys/js/htdrv_imagebutton.js b/centrallix-os/sys/js/htdrv_imagebutton.js index c3aae6b2a..1122a2303 100644 --- a/centrallix-os/sys/js/htdrv_imagebutton.js +++ b/centrallix-os/sys/js/htdrv_imagebutton.js @@ -211,6 +211,7 @@ function ib_init(param) l.img.kind = 'ib'; l.cursrc = param.n; setClipWidth(l, w); + disableClippingCSS(l); l.trigger = ib_trigger; diff --git a/centrallix/htmlgen/htdrv_imagebutton.c b/centrallix/htmlgen/htdrv_imagebutton.c index 64cfe92eb..3f0bb5390 100644 --- a/centrallix/htmlgen/htdrv_imagebutton.c +++ b/centrallix/htmlgen/htdrv_imagebutton.c @@ -137,7 +137,23 @@ htibtnRender(pHtSession s, pWgtrNode tree, int z) button_repeat = htrGetBoolean(tree, "repeat", 0); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#ib%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; cursor:pointer; }\n",id,x,y,w,z); + htrAddStylesheetItem_va(s, + "\t#ib%POSpane { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden;" + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z + ); htrAddScriptGlobal(s, "ib_cur_img", "null", 0); htrAddWgtrObjLinkage_va(s, tree, "ib%POSpane", id); From afe93885fa088d1bc9d80fab8f3370037baecd8a Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:27:39 -0700 Subject: [PATCH 045/107] Make menu widget responsive. Update C generation to create responsive CSS. Update C style for script init. Update JS to only modify CSS when it is not provided in C. --- centrallix-os/sys/js/htdrv_menu.js | 10 ++-- centrallix/htmlgen/htdrv_menu.c | 75 +++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_menu.js b/centrallix-os/sys/js/htdrv_menu.js index f517e3948..1c25e62a3 100644 --- a/centrallix-os/sys/js/htdrv_menu.js +++ b/centrallix-os/sys/js/htdrv_menu.js @@ -395,19 +395,21 @@ function mn_init(param) menu.objname = param.name; menu.cur_highlight = null; + // Set up sizing. if (cx__capabilities.CSS2) { if (menu.scrollHeight == 0) { - pg_set_style(menu,'height',menu.childNodes[0].scrollHeight); - pg_set_style(menu,'width',menu.childNodes[0].scrollWidth); + if (param.h === -1) pg_set_style(menu, 'height', menu.childNodes[0].scrollHeight); + if (param.w === -1) pg_set_style(menu, 'width', menu.childNodes[0].scrollWidth); } else { - pg_set_style(menu,'height',menu.scrollHeight); - pg_set_style(menu,'width',menu.scrollWidth); + if (param.h === -1) pg_set_style(menu, 'height', menu.scrollHeight); + if (param.w === -1) pg_set_style(menu, 'width', menu.scrollWidth); } } + disableClippingCSS(menu); menu.act_w = getClipWidth(menu.clayer); menu.act_h = getClipHeight(menu.clayer); if ($(menu).css('visibility') == 'hidden' && (!menu.__WgtrParent.style || $(menu.__WgtrParent).css('visibility') == 'inherit')) diff --git a/centrallix/htmlgen/htdrv_menu.c b/centrallix/htmlgen/htdrv_menu.c index a400c885e..331b8bb92 100644 --- a/centrallix/htmlgen/htdrv_menu.c +++ b/centrallix/htmlgen/htdrv_menu.c @@ -284,21 +284,50 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) is_popup = htrGetBoolean(menu, "popup", 0); if (is_popup < 0) is_popup = 0; - /** Write the main style header item. **/ - htrAddStylesheetItem_va(s,"\t#mn%POSmain { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]%[WIDTH:%POSpx; %]Z-INDEX:%POS; }\n", id,is_popup?"hidden":"inherit", x, y, h != -1, h-2*bx, w != -1, w-2*bx, z); + /** Write styles for the main DOM element. **/ + htrAddStylesheetItem_va(s, + "\t#mn%POSmain { " + "position:absolute; " + "visibility:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "%[width:"ht_flex_format"; %]" + "z-index:%POS; " + "}\n", + id, + (is_popup) ? "hidden" : "inherit", + ht_flex_x(x, menu), + ht_flex_y(y, menu), + (h != -1), ht_flex_h(h - 2 * bx, menu), + (w != -1), ht_flex_w(w - 2 * bx, menu), + z + ); if (shadow_radius > 0) { htrAddStylesheetItem_va(s,"\t#mn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", id, shadow_offset, shadow_offset, shadow_radius, shadow_color); } - htrAddStylesheetItem_va(s,"\t#mn%POScontent { POSITION:absolute; VISIBILITY: inherit; LEFT:0px; TOP:0px; %[HEIGHT:%POSpx; %]%[WIDTH:%POSpx; %]Z-INDEX:%POS; }\n", id, h != -1, h-2*bx, w != -1, w-2*bx, z+1); if (s->Capabilities.CSS2) htrAddStylesheetItem_va(s,"\t#mn%POSmain { overflow:hidden; border-style: solid; border-width: 1px; border-color: white gray gray white; color:%STR; %STR }\n", id, textcolor, bgstr); - - /** content layer **/ + + /** Write styles for the content container. **/ + htrAddStylesheetItem_va(s, + "\t#mn%POScontent { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:0px; " + "height:%%100;" + "width:%%100;" + "z-index:%POS; " + "}\n", + id, + z + 1 + ); if (s->Capabilities.CSS2) htrAddStylesheetItem_va(s,"\t#mn%POScontent { overflow:hidden; cursor:default; }\n", id ); - /** highlight bar **/ + /** Write styles for the highlight bar. **/ htrAddStylesheetItem_va(s, "\t#mn%POShigh { POSITION:absolute; VISIBILITY: hidden; LEFT:0px; TOP:0px; Z-INDEX:%POS; }\n", id, z); if (s->Capabilities.CSS2) htrAddStylesheetItem_va(s,"\t#mn%POShigh { overflow:hidden; }\n", id ); @@ -324,10 +353,34 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) htrAddScriptInclude(s, "/sys/js/htdrv_menu.js", 0); /** Initialization **/ - htrAddScriptInit_va(s," mn_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), clayer:wgtrGetContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), hlayer:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"mn%POShigh\"), bgnd:\"%STR&JSSTR\", high:\"%STR&JSSTR\", actv:\"%STR&JSSTR\", txt:\"%STR&JSSTR\", w:%INT, h:%INT, horiz:%INT, pop:%INT, name:\"%STR&SYM\"});\n", - name, name, name, id, - bgstr, highlight, active, textcolor, - w, h, is_horizontal, is_popup, name); + htrAddScriptInit_va(s, + "mn_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "clayer:wgtrGetContainer(wgtrGetNodeRef(ns,'%STR&SYM')), " + "hlayer:htr_subel(wgtrGetNodeRef(ns,'%STR&SYM'), 'mn%POShigh'), " + "bgnd:'%STR&JSSTR', " + "high:'%STR&JSSTR', " + "actv:'%STR&JSSTR', " + "txt:'%STR&JSSTR', " + "w:%INT, " + "h:%INT, " + "horiz:%INT, " + "pop:%INT, " + "name:'%STR&SYM', " + "});\n", + name, + name, + name, id, + bgstr, + highlight, + active, + textcolor, + w, + h, + is_horizontal, + is_popup, + name + ); /** Event handlers **/ htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "mn", "mn_mousemove"); @@ -596,5 +649,3 @@ htmenuInitialize() return 0; } - - From 6277f78435d0971538d38339da5d137b7fe24b00 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:28:11 -0700 Subject: [PATCH 046/107] Make objcanvas widget responsive (sort of). Update C generation to create responsive CSS. Update JS to give an error when creating a new layer, which will probably break responsive design. --- centrallix-os/sys/js/htdrv_objcanvas.js | 1 + centrallix/htmlgen/htdrv_objcanvas.c | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/centrallix-os/sys/js/htdrv_objcanvas.js b/centrallix-os/sys/js/htdrv_objcanvas.js index aeaa1fd1a..b32aa93e1 100644 --- a/centrallix-os/sys/js/htdrv_objcanvas.js +++ b/centrallix-os/sys/js/htdrv_objcanvas.js @@ -113,6 +113,7 @@ function oc_add_osrc_object(o) y = parseInt(y); // make and position the layer + console.error("New layer created. This will probably break responsive design."); var l = this.NewLayer(); htr_init_layer(l, this, 'oc'); l.osrc_oid = o.oid; diff --git a/centrallix/htmlgen/htdrv_objcanvas.c b/centrallix/htmlgen/htdrv_objcanvas.c index 6ff6078e8..5a1150533 100644 --- a/centrallix/htmlgen/htdrv_objcanvas.c +++ b/centrallix/htmlgen/htdrv_objcanvas.c @@ -106,7 +106,28 @@ htocRender(pHtSession s, pWgtrNode oc_node, int z) /** Add css item for the layer **/ if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#oc%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; overflow: hidden; %STR}\n",id,x,y,w,h,z,main_bg); + { + htrAddStylesheetItem_va(s, + "\t#oc%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "%STR " + "}\n", + id, + ht_flex_x(x, oc_node), + ht_flex_y(y, oc_node), + ht_flex_w(w, oc_node), + ht_flex_h(h, oc_node), + z, + main_bg + ); + } else htrAddStylesheetItem_va(s,"\t#oc%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; HEIGHT:%POS; Z-INDEX:%POS; }\n",id,x,y,w,h,z); From a2f1f1bf841a9c0e5ec4812b8613cd005cd760c5 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:31:26 -0700 Subject: [PATCH 047/107] Make radiobutton pannel widget responsive. Update C generation to create responsive CSS. Remove unnecessary clip rectangle CSS styles. Clean up HTML & CSS generation code. Add width and height getters in JS that return the current width and height of the widget, allowing areas to resize properly. --- centrallix-os/sys/js/htdrv_radiobutton.js | 4 +- centrallix/htmlgen/htdrv_radiobutton.c | 191 +++++++++++++++++----- 2 files changed, 149 insertions(+), 46 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_radiobutton.js b/centrallix-os/sys/js/htdrv_radiobutton.js index 5b106deb9..2d5c9eb77 100644 --- a/centrallix-os/sys/js/htdrv_radiobutton.js +++ b/centrallix-os/sys/js/htdrv_radiobutton.js @@ -152,7 +152,9 @@ function add_radiobutton(optionPane, param) { } optionPane.yOffset = getRelativeY(optionPane)+getRelativeY(rb.coverPane)+getRelativeY(rb.borderPane); - pg_addarea(rb, getRelativeX(optionPane), optionPane.yOffset, getClipWidth(optionPane), pg_parah+4, optionPane, 'rb', 3); + optionPane.area = pg_addarea(rb, getRelativeX(optionPane), optionPane.yOffset, getClipWidth(optionPane), pg_parah+4, optionPane, 'rb', 3); + optionPane.area.__defineGetter__('width', () => parseInt(getComputedStyle(optionPane).width)); + optionPane.area.__defineGetter__('height', () => parseInt(getComputedStyle(optionPane).height)); } function rb_getfocus(xo,yo,l,c,n,a,from_kbd) diff --git a/centrallix/htmlgen/htdrv_radiobutton.c b/centrallix/htmlgen/htdrv_radiobutton.c index c1d395c06..d5258ed5e 100644 --- a/centrallix/htmlgen/htdrv_radiobutton.c +++ b/centrallix/htmlgen/htdrv_radiobutton.c @@ -9,6 +9,7 @@ #include "cxlib/xhash.h" #include "cxlib/mtsession.h" #include "cxlib/strtcpy.h" +#include "cxlib/util.h" /************************************************************************/ /* Centrallix Application Server System */ @@ -61,13 +62,9 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { char form[64]; pWgtrNode radiobutton_obj, sub_tree; int x=-1,y=-1,w,h; - int top_offset; - int cover_height, cover_width; - int item_spacing; int id, i, j; int is_selected; - int rb_cnt; - int cover_margin; + int raido_button_count; char fieldname[32]; char value[64]; char label[64]; @@ -137,19 +134,74 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { htrAddScriptInclude(s, "/sys/js/htdrv_radiobutton.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - /** Ok, write the style header items. **/ - top_offset = s->ClientInfo->ParagraphHeight*3/4+1; - cover_height = h-(top_offset+3+2); - cover_width = w-(2*3 +2); - htrAddStylesheetItem_va(s,"\t#rb%POSparent { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,x,y,w,h,z,w,h); - htrAddStylesheetItem_va(s,"\t#rb%POSborder { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,3,top_offset,w-(2*3),h-(top_offset+3),z+1,w-(2*3),h-(top_offset+3)); - htrAddStylesheetItem_va(s,"\t#rb%POScover { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,1,1,cover_width,cover_height,z+2,cover_width,cover_height); - htrAddStylesheetItem_va(s,"\t#rb%POStitle { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n", - id,10,1,w/2,s->ClientInfo->ParagraphHeight,z+3); - + /** Write style headers for container DOM nodes. **/ + const int para_height = s->ClientInfo->ParagraphHeight; + fprintf(stderr, "h: %d\n", para_height); + const int top_offset = (para_height * 3) / 4 + 1; + htrAddStylesheetItem_va(s, + "#rb%POSparent { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ); + htrAddStylesheetItem_va(s, + "#rb%POSborder { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:3px; " + "top:%POSpx; " + "width:calc(100%% - 6px); " + "height:calc(100%% - %POSpx); " + "z-index:%POS; " + "}\n", + id, + top_offset, + top_offset + 3, + z + 1 + ); + htrAddStylesheetItem_va(s, + "#rb%POScover { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:1px; " + "top:1px; " + "width:calc(100%% - 2px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "}\n", + id, + z + 2 + ); + htrAddStylesheetItem_va(s, + "#rb%POStitle { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:10px; " + "top:1px; " + "width:50%%; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, + para_height, + z + 3 + ); + htrAddScriptGlobal(s, "radiobutton", "null", 0); /** DOM linkages **/ @@ -157,27 +209,23 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(htr_subel(_obj,\"rb%POSborder\"),\"rb%POScover\")",id,id); /** Loop through each radiobutton and flag it NOOBJECT **/ - rb_cnt = 0; + raido_button_count = 0; for (j=0;jChildren));j++) { radiobutton_obj = xaGetItem(&(tree->Children), j); radiobutton_obj->RenderFlags |= HT_WGTF_NOOBJECT; wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - rb_cnt++; - } + if (strcmp(ptr,"widget/radiobutton") == 0) raido_button_count++; } - /* - Now lets loop through and create a style sheet for each optionpane on the - radiobuttonpanel - */ - item_spacing = 12 + s->ClientInfo->ParagraphHeight; - cover_margin = 10; - if (item_spacing*rb_cnt+2*cover_margin > cover_height) - item_spacing = (cover_height-2*cover_margin)/rb_cnt; - if (item_spacing*rb_cnt+2*cover_margin > cover_height) - cover_margin = (cover_height-(item_spacing*rb_cnt))/2; + + /** Compute values for laying out radio buttons. **/ + const int cover_height = h - top_offset - 6; + int item_spacing = para_height + 12; + int cover_margin = 10; + if (item_spacing * raido_button_count + 2*cover_margin > cover_height) + item_spacing = (cover_height-2*cover_margin)/raido_button_count; + if (item_spacing * raido_button_count + 2*cover_margin > cover_height) + cover_margin = (cover_height-(item_spacing*raido_button_count))/2; if (cover_margin < 2) cover_margin = 2; i = 1; for (j=0;jChildren));j++) @@ -186,8 +234,22 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); if (!strcmp(ptr,"widget/radiobutton")) { - htrAddStylesheetItem_va(s,"\t#rb%POSoption%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px, %POSpx, %POSpx, 0px); }\n", - id,i,7,cover_margin+((i-1)*item_spacing)+3,cover_width-7,item_spacing,z+2,cover_width-7,item_spacing); + htrAddStylesheetItem_va(s, + "#rb%POSoption%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:7px; " + "top:%INTpx; " + "width:calc(100%% - 7px); " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, i, + cover_margin + ((i-1) * item_spacing) + 2, + min(item_spacing, para_height + 4), + z + 2 + ); i++; } } @@ -266,19 +328,58 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { if (!strcmp(ptr,"widget/radiobutton")) { /** CSS layers **/ - htrAddStylesheetItem_va(s,"\t#rb%POSbuttonset%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,5,2+(s->ClientInfo->ParagraphHeight-12)/2,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSbuttonunset%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,5,2+(s->ClientInfo->ParagraphHeight-12)/2,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSvalue%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,i,5,5,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSlabel%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,27,2,cover_width-(27+1),item_spacing-1,z+2,cover_width-(27+1),item_spacing-1); + htrAddStylesheetItem_va(s, + "#rb%POSbuttonset%POS, " + "#rb%POSbuttonunset%POS { " + "position:absolute; " + "overflow:hidden; " + "left:5px; " + "top:%INTpx; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + id, i, + (para_height / 2) - 3, + z + 2 + ); + htrAddStylesheetItem_va(s, + "#rb%POSvalue%POS { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:5px; " + "top:6px; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "}\n", + id, i, + z + 2 + ); + htrAddStylesheetItem_va(s, + "#rb%POSlabel%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:27px; " + "top:3px; " + "width:calc(100%% - 27px); " + "height:%POSpx; " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + item_spacing - 1, + z + 2 + ); /** Body layers **/ htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); + htrAddBodyItem_va(s,"
\n", id, i); + htrAddBodyItem_va(s,"
\n", id, i); wgtrGetPropertyValue(radiobutton_obj,"label",DATA_T_STRING,POD(&ptr)); strtcpy(sbuf2,ptr,sizeof(sbuf2)); From 0e35c681ce894486e0b7945295b55f4657e0e62f Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:35:39 -0700 Subject: [PATCH 048/107] Remove unnecessary width from htdrv_treeview.js. --- centrallix-os/sys/js/htdrv_treeview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_treeview.js b/centrallix-os/sys/js/htdrv_treeview.js index b3d4f1873..fb89579fa 100644 --- a/centrallix-os/sys/js/htdrv_treeview.js +++ b/centrallix-os/sys/js/htdrv_treeview.js @@ -38,7 +38,6 @@ function tv_new_layer(width,pdoc,l) else if(cx__capabilities.Dom1HTML) { nl = document.createElement('DIV'); - if (width) nl.style.width = width + 'px'; nl.className = l.divclass; //setClip(0, width, 0, 0); pg_set_style(nl, 'position','absolute'); @@ -1297,7 +1296,7 @@ function tv_mouseover(e) if (e.kind == 'tv') { cn_activate(e.mainlayer, 'MouseOver'); - if (getClipWidth(e.layer) <= getdocWidth(e.layer)+2 && e.layer.link_txt) + if (e.layer.link_txt) e.layer.tipid = pg_tooltip(e.layer.link_txt, e.pageX, e.pageY); } return EVENT_CONTINUE | EVENT_ALLOW_DEFAULT_ACTION; From 03c915337a2d52f4417b2f81320f6357089365c3 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:38:25 -0700 Subject: [PATCH 049/107] Make window widget responsive. Clean up C code for generation and remove dead clutter. Improve readability of wn_do_move(). Add wn_do_move_internal(). Add resize observer to keep windows on screen when the page is resized. --- centrallix-os/sys/js/htdrv_window.js | 118 +++++++++---- centrallix/htmlgen/htdrv_window.c | 249 +++++++++++++++++---------- 2 files changed, 242 insertions(+), 125 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_window.js b/centrallix-os/sys/js/htdrv_window.js index 6cdcf0da9..a0b2367a9 100644 --- a/centrallix-os/sys/js/htdrv_window.js +++ b/centrallix-os/sys/js/htdrv_window.js @@ -136,6 +136,24 @@ function wn_init(param) pg_addsched_fn(window, "pg_reveal_event", [l,l,'Reveal'], 0); } + // Setup responsive movement. + // resize_listener.params stores the params from the last time the window + // was moved, allowing us to reuse them to call wn_do_move_internal(), + // allowing that function to recalculate the window position based on the + // new viewport size. + const resize_listener = l.resize_listener = {}; + resize_listener.handler = () => + { + const { pg_attract, wn_new_x, wn_new_y } = l.resize_listener.params; + wn_do_move_internal(l, pg_attract, wn_new_x, wn_new_y); + }; + resize_listener.params = { + pg_attract: 0, + wn_new_x: getPageX(l), + wn_new_y: getPageY(l), + }; + window.addEventListener('resize', resize_listener.handler); + // force on page... if (getPageY(l) + l.orig_height > getInnerHeight()) { @@ -658,33 +676,71 @@ function wn_setvisibility(aparam) } } -function wn_domove2() - { +/*** This function does movement without worrying about global variables, + *** resize observers, etc. It just takes params and does movement. + *** + *** This function does handle snapping to edges and preventing windows from + *** being moved too far outside the viewport. + *** + *** @param wn_current The window to affect. + *** @param pg_attract The number of pixels from the edge of the viewport at + *** which windows snap to the edge. (Seems to always be 0.) + *** @param wn_new_x The new x coordinate for moving the window. + *** @param wn_new_y The new y coordinate for moving the window. + ***/ +function wn_do_move_internal(wn_current, pg_attract, wn_new_x, wn_new_y) + { + /** Get useful values. **/ + const { innerWidth, innerHeight } = window; + const window_width = getClipWidth(wn_current); + const window_height = getClipHeight(wn_current); + + /** Calculate available width and height, taking the sizes of scrollbars into account. **/ + const available_width = innerWidth - ((document.height - innerHeight - 2 >= 0) ? 15 : 0); + const available_height = innerHeight - ((document.width - innerWidth - 2 >= 0) ? 15 : 0); + let new_x, new_y; + + /** X: Handle snapping to edges. **/ + if (Math.isBetween(-pg_attract, wn_new_x, pg_attract)) new_x = 0; + else if (Math.isBetween(available_width - pg_attract, wn_new_x + window_width, available_width + pg_attract)) + new_x = available_width - window_width; + + /** X: Prevent windows getting lost off the left side of the page. **/ + else if (wn_new_x + window_width < 24) new_x = 24 - window_width; + else if (wn_new_x > available_width - 32) new_x = available_width - 32; + + /** X: Default case, no movement needed. **/ + else new_x = wn_new_x; + + /** Y: Handle snapping to edges. **/ + if (Math.isBetween(-pg_attract, wn_new_y, pg_attract)) new_y = 0; + else if (Math.isBetween(available_height - pg_attract, wn_new_y + window_height, available_height + pg_attract)) + new_y = available_height - window_height; + + /** Y: Prevent windows from going too far off the screen. **/ + else new_y = Math.clamp(0, wn_new_y, available_height - 24); + + /** Move the window to the new location. **/ + moveToAbsolute(wn_current, new_x, new_y); + /** Clicking and dragging a window is not a click. **/ + wn_current.clicked = 0; } -function wn_domove() +function wn_do_move() { - if (wn_current != null) - { - var ha=(document.height-window.innerHeight-2)>=0?15:0; - var va=(document.width-window.innerWidth-2)>=0?15:0; - var newx,newy; - if (wn_newx < pg_attract && wn_newx > -pg_attract) newx = 0; - else if (wn_newx+getClipWidth(wn_current) > window.innerWidth-ha-pg_attract && wn_newx+ getClipWidth(wn_current) < window.innerWidth-ha+pg_attract) - newx = window.innerWidth-ha-pg_get_style(wn_current,'clip.width'); - else if (wn_newx+getClipWidth(wn_current) < 25) newx = 25-pg_get_style(wn_current,'clip.width'); - else if (wn_newx > window.innerWidth-35-ha) newx = window.innerWidth-35-ha; - else newx = wn_newx; - if (wn_newy<0) newy = 0; - else if (wn_newy > window.innerHeight-12-va) newy = window.innerHeight-12-va; - else if (wn_newy < pg_attract) newy = 0; - else if (wn_newy+getClipHeight(wn_current) > window.innerHeight-va-pg_attract && wn_newy+getClipHeight(wn_current) < window.innerHeight-va+pg_attract) - newy = window.innerHeight-va-pg_get_style(wn_current,'clip.height'); - else newy = wn_newy; - moveToAbsolute(wn_current,newx,newy); - wn_current.clicked = 0; - } + /** Dereference globals once for performance. **/ + const { wn_current, pg_attract, wn_new_x, wn_new_y } = window; + + /** No window is selected, so we don't have to move anything. **/ + if (wn_current === null) return true; + + /** Call the unresponsive version. */ + wn_do_move_internal(wn_current, pg_attract, wn_new_x, wn_new_y); + + /** Update params for future resize calls. */ + wn_current.resize_listener.params = { pg_attract, wn_new_x, wn_new_y }; + return true; } @@ -736,8 +792,8 @@ function wn_mousedown(e) wn_current = e.mainlayer; wn_msx = e.pageX; wn_msy = e.pageY; - wn_newx = null; - wn_newy = null; + wn_new_x = null; + wn_new_y = null; wn_moved = 0; if (!cx__capabilities.Dom0IE) wn_windowshade_ns_moz(e.mainlayer); return EVENT_CONTINUE | EVENT_PREVENT_DEFAULT_ACTION; @@ -787,17 +843,17 @@ function wn_mousemove(e) wn_current.clicked = 0; if (wn_current.tid) clearTimeout(wn_current.tid); wn_current.tid = null; - if (wn_newx == null) + if (wn_new_x == null) { - wn_newx = getPageX(wn_current) + e.pageX-wn_msx; - wn_newy = getPageY(wn_current) + e.pageY-wn_msy; + wn_new_x = getPageX(wn_current) + e.pageX-wn_msx; + wn_new_y = getPageY(wn_current) + e.pageY-wn_msy; } else { - wn_newx += (e.pageX - wn_msx); - wn_newy += (e.pageY - wn_msy); + wn_new_x += (e.pageX - wn_msx); + wn_new_y += (e.pageY - wn_msy); } - setTimeout(wn_domove,60); + wn_do_move(); wn_moved = 1; wn_msx = e.pageX; wn_msy = e.pageY; diff --git a/centrallix/htmlgen/htdrv_window.c b/centrallix/htmlgen/htdrv_window.c index a02da42bb..5b6fbcd5d 100644 --- a/centrallix/htmlgen/htdrv_window.c +++ b/centrallix/htmlgen/htdrv_window.c @@ -3,8 +3,10 @@ #include #include #include + #include "ht_render.h" #include "obj.h" +#include "cxlib/util.h" #include "cxlib/mtask.h" #include "cxlib/xarray.h" #include "cxlib/xhash.h" @@ -59,14 +61,11 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; char name[64]; - pWgtrNode sub_tree; - int x,y,w,h; - int tbw,tbh,bx,by,bw,bh; int id, i; int visible = 1; - char bgnd_style[128] = ""; - char hdr_bgnd_style[128] = ""; - char txtcolor[64] = ""; + char background_style[128] = ""; + char header_background_style[128] = ""; + char text_color[64] = ""; int has_titlebar = 1; char title[128]; int is_dialog_style = 0; @@ -76,7 +75,7 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) int is_toplevel = 0; int is_modal = 0; char icon[128]; - int shadow_offset,shadow_radius,shadow_angle; + int shadow_offset, shadow_radius, shadow_angle; char shadow_color[128]; int border_radius; char border_color[128]; @@ -99,6 +98,7 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) id = (HTWIN.idcnt++); /** Get x,y,w,h of this object **/ + int x, y, w, h; if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x = 0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y = 0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) @@ -156,17 +156,17 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) is_modal = htrGetBoolean(tree, "modal", 0); /** Check background color **/ - htrGetBackground(tree, NULL, 1, bgnd_style, sizeof(bgnd_style)); + htrGetBackground(tree, NULL, 1, background_style, sizeof(background_style)); /** Check header background color/image **/ - if (htrGetBackground(tree, "hdr", 1, hdr_bgnd_style, sizeof(hdr_bgnd_style)) < 0) - strcpy(hdr_bgnd_style, bgnd_style); + if (htrGetBackground(tree, "hdr", 1, header_background_style, sizeof(header_background_style)) < 0) + strcpy(header_background_style, background_style); /** Check title text color. **/ if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(txtcolor,ptr,sizeof(txtcolor)); + strtcpy(text_color, ptr, sizeof(text_color)); else - strcpy(txtcolor,"black"); + strcpy(text_color, "black"); /** Check window title. **/ if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) @@ -208,79 +208,95 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) } /** Compute titlebar width & height - includes edge below titlebar. **/ - if (has_titlebar) - { - tbw = w-2; - if (is_dialog_style || !s->Capabilities.Dom0NS) - tbh = 24; - else - tbh = 23; - } - else - { - tbw = w-2; - tbh = 0; - } - - /** Compute window body geometry **/ - if (is_dialog_style) - { - bx = 1; - by = 1+tbh; - bw = w-2; - bh = h-tbh-2; - } - else - { - bx = 2; - bw = w-4; - if (has_titlebar) - { - by = 1+tbh; - bh = h-tbh-3; - } - else - { - by = 2; - bh = h-4; - } - } + int title_bar_height = (has_titlebar) ? ((is_dialog_style) ? 24 : 23) : 0; /** Draw the main window layer and outer edge. **/ - /*htrAddStylesheetItem_va(s,"\t#wn%POSbase { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS;}\n", - id,visible?"inherit":"hidden",x,y,w-2*box_offset,h-2*box_offset, w, h, z+100);*/ - htrAddStylesheetItem_va(s,"\t#wn%POSbase { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; overflow: hidden; Z-INDEX:%POS;}\n", - id,visible?"inherit":"hidden",x,y,w-2*box_offset,h-2*box_offset, z+100); - htrAddStylesheetItem_va(s,"\t#wn%POSbase { border-style: %STR&CSSVAL; border-width: %INTpx; border-color: %STR&CSSVAL; border-radius: %INTpx; }\n", - id, border_style, border_width, border_color, border_radius); + /*** We don't even bother making these styles flex responsively because + *** they will be overwritten by the JS anyway. + ***/ + htrAddStylesheetItem_va(s, + "\t#wn%POSbase { " + "position:absolute; " + "visibility:%STR; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "overflow:hidden; " + "z-index:%POS; " + "border-style:%STR&CSSVAL; " + "border-width:%INTpx; " + "border-color:%STR&CSSVAL; " + "border-radius:%INTpx; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + x, + y, + w - 2 * box_offset, + h - 2 * box_offset, + z + 100, + border_style, + border_width, + border_color, + border_radius + ); if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#wn%POSbase { box-shadow: %DBLpx %DBLpx %POSpx %STR&CSSVAL; }\n", id, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color); - } - - /** draw titlebar div **/ - if (has_titlebar) - { - htrAddStylesheetItem_va(s,"\t#wn%POStitlebar { POSITION: absolute; VISIBILITY: inherit; LEFT: 0px; TOP: 0px; HEIGHT: %POSpx; WIDTH: 100%%; overflow: hidden; Z-INDEX: %POS; color:%STR&CSSVAL; cursor:default; %STR}\n", id, tbh-1-box_offset, z+1, txtcolor, hdr_bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POStitlebar { border-style: solid; border-width: 0px 0px 1px 0px; border-color: gray; }\n", id); + double shadow_angle_radians = (double)shadow_angle * M_PI/180; + htrAddStylesheetItem_va(s, + "\t#wn%POSbase { box-shadow: %DBLpx %DBLpx %POSpx %STR&CSSVAL; }\n", id, + sin(shadow_angle_radians)*shadow_offset, cos(shadow_angle_radians)*(-shadow_offset), shadow_radius, shadow_color + ); } /** inner structure depends on dialog vs. window style **/ + int main_width, main_height, clip_height, dialogue_width; + char* border_color_str; if (is_dialog_style) { /** window inner container -- dialog **/ - htrAddStylesheetItem_va(s,"\t#wn%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:%INTpx; WIDTH: %POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS; %STR}\n", - id, tbh?(tbh-1):0, w-2, h-tbh-1, w, h-tbh+1, z+1, bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POSmain { border-style: solid; border-width: %POSpx 0px 0px 0px; border-color: white; }\n", id, has_titlebar?1:0); + main_width = w - 2; + main_height = h - title_bar_height - 1; + clip_height = h - title_bar_height + 1; + dialogue_width = 0; + border_color_str = "white"; } else { /** window inner container -- window **/ - htrAddStylesheetItem_va(s,"\t#wn%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:%INTpx; WIDTH: %POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS; %STR}\n", - id, tbh?(tbh-1):0, w-2-2*box_offset, h-tbh-(has_titlebar?1:2)-(has_titlebar?1:2)*box_offset, w, h-tbh+(has_titlebar?1:0)-2*box_offset, z+1, bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POSmain { border-style: solid; border-width: %POSpx 1px 1px 1px; border-color: gray white white gray; }\n", id, has_titlebar?0:1); + main_width = w - 2 * (box_offset + 1); + main_height = h - title_bar_height - (box_offset + 1) * ((has_titlebar) ? 1 : 2); + clip_height = h - title_bar_height + ((has_titlebar) ? 1 : 0) - 2 * box_offset; + dialogue_width = 1; + border_color_str = "gray white white gray"; } + htrAddStylesheetItem_va(s, + "\t#wn%POSmain { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "overflow:hidden; " + "clip:rect(0px, %INTpx, %INTpx, 0px); " + "border-style:solid; " + "border-color:%STR; " + "border-width:%POSpx %POSpx %POSpx %POSpx; " + "z-index:%POS; " + "%STR" + "}\n", + id, + max(title_bar_height - 1, 0), + main_width, + main_height, + w, clip_height, + border_color_str, + (has_titlebar) ? 1 : 0, dialogue_width, dialogue_width, dialogue_width, + z + 1, + background_style + ); /** Write globals for internal use **/ htrAddScriptGlobal(s, "wn_top_z","10000",0); @@ -295,7 +311,7 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) htrAddScriptGlobal(s, "wn_clicked","0",0); /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "wn%POSbase",id); + htrAddWgtrObjLinkage_va(s, tree, "wn%POSbase", id); htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, \"wn%POSmain\")",id); htrAddScriptInclude(s, "/sys/js/htdrv_window.js", 0); @@ -312,36 +328,81 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "wn", "wn_mouseout"); /** Script initialization call. **/ + htrAddScriptInit_va(s, + "wn_init({ " + "mainlayer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "clayer:wgtrGetContainer(wgtrGetNodeRef(ns, '%STR&SYM')), " + "titlebar:%[htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'wn%POStitlebar')%]%[null%], " + "gshade:%INT, " + "closetype:%INT, " + "toplevel:%INT, " + "modal:%INT, " + "});\n", + name, + name, + has_titlebar, name, id, !has_titlebar, + gshade, + closetype, + is_toplevel, + is_modal + ); + + /** Write HTML for the child window. **/ + htrAddBodyItem_va(s, "
\n", id); if (has_titlebar) { - htrAddScriptInit_va(s," wn_init({mainlayer:wgtrGetNodeRef(ns,'%STR&SYM'), clayer:wgtrGetContainer(wgtrGetNodeRef(ns,'%STR&SYM')), gshade:%INT, closetype:%INT, toplevel:%INT, modal:%INT, titlebar:htr_subel(wgtrGetNodeRef(ns,'%STR&SYM'),'wn%POStitlebar')});\n", - name,name,gshade,closetype, is_toplevel, is_modal, name, id); + /** Write styles and HTML for the title bar. **/ + htrAddStylesheetItem_va(s, + "\t#wn%POStitlebar { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:0px; " + "height:%POSpx; " + "width:calc(100%% - 4px); " + "overflow:hidden; " + "z-index:%POS; " + "color:%STR&CSSVAL; " + "cursor:default; " + "border-style:solid; " + "border-width:0px 0px 1px 0px; " + "border-color:gray; " + "%STR" + "}\n", + id, + title_bar_height - 1 - box_offset, + z + 1, + text_color, + header_background_style + ); + htrAddBodyItem_va(s, + "
" + "" + "
%STR&HTE
" + "" + "
\n", + id, + icon, + text_color, title + ); } - else - { - htrAddScriptInit_va(s," wn_init({mainlayer:wgtrGetNodeRef(ns,'%STR&SYM'), clayer:wgtrGetNodeRef(ns,'%STR&SYM'), gshade:%INT, closetype:%INT, toplevel:%INT, modal:%INT, titlebar:null});\n", - name,name,gshade,closetype, is_toplevel, is_modal); - } - - /** HTML body
elements for the layers. **/ - htrAddBodyItem_va(s,"
\n",id); - if (has_titlebar) - { - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
 %STR&HTE
\n", - tbh-1, tbw-2, icon, txtcolor, title); - htrAddBodyItem(s, "
\n"); - } - htrAddBodyItem_va(s,"
\n",id); - /** Check for more sub-widgets within the page. **/ + /** Write HTML for child widgets in the window. **/ + htrAddBodyItem_va(s,"
\n",id); for (i=0;iChildren));i++) { - sub_tree = xaGetItem(&(tree->Children), i); - htrRenderWidget(s, sub_tree, z+2); + /** TODO: Israel - Rewrite this using util.h, once its updated from the dups branch. **/ + const pWgtrNode child = xaGetItem(&(tree->Children), i); + const int ret = htrRenderWidget(s, child, z + 2); + if (ret != 0) + { + mssError(0, "HTWIN", + "Failed to render child widget '%s:%s' with error code %d.", + child->Namespace, child->Name, ret + ); + } } - - htrAddBodyItem(s,"
\n"); + htrAddBodyItem(s,"
\n"); return 0; } From 56e114ae3aaed84c9171b3b7cac0d4ed38c8a528 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:40:16 -0700 Subject: [PATCH 050/107] Make active areas responsive. Add resize observer to htdrv_page.js to update areas/boxes when the page is resized. Fix undefined layer edge case in pg_setkbdfocus(). Improve code readability. --- centrallix-os/sys/js/htdrv_page.js | 96 ++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_page.js b/centrallix-os/sys/js/htdrv_page.js index 85c58c5b5..3023ea47c 100755 --- a/centrallix-os/sys/js/htdrv_page.js +++ b/centrallix-os/sys/js/htdrv_page.js @@ -51,6 +51,11 @@ function pg_scriptavailable(s) } +/** Quick and dirty resize handling tool. **/ +const resize_handlers = {}; +window.addEventListener('resize', (e) => Object.values(resize_handlers).forEach(f => f(e))); + + //START SECTION: DOM/CSS helper functions ----------------------------------- /** returns an attribute of the element in pixels **/ @@ -1929,7 +1934,9 @@ function pg_findfocusarea(l, xo, yo) function pg_setmousefocus(l, xo, yo) { - var a = pg_findfocusarea(l, xo, yo); + if (!l) return false; + + const a = pg_findfocusarea(l, xo, yo); if (a && a != pg_curarea) { pg_curarea = a; @@ -1937,26 +1944,52 @@ function pg_setmousefocus(l, xo, yo) { if (!pg_curarea.layer.getmousefocushandler || pg_curarea.layer.getmousefocushandler(xo, yo, a.layer, a.cls, a.name, a)) { - // wants mouse focus - var offs = $(pg_curarea.layer).offset(); - //var x = getPageX(pg_curarea.layer)+pg_curarea.x; - //var y = getPageY(pg_curarea.layer)+pg_curarea.y; - var x = offs.left+pg_curarea.x; - var y = offs.top+pg_curarea.y; - - var w = pg_curarea.width; - var h = pg_curarea.height; - if (cx__capabilities.Dom0NS) + // Create a function to handle all box updates with this focus. + const update_box = (area) => { - pg_mkbox(l, x,y,w,h, 1, document.layers.pgtop,document.layers.pgbtm,document.layers.pgrgt,document.layers.pglft, page.mscolor1, page.mscolor2, document.layers.pgktop.zIndex-1); - } - else if (cx__capabilities.Dom1HTML) - { - pg_mkbox(l, x,y,w,h, 1, document.getElementById("pgtop"),document.getElementById("pgbtm"),document.getElementById("pgrgt"),document.getElementById("pglft"), page.mscolor1, page.mscolor2, htr_getzindex(document.getElementById("pgktop"))-1); - } + // Compute layout data. + const offs = $(area.layer).offset(); + const x = area.x + offs.left; + const y = area.y + offs.top; + const w = area.width; + const h = area.height; + + if (cx__capabilities.Dom0NS) + { + pg_mkbox(l, + x, y, w, h, 1, + document.layers.pgtop, + document.layers.pgbtm, + document.layers.pgrgt, + document.layers.pglft, + page.mscolor1, page.mscolor2, + document.layers.pgktop.zIndex - 1 + ); + } + else if (cx__capabilities.Dom1HTML) + { + pg_mkbox(l, + x, y, w, h, 1, + document.getElementById("pgtop"), + document.getElementById("pgbtm"), + document.getElementById("pgrgt"), + document.getElementById("pglft"), + page.mscolor1, page.mscolor2, + htr_getzindex(document.getElementById("pgktop")) - 1 + ); + } + }; + + // Initial update. + update_box(pg_curarea); + + // Responsive updates. + const area = pg_curarea; // Save value so we can create a closure below. + resize_handlers.mouse_focus = () => update_box(area); } } } + if (!a) delete resize_handlers.mouse_focus; } function pg_removekbdfocus(p) @@ -1975,16 +2008,30 @@ function pg_removekbdfocus(p) pg_mkbox(null,0,0,0,0, 1, document.getElementById("pgktop"),document.getElementById("pgkbtm"),document.getElementById("pgkrgt"),document.getElementById("pgklft"), page.kbcolor1, page.kbcolor2, pg_get_style(document.getElementById("pgtop"), 'zIndex')+100); } } + + // Clear resize handling. + delete resize_handlers.mouse_focus; + delete resize_handlers.data_focus; + delete resize_handlers.kbd_focus; + return true; } function pg_setdatafocus(a) { + if (!a) return false; + var x = getPageX(a.layer)+a.x; var y = getPageY(a.layer)+a.y; var w = a.width; var h = a.height; var l = a.layer; + + // Setup resize handling. + resize_handlers.data_focus = () => { + // Recall function to update values. + pg_setdatafocus(a); + }; // hide old data focus box if (l.pg_dttop != null) @@ -2037,6 +2084,8 @@ function pg_setdatafocus(a) function pg_setkbdfocus(l, a, xo, yo) { + if (!l) return false; + var from_kbd = false; if (xo == null && yo == null) { @@ -2068,6 +2117,12 @@ function pg_setkbdfocus(l, a, xo, yo) pg_curkbdarea = a; pg_curkbdlayer = l; + // Setup resize handling. + resize_handlers.kbd_focus = () => { + // Recall function to update values. + pg_setkbdfocus(l, a, xo, yo); + }; + if (pg_curkbdlayer && pg_curkbdlayer.getfocushandler) { v=pg_curkbdlayer.getfocushandler(xo,yo,a.layer,a.cls,a.name,a,from_kbd); @@ -3040,9 +3095,12 @@ function pg_check_resize(l) { if (wgtrGetServerProperty(l, "height") != $(l).height()) { - if (wgtrGetParent(l).childresize) + const parent = wgtrGetParent(l); + if (parent.childresize) { - var geom = wgtrGetParent(l).childresize(l, wgtrGetServerProperty(l, "width"), wgtrGetServerProperty(l, "height"), $(l).width(), $(l).height()); + const width = wgtrGetServerProperty(l, "width"); + const height = wgtrGetServerProperty(l, "height"); + const geom = parent.childresize(l, width, height, $(l).width(), $(l).height()); if (geom) { wgtrSetServerProperty(l, "height", geom.height); From cfc5e47e243384a7249b6cd0a572c62afb58ecb0 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:40:34 -0700 Subject: [PATCH 051/107] Cleanup unused comments. --- centrallix-os/sys/js/htdrv_osrc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_osrc.js b/centrallix-os/sys/js/htdrv_osrc.js index f256cd70e..7e3cdc89f 100644 --- a/centrallix-os/sys/js/htdrv_osrc.js +++ b/centrallix-os/sys/js/htdrv_osrc.js @@ -2330,8 +2330,6 @@ function osrc_open_query_startat() else this.querysize = this.replicasize; this.query_ended = false; - //this.LogStatus(); - //console.log('OSRC ' + this.__WgtrName + ': startat ' + this.startat + ', rowcount ' + this.querysize); this.DoRequest('multiquery', '/', {ls__startat:this.startat, ls__autoclose_sr:1, ls__autofetch:1, ls__objmode:0, ls__notify:this.request_updates, ls__rowcount:this.querysize, ls__sql:this.query, ls__sqlparam:this.EncodeParams()}, osrc_get_qid_startat); } From 1e0d948680c04b98f7a67bea1fabc44376acc3a0 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:41:24 -0700 Subject: [PATCH 052/107] Add error messages to the image widget. Add an error when the offset action is used, warning that it breaks responsive design. Add an error when the scale action is used, warning that it breaks responsive design. --- centrallix-os/sys/js/htdrv_image.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/centrallix-os/sys/js/htdrv_image.js b/centrallix-os/sys/js/htdrv_image.js index a4a608022..38d05b6cf 100644 --- a/centrallix-os/sys/js/htdrv_image.js +++ b/centrallix-os/sys/js/htdrv_image.js @@ -146,12 +146,18 @@ function im_set_scale(a, v) function im_action_offset(aparam) { + /** This action is never used anywhere and looks hard to update, so it's deprecated for now. **/ + console.error("The offset action is no longer supported. Using it may break responsive layouts."); + im_set_x.call(this, "xoffset", aparam.X); im_set_y.call(this, "yoffset", aparam.Y); } function im_action_scale(aparam) { + /** This action is never used anywhere and looks hard to update, so it's deprecated for now. **/ + console.error("The scale action is no longer supported. Using it may break responsive layouts."); + im_set_scale.call(this, "scale", aparam.Scale); } From 44bbef46516f03efd2eda13bbf0a5c789db450da Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:58:40 -0700 Subject: [PATCH 053/107] Make table widget back end code responsive. Improve HTML generation C code. Reformat script call to massively improve readability. Reorder attributes to improve code organization. Rename box to scroll thumb for clarity and consistency. Remove code that renamed legacy attributes to their new names (no longer needed). Remove code that wrote unused attributes to the front end. Remove code that read unused attributes which were never used. Remove unused attributes from the httbl_struct struct. Remove unused variables. Add a TODO to investigate some strange looking logic. Add comments with comment anchors for faster code navigation. Add comment explaining where to find more info about comment anchors. Note: This comment will be wrong until the dups branch is merged to add the required info. Clean up code, comments, and formatting. --- centrallix/htmlgen/htdrv_table.c | 565 +++++++++++++++++++------------ 1 file changed, 352 insertions(+), 213 deletions(-) diff --git a/centrallix/htmlgen/htdrv_table.c b/centrallix/htmlgen/htdrv_table.c index 54dc47f41..5876bf658 100644 --- a/centrallix/htmlgen/htdrv_table.c +++ b/centrallix/htmlgen/htdrv_table.c @@ -35,7 +35,7 @@ /* A copy of the GNU General Public License has been included in this */ /* distribution in the file "COPYING". */ /* */ -/* Module: htdrv_table.c */ +/* Module: htdrv_table.c */ /* Author: Greg Beeley (GRB) */ /* Creation: October 29, 1999 */ /* Description: HTML Widget driver for a data-driven table. Has three */ @@ -58,6 +58,10 @@ /* only. Dynamicrow tables use the most client resources. */ /************************************************************************/ +/*** This file uses the optional Comment Anchors VSCode extension, documented + *** with CommentAnchorsExtension.md in centrallix-sysdoc. + ***/ + #define HTTBL_MAX_COLS (64) @@ -92,12 +96,7 @@ typedef struct typedef struct { char name[64]; - char sbuf[160]; - char tbl_bgnd[128]; char hdr_bgnd[128]; - char row_bgnd1[128]; - char row_bgnd2[128]; - char row_bgndhigh[128]; char colsep_bgnd[128]; char textcolor[64]; char textcolorhighlight[64]; @@ -105,34 +104,25 @@ typedef struct char newrow_bgnd[128]; char newrow_textcolor[64]; char osrc[64]; - char row_border[64]; - char row_shadow_color[64]; - int row_shadow; - int row_shadow_radius; - int row_radius; - int x,y,w,h; + httbl_col* col_infs[HTTBL_MAX_COLS]; + int ncols; int id; + int x,y,w,h; int data_mode; /* 0="rows" or 1="properties" */ - int outer_border; - int inner_border; int inner_padding; - httbl_col* col_infs[HTTBL_MAX_COLS]; - int ncols; int windowsize; int min_rowheight; int max_rowheight; int cellhspacing; int cellvspacing; - int followcurrent; int dragcols; int colsep; int colsep_mode; - int gridinemptyrows; + int grid_in_empty_rows; int allow_selection; int show_selection; int initial_selection; int allow_deselection; - int reverse_order; int overlap_scrollbar; /* scrollbar overlaps with table */ int hide_scrollbar; /* don't show scrollbar at all */ int demand_scrollbar; /* only show scrollbar when needed */ @@ -140,20 +130,11 @@ typedef struct int rowcache_size; /* number of rows the table caches for display */ } httbl_struct; + int httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) { - int colid; - char *ptr; - int i; - pWgtrNode sub_tree; - int subcnt = 0; - char *nptr; - int h; - int first_offset = (t->has_header)?(t->min_rowheight + t->cellvspacing):0; - pWgtrNode children[32]; - int detailcnt; - httbl_col* col; + char* ptr; if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) { @@ -161,107 +142,279 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) return -1; } - /** STYLE for the layer **/ - htrAddStylesheetItem_va(s,"\t#tbld%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; } \n",t->id,t->x,t->y,(t->overlap_scrollbar)?(t->w):(t->w-18),z+0); - htrAddStylesheetItem_va(s,"\t#tbld%POSscroll { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:18px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",t->id,(t->hide_scrollbar || t->demand_scrollbar)?"hidden":"inherit",t->x+t->w-18,t->y+first_offset,t->h-first_offset,z+0); - htrAddStylesheetItem_va(s,"\t#tbld%POSbox { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:16px; HEIGHT:16px; Z-INDEX:%POS; BORDER: solid 1px; BORDER-COLOR: white gray gray white; }\n",t->id,z+1); + /** Write CSS for the table base element. **/ + const int content_width = (t->overlap_scrollbar) ? (t->w) : (t->w - 18); + htrAddStylesheetItem_va(s, + "\t#tbld%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + t->id, + ht_flex_x(t->x, tree), + ht_flex_y(t->y, tree), + ht_flex_w(content_width, tree), + ht_flex_h(t->h, tree), + z + 0 + ); + + /** Write CSS for the table scrollbar. **/ + const int row_start_y = (t->has_header) ? (t->min_rowheight + t->cellvspacing) : 0; + htrAddStylesheetItem_va(s, + "\t#tbld%POSscroll { " + "position:absolute; " + "visibility:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:18px; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + t->id, + (t->hide_scrollbar || t->demand_scrollbar) ? "hidden" : "inherit", /** TODO: Greg - This logic looks fishy. Why does `demand_scrollbar = true` hide the scrollbar?? **/ + ht_flex(t->x + t->w - 18, ht_get_parent_w(tree), ht_get_fl_x(tree) + ht_get_fl_w(tree)), + ht_flex_y(t->y + row_start_y, tree), + ht_flex_h(t->h - row_start_y, tree), + z + 0 + ); + + /** Write CSS for the table scroll thumb. **/ + htrAddStylesheetItem_va(s, + "\t#tbld%POSthumb { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:18px; " + "width:16px; " + "height:16px; " + "z-index:%POS; " + "border:solid 1px; " + "border-color:white gray gray white; " + "}\n", + t->id, + z + 1 + ); + + /** Link to the table base object. **/ + htrAddWgtrObjLinkage_va(s, tree, "tbld%POSbase", t->id); + /** Add globals for scripts. **/ htrAddScriptGlobal(s,"tbld_current","null",0); htrAddScriptGlobal(s,"tbldb_current","null",0); htrAddScriptGlobal(s,"tbldx_current","null",0); htrAddScriptGlobal(s,"tbldb_start","null",0); htrAddScriptGlobal(s,"tbldbdbl_current","null",0); + /** Include scripts. **/ htrAddScriptInclude(s, "/sys/js/htdrv_table.js", 0); htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - - htrAddWgtrObjLinkage_va(s, tree, "tbld%POSpane",t->id); - - htrAddScriptInit_va(s," tbld_init({tablename:'%STR&SYM', table:wgtrGetNodeRef(ns,\"%STR&SYM\"), scroll:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"tbld%POSscroll\"), boxname:\"tbld%POSbox\", name:\"%STR&SYM\", height:%INT, width:%INT, innerpadding:%INT, innerborder:%INT, windowsize:%INT, min_rowheight:%INT, max_rowheight:%INT, cellhspacing:%INT, cellvspacing:%INT, textcolor:\"%STR&JSSTR\", textcolorhighlight:\"%STR&JSSTR\", titlecolor:\"%STR&JSSTR\", rowbgnd1:\"%STR&JSSTR\", rowbgnd2:\"%STR&JSSTR\", rowbgndhigh:\"%STR&JSSTR\", hdrbgnd:\"%STR&JSSTR\", followcurrent:%INT, dragcols:%INT, colsep:%INT, colsep_mode:%INT, colsep_bgnd:\"%STR&JSSTR\", gridinemptyrows:%INT, reverse_order:%INT, allow_selection:%INT, show_selection:%INT, initial_selection:%INT, allow_deselection:%INT, overlap_sb:%INT, hide_sb:%INT, demand_sb:%INT, osrc:%['%STR&SYM'%]%[null%], dm:%INT, hdr:%INT, newrow_bgnd:\"%STR&JSSTR\", newrow_textcolor:\"%STR&JSSTR\", rcsize:%INT, cols:[", - t->name,t->name,t->name,t->id,t->id,t->name,t->h, - (t->overlap_scrollbar)?t->w:t->w-18, - t->inner_padding,t->inner_border,t->windowsize,t->min_rowheight, t->max_rowheight, - t->cellhspacing, t->cellvspacing,t->textcolor, - t->textcolorhighlight, t->titlecolor,t->row_bgnd1,t->row_bgnd2, - t->row_bgndhigh,t->hdr_bgnd,t->followcurrent,t->dragcols, - t->colsep, t->colsep_mode, t->colsep_bgnd,t->gridinemptyrows, t->reverse_order, - t->allow_selection, t->show_selection, t->initial_selection, t->allow_deselection, - t->overlap_scrollbar, t->hide_scrollbar, t->demand_scrollbar, - *(t->osrc) != '\0', t->osrc, *(t->osrc) == '\0', - t->data_mode, t->has_header, - t->newrow_bgnd, t->newrow_textcolor, - t->rowcache_size); - for(colid=0;colidncols;colid++) + /** Begin writing the js initialization call. **/ + htrAddScriptInit_va(s, "tbld_init({"); + + /** Write identification data. **/ + const int has_osrc = (*(t->osrc) != '\0'); + htrAddScriptInit_va(s, + "name:'%STR&SYM', " + "table:wgtrGetNodeRef(ns, '%STR&SYM'), " + "scroll:htr_subel(" + "wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), " + "'tbld%POSscroll'" + "), " + "osrc:%['%STR&SYM'%]%[null%], ", + t->name, t->name, t->name, t->id, + has_osrc, t->osrc, !has_osrc + ); + + /** Write layout data. **/ + htrAddScriptInit_va(s, + "height:%INT, " + "width:%INT, " + "innerpadding:%INT, " + "min_rowheight:%INT, " + "max_rowheight:%INT, " + "cellhspacing:%INT, " + "cellvspacing:%INT, ", + t->h, content_width, + t->inner_padding, + t->min_rowheight, t->max_rowheight, + t->cellhspacing, t->cellvspacing + ); + + /** Write selection data. **/ + htrAddScriptInit_va(s, + "allow_selection:%INT, " + "show_selection:%INT, " + "initial_selection:%INT, " + "allow_deselection:%INT, ", + t->allow_selection, + t->show_selection, + t->initial_selection, + t->allow_deselection + ); + + /** Write scrollbar data. **/ + htrAddScriptInit_va(s, + "thumb_name:'tbld%POSthumb', " + "demand_sb:%INT, ", + t->id, + t->demand_scrollbar + ); + + /** Write theme data (colors, backgrounds, etc.). **/ + htrAddScriptInit_va(s, + "textcolor:'%STR&JSSTR', " + "textcolorhighlight:'%STR&JSSTR', " + "titlecolor:'%STR&JSSTR', " + "colsep_bgnd:'%STR&JSSTR', " + "hdrbgnd:'%STR&JSSTR', " + "newrow_bgnd:'%STR&JSSTR', " + "newrow_textcolor:'%STR&JSSTR', ", + t->textcolor, t->textcolorhighlight, + t->titlecolor, t->colsep_bgnd, t->hdr_bgnd, + t->newrow_bgnd, t->newrow_textcolor + ); + + /** Write general row data. **/ + htrAddScriptInit_va(s, + "dm:%INT, " + "hdr:%INT, " + "grid_in_empty_rows:%INT, " + "windowsize:%INT, " + "rcsize:%INT, ", + t->data_mode, + t->has_header, + t->grid_in_empty_rows, + t->windowsize, + t->rowcache_size + ); + + /** Write general column data. **/ + htrAddScriptInit_va(s, + "dragcols:%INT, " + "colsep:%INT, " + "colsep_mode:%INT, ", + t->dragcols, t->colsep, t->colsep_mode + ); + + /** Write the cols array with data for each column. **/ + /** ANCHOR[id=table-column] **/ + htrAddScriptInit_va(s, "cols:["); + for (int colid = 0; colid < t->ncols; colid++) { - col = t->col_infs[colid]; - htrAddScriptInit_va(s,"{name:\"%STR&JSSTR\",ns:\"%STR&JSSTR\",fieldname:\"%STR&JSSTR\",sort_fieldname:\"%STR&JSSTR\",title:\"%STR&JSSTR\",width:%INT,type:\"%STR&JSSTR\",group:%POS,align:\"%STR&JSSTR\",wrap:\"%STR&JSSTR\",caption_fieldname:\"%STR&JSSTR\",caption_textcolor:\"%STR&JSSTR\",image_maxwidth:%POS,image_maxheight:%POS},", - col->wname, - col->wnamespace, - col->fieldname, - col->sort_fieldname, - col->title, - col->width, - col->type, - col->group[0]?1:0, - col->align, - col->wrap, - col->caption_fieldname, - col->caption_textcolor, - col->image_maxwidth, - col->image_maxheight - ); + httbl_col* col = t->col_infs[colid]; + htrAddScriptInit_va(s, + "{ " + "name:'%STR&JSSTR', " + "ns:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "sort_fieldname:'%STR&JSSTR', " + "title:'%STR&JSSTR', " + "width:%INT, " + "type:'%STR&JSSTR', " + "group:%POS, " + "align:'%STR&JSSTR', " + "wrap:'%STR&JSSTR', " + "caption_fieldname:'%STR&JSSTR', " + "caption_textcolor:'%STR&JSSTR', " + "image_maxwidth:%POS, " + "image_maxheight:%POS, " + " }, ", + col->wname, + col->wnamespace, + col->fieldname, + col->sort_fieldname, + col->title, + col->width, + col->type, + col->group[0] ? 1 : 0, + col->align, + col->wrap, + col->caption_fieldname, + col->caption_textcolor, + col->image_maxwidth, + col->image_maxheight + ); } + /** Null terminate the array, and finish writing the init call. **/ htrAddScriptInit(s,"null]});\n"); - htrAddBodyItem_va(s,"
\n",t->id); + /** Write HTML for the table base container. **/ + htrAddBodyItem_va(s,"
\n",t->id); - detailcnt = wgtrGetMatchingChildList(tree, "widget/table-row-detail", children, sizeof(children)/sizeof(pWgtrNode)); - //for (i=0;iChildren));i++) - for (i=0;iChildren), i); - // + pWgtrNode sub_tree = children[i]; + + /** Only affect table-row-detail widgets. **/ wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING,POD(&ptr)); - wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING,POD(&nptr)); - if (strcmp(ptr, "widget/table-row-detail") == 0) { htrCheckNSTransition(s, tree, sub_tree); - if (wgtrGetPropertyValue(sub_tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = t->min_rowheight; - htrAddStylesheetItem_va(s,"\t#tbld%POSsub%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; } \n", - t->id, ++subcnt, t->w-(t->demand_scrollbar?0:18), h, z+1); + /** Write CSS for the table row detail. **/ + int h; + if (wgtrGetPropertyValue(sub_tree, "height", DATA_T_INTEGER, POD(&h)) != 0) h = t->min_rowheight; + htrAddStylesheetItem_va(s, + "\t#tbld%POSsub%POS { " + "position:absolute; " + "visibility:hidden; " + "left:0px; " + "top:0px; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + t->id, ++subcnt, + ht_flex_w(t->w - (t->demand_scrollbar ? 0 : 18), tree), + ht_flex_h(h, tree), + z + 1 + ); + + /** Write HTML (including subwidgets). **/ htrAddBodyItem_va(s,"
\n", t->id, subcnt); htrRenderSubwidgets(s, sub_tree, z+2); htrAddBodyItem(s,"
\n"); + + /** Add linkage. **/ htrAddWgtrObjLinkage_va(s, sub_tree, "tbld%POSsub%POS", t->id, subcnt); - htrCheckAddExpression(s, sub_tree, nptr, "display_for"); + + /** Add 'display_for'. **/ + wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING,POD(&ptr)); + htrCheckAddExpression(s, sub_tree, ptr, "display_for"); htrCheckNSTransitionReturn(s, tree, sub_tree); } - //else if (strcmp(ptr,"widget/table-column") != 0) //got columns earlier - //{ - //htrRenderWidget(s, sub_tree, z+3); - //} } htrRenderSubwidgets(s, tree, z+2); + /** Close the base container. **/ htrAddBodyItem(s,"
\n"); - /** HTML body
element for the scrollbar layer. **/ - htrAddBodyItem_va(s,"
\n",t->id); - htrAddBodyItem(s,"\n"); - htrAddBodyItem(s,"\n"); - htrAddBodyItem_va(s,"\n", t->id, t->h-2*18-first_offset); - htrAddBodyItem(s,"\n"); - htrAddBodyItem(s,"
\n"); - /*htrAddBodyItem_va(s,"
\n",t->id);*/ - htrAddBodyItem_va(s,"
\n",t->id); - htrAddBodyItem(s,"
\n"); - + /** Write HTML for the scrollbar. **/ + htrAddBodyItem_va(s, + "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "
\n" + "
\n", + t->id, t->id, ht_flex_h(t->h - row_start_y - 2*18, tree), t->id + ); + + /** Register event handlers. **/ htrAddEventHandlerFunction(s,"document","MOUSEOVER","tbld","tbld_mouseover"); htrAddEventHandlerFunction(s,"document","MOUSEOUT","tbld","tbld_mouseout"); htrAddEventHandlerFunction(s,"document","MOUSEDOWN","tbld","tbld_mousedown"); @@ -288,14 +441,18 @@ httblRender(pHtSession s, pWgtrNode tree, int z) { pWgtrNode sub_tree; char* ptr; - char* nptr; int n, i; httbl_struct* t; int rval; pWgtrNode children[HTTBL_MAX_COLS]; httbl_col* col; - /** Don't try to render table-column, etc. We do that elsewhere **/ + /*** Only render "widget/table". Other widgets (e.g. table-column or + *** table-row-detail) are rendered elsewhere (see links below). + *** + *** LINK #table-column + *** LINK #table-row-detail + ***/ wgtrGetPropertyValue(tree,"outer_type",DATA_T_STRING,POD(&ptr)); if (strcmp(ptr, "widget/table") != 0) return 0; @@ -304,154 +461,131 @@ httblRender(pHtSession s, pWgtrNode tree, int z) if (!t) return -1; memset(t, 0, sizeof(httbl_struct)); - t->x=-1; - t->y=-1; - - /** Get an id for thit. **/ + /** Get an id. **/ t->id = (HTTBL.idcnt++); - /** Backwards compat for the time being **/ - wgtrRenameProperty(tree, "row_bgcolor1", "row1_bgcolor"); - wgtrRenameProperty(tree, "row_background1", "row1_background"); - wgtrRenameProperty(tree, "row_bgcolor2", "row2_bgcolor"); - wgtrRenameProperty(tree, "row_background2", "row2_background"); - wgtrRenameProperty(tree, "row_bgcolorhighlight", "rowhighlight_bgcolor"); - wgtrRenameProperty(tree, "row_backgroundhighlight", "rowhighlight_background"); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&(t->x))) != 0) t->x = -1; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&(t->y))) != 0) t->y = -1; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&(t->w))) != 0) t->w = -1; - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&(t->h))) != 0) + /** Get name. **/ + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) { - mssError(1,"HTTBL","'height' property is required"); + nmFree(t, sizeof(httbl_struct)); return -1; } - if (wgtrGetPropertyValue(tree,"windowsize",DATA_T_INTEGER,POD(&(t->windowsize))) != 0) t->windowsize = -1; - if (wgtrGetPropertyValue(tree,"rowheight",DATA_T_INTEGER,POD(&n)) == 0) + strtcpy(t->name, ptr, sizeof(t->name)); + + /** Get object source path. **/ + if (wgtrGetPropertyValue(tree, "objectsource", DATA_T_STRING, POD(&ptr)) != 0) + strcpy(t->osrc, ""); + else + strtcpy(t->osrc, ptr, sizeof(t->osrc)); + + /** Get the location, size, and layout data. **/ + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&(t->x))) != 0) t->x = -1; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&(t->y))) != 0) t->y = -1; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&(t->w))) != 0) t->w = -1; + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&(t->h))) != 0) { - t->min_rowheight = t->max_rowheight = n; + mssError(1,"HTTBL","'height' property is required"); + return -1; } - else + if (wgtrGetPropertyValue(tree, "rowheight", DATA_T_INTEGER, POD(&n)) != 0) { t->min_rowheight = s->ClientInfo->ParagraphHeight + 2; t->max_rowheight = -1; } - wgtrGetPropertyValue(tree,"min_rowheight",DATA_T_INTEGER,POD(&(t->min_rowheight))); - wgtrGetPropertyValue(tree,"max_rowheight",DATA_T_INTEGER,POD(&(t->max_rowheight))); - if (wgtrGetPropertyValue(tree,"cellhspacing",DATA_T_INTEGER,POD(&(t->cellhspacing))) != 0) t->cellhspacing = 1; - if (wgtrGetPropertyValue(tree,"cellvspacing",DATA_T_INTEGER,POD(&(t->cellvspacing))) != 0) t->cellvspacing = 1; - - if (wgtrGetPropertyValue(tree,"colsep",DATA_T_INTEGER,POD(&(t->colsep))) != 0) t->colsep = 1; - if (wgtrGetPropertyValue(tree,"colsep_mode",DATA_T_STRING,POD(&ptr)) == 0) + else { - if (!strcasecmp(ptr, "full")) - t->colsep_mode = 0; - else if (!strcasecmp(ptr, "header")) - t->colsep_mode = 1; + t->min_rowheight = t->max_rowheight = n; } - - if (wgtrGetPropertyValue(tree,"rowcache_size",DATA_T_INTEGER,POD(&(t->rowcache_size))) != 0) t->rowcache_size = 0; - - t->dragcols = htrGetBoolean(tree, "dragcols", 1); - t->gridinemptyrows = htrGetBoolean(tree, "gridinemptyrows", 1); + if (wgtrGetPropertyValue(tree, "inner_padding", DATA_T_INTEGER, POD(&(t->inner_padding))) != 0) t->inner_padding = 0; + if (wgtrGetPropertyValue(tree, "min_rowheight", DATA_T_INTEGER, POD(&(t->min_rowheight))) != 0); /* Keep value from above. */ + if (wgtrGetPropertyValue(tree, "max_rowheight", DATA_T_INTEGER, POD(&(t->max_rowheight))) != 0); /* Keep value from above. */ + if (wgtrGetPropertyValue(tree, "cellhspacing", DATA_T_INTEGER, POD(&(t->cellhspacing))) != 0) t->cellhspacing = 1; + if (wgtrGetPropertyValue(tree, "cellvspacing", DATA_T_INTEGER, POD(&(t->cellvspacing))) != 0) t->cellvspacing = 1; + + /** Get selection data. **/ t->allow_selection = htrGetBoolean(tree, "allow_selection", 1); t->show_selection = htrGetBoolean(tree, "show_selection", 1); - if (wgtrGetPropertyType(tree, "initial_selection") == DATA_T_STRING && wgtrGetPropertyValue(tree,"initial_selection",DATA_T_STRING,POD(&ptr)) == 0 && !strcasecmp(ptr,"noexpand")) + if (wgtrGetPropertyType(tree, "initial_selection") == DATA_T_STRING + && wgtrGetPropertyValue(tree, "initial_selection", DATA_T_STRING, POD(&ptr)) == 0 + && strcasecmp(ptr, "noexpand") == 0) t->initial_selection = 2; else t->initial_selection = htrGetBoolean(tree, "initial_selection", 1); - t->allow_deselection = htrGetBoolean(tree, "allow_deselection", t->initial_selection?0:1); - t->reverse_order = htrGetBoolean(tree, "reverse_order", 0); + t->allow_deselection = htrGetBoolean(tree, "allow_deselection", (t->initial_selection) ? 0 : 1); + /** Get scrollbar data. **/ t->overlap_scrollbar = htrGetBoolean(tree, "overlap_scrollbar", 0); t->hide_scrollbar = htrGetBoolean(tree, "hide_scrollbar", 0); t->demand_scrollbar = htrGetBoolean(tree, "demand_scrollbar", 0); - t->has_header = htrGetBoolean(tree, "titlebar", 1); - - /** Which data mode to use? **/ - if (wgtrGetPropertyValue(tree,"data_mode", DATA_T_STRING, POD(&ptr)) == 0) + + /** Get theme data (colors, backgrounds, etc.). **/ + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->textcolor, ptr, sizeof(t->textcolor)); + if (wgtrGetPropertyValue(tree, "textcolorhighlight", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->textcolorhighlight, ptr, sizeof(t->textcolorhighlight)); + if (wgtrGetPropertyValue(tree, "titlecolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->titlecolor, ptr, sizeof(t->titlecolor)); + if (!*t->titlecolor) strcpy(t->titlecolor, t->textcolor); + htrGetBackground(tree, "colsep", !s->Capabilities.Dom0NS, t->colsep_bgnd, sizeof(t->colsep_bgnd)); + htrGetBackground(tree, "hdr", !s->Capabilities.Dom0NS, t->hdr_bgnd, sizeof(t->hdr_bgnd)); + htrGetBackground(tree, "newrow", !s->Capabilities.Dom0NS, t->newrow_bgnd, sizeof(t->newrow_bgnd)); + if (wgtrGetPropertyValue(tree, "textcolornew", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->newrow_textcolor, ptr, sizeof(t->newrow_textcolor)); + + /** Get general row data. **/ + if (wgtrGetPropertyValue(tree, "data_mode", DATA_T_STRING, POD(&ptr)) != 0) + t->data_mode = 0; + else { - if (!strcmp(ptr, "rows")) - t->data_mode = 0; - else if (!strcmp(ptr, "properties")) - t->data_mode = 1; + if (strcasecmp(ptr, "rows") == 0) t->data_mode = 0; + else if (strcasecmp(ptr, "properties") == 0) t->data_mode = 1; + else + { + mssError(1, "TBL", "Invalid value for attribute 'data_mode': %s", ptr); + return -1; + } } + t->has_header = htrGetBoolean(tree, "titlebar", 1); /* Whether to render a header row. */ + t->grid_in_empty_rows = htrGetBoolean(tree, "gridinemptyrows", 1); /* Whether to show the grid in empty rows. */ + if (wgtrGetPropertyValue(tree, "windowsize", DATA_T_INTEGER, POD(&(t->windowsize))) != 0) t->windowsize = -1; + if (wgtrGetPropertyValue(tree, "rowcache_size", DATA_T_INTEGER, POD(&(t->rowcache_size))) != 0) t->rowcache_size = 0; - /** Should we follow the current record around? **/ - t->followcurrent = htrGetBoolean(tree, "followcurrent", 1); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) + /** Get general column data. **/ + t->dragcols = htrGetBoolean(tree, "dragcols", 1); + if (wgtrGetPropertyValue(tree, "colsep", DATA_T_INTEGER, POD(&(t->colsep))) != 0) t->colsep = 1; + if (wgtrGetPropertyValue(tree, "colsep_mode", DATA_T_STRING, POD(&ptr)) != 0) + t->colsep_mode = 0; + else { - nmFree(t, sizeof(httbl_struct)); - return -1; + if (strcasecmp(ptr, "full") == 0) t->colsep_mode = 0; + else if (strcasecmp(ptr, "header") == 0) t->colsep_mode = 1; + else + { + mssError(1, "TBL", "Invalid value for attribute 'colsep_mode': %s", ptr); + return -1; + } } - strtcpy(t->name,ptr,sizeof(t->name)); - - if (wgtrGetPropertyValue(tree,"objectsource",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->osrc,ptr,sizeof(t->osrc)); - else - strcpy(t->osrc,""); - - /** Get background color/image for table header **/ - htrGetBackground(tree, NULL, !s->Capabilities.Dom0NS, t->tbl_bgnd, sizeof(t->tbl_bgnd)); - - /** Get background color/image for header row **/ - htrGetBackground(tree, "hdr", !s->Capabilities.Dom0NS, t->hdr_bgnd, sizeof(t->hdr_bgnd)); - - /** Get background color/image for rows **/ - htrGetBackground(tree, "row1", !s->Capabilities.Dom0NS, t->row_bgnd1, sizeof(t->row_bgnd1)); - htrGetBackground(tree, "row2", !s->Capabilities.Dom0NS, t->row_bgnd2, sizeof(t->row_bgnd2)); - htrGetBackground(tree, "rowhighlight", !s->Capabilities.Dom0NS, t->row_bgndhigh, sizeof(t->row_bgndhigh)); - htrGetBackground(tree, "colsep", !s->Capabilities.Dom0NS, t->colsep_bgnd, sizeof(t->colsep_bgnd)); - htrGetBackground(tree, "newrow", !s->Capabilities.Dom0NS, t->newrow_bgnd, sizeof(t->newrow_bgnd)); - - /** Get borders and padding information **/ - wgtrGetPropertyValue(tree,"outer_border",DATA_T_INTEGER,POD(&(t->outer_border))); - wgtrGetPropertyValue(tree,"inner_border",DATA_T_INTEGER,POD(&(t->inner_border))); - wgtrGetPropertyValue(tree,"inner_padding",DATA_T_INTEGER,POD(&(t->inner_padding))); - - /** Row decorations **/ - wgtrGetPropertyValue(tree, "row_border_color", DATA_T_STRING, POD(&t->row_border)); - wgtrGetPropertyValue(tree, "row_shadow_color", DATA_T_STRING, POD(&t->row_shadow_color)); - wgtrGetPropertyValue(tree, "row_shadow_offset", DATA_T_INTEGER, POD(&t->row_shadow)); - wgtrGetPropertyValue(tree, "row_shadow_radius", DATA_T_INTEGER, POD(&t->row_shadow_radius)); - wgtrGetPropertyValue(tree, "row_border_radius", DATA_T_INTEGER, POD(&t->row_radius)); - - /** Text color information **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->textcolor,ptr,sizeof(t->textcolor)); - - /** Text color information **/ - if (wgtrGetPropertyValue(tree,"textcolorhighlight",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->textcolorhighlight,ptr,sizeof(t->textcolorhighlight)); - /** Text color information for "new row" in process of being created **/ - if (wgtrGetPropertyValue(tree,"textcolornew",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->newrow_textcolor,ptr,sizeof(t->newrow_textcolor)); - - /** Title text color information **/ - if (wgtrGetPropertyValue(tree,"titlecolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->titlecolor,ptr,sizeof(t->titlecolor)); - if (!*t->titlecolor) strcpy(t->titlecolor,t->textcolor); - - /** Get column data **/ + /** Get specific column data for each column. **/ t->ncols = wgtrGetMatchingChildList(tree, "widget/table-column", children, sizeof(children)/sizeof(pWgtrNode)); for (i=0;incols;i++) { sub_tree = children[i]; + + /** Only read table-column widgets. **/ wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING,POD(&ptr)); - wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING,POD(&nptr)); - if (!strcmp(ptr,"widget/table-column") != 0) + if (strcmp(ptr, "widget/table-column") == 0) { + /** Allocate space to store the column data. **/ col = (httbl_col*)nmMalloc(sizeof(httbl_col)); memset(col, 0, sizeof(*col)); + + /** Basic column info. **/ t->col_infs[i] = col; strtcpy(col->wname, wgtrGetName(sub_tree), sizeof(col->wname)); strtcpy(col->wnamespace, wgtrGetNamespace(sub_tree), sizeof(col->wnamespace)); - /** no layer associated with this guy **/ + /** The object system doesn't need to render this widget (we will do that). **/ sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; /** Get column properties **/ @@ -470,8 +604,9 @@ httblRender(pHtSession s, pWgtrNode tree, int z) strtcpy(col->title, ptr, sizeof(col->title)); else strtcpy(col->title, col->fieldname, sizeof(col->title)); - htrCheckAddExpression(s, sub_tree, nptr, "title"); - htrCheckAddExpression(s, sub_tree, nptr, "visible"); + wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING, POD(&ptr)); + htrCheckAddExpression(s, sub_tree, ptr, "title"); + htrCheckAddExpression(s, sub_tree, ptr, "visible"); if (wgtrGetPropertyValue(sub_tree, "align", DATA_T_STRING,POD(&ptr)) == 0) strtcpy(col->align, ptr, sizeof(col->align)); else @@ -489,7 +624,10 @@ httblRender(pHtSession s, pWgtrNode tree, int z) } } + /** Render the table. **/ rval = httblRenderDynamic(s, tree, z, t); + + /** Clean up. **/ for(i=0;incols;i++) nmFree(t->col_infs[i], sizeof(httbl_col)); nmFree(t, sizeof(httbl_struct)); @@ -505,25 +643,26 @@ httblInitialize() { pHtDriver drv; - /** Allocate the driver **/ + /** Allocate the driver struct. **/ drv = htrAllocDriver(); if (!drv) return -1; - /** Fill in the structure. **/ + /** Initialize driver values. **/ strcpy(drv->Name,"DHTML DataTable Driver"); strcpy(drv->WidgetName,"table"); drv->Render = httblRender; xaAddItem(&(drv->PseudoTypes), "table-column"); xaAddItem(&(drv->PseudoTypes), "table-row-detail"); + /** Add driver events. **/ htrAddEvent(drv,"Click"); htrAddEvent(drv,"DblClick"); - /** Register. **/ + /** Register the driver, with dhtml support. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); + /** Initialize the ID counter. **/ HTTBL.idcnt = 0; return 0; From 2354c9a91f0343518f685a11a42ef9addf285ed8 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Mon, 9 Feb 2026 12:58:50 -0700 Subject: [PATCH 054/107] Make table widget front end code responsive (I think). Add a resize observer to reflow widths and update the rows, scroll thumb, and no data message when the page was resized. Add code to allow changing column width using any row (not just the header row). Rewrite tbld_change_width() to be better in almost every way (might fix some bugs). Rewrite tbl_reflow_width() to be far easier to read (might fix some bugs). Rewrite tbl_apply_row_geom() to be faster and better in general (might fix some bugs). Refactor no data message handling to reduce duplicate code. Rename box to scroll thumb for clarity and consistency. Remove code that stored attributes in the table even though they were never used. Improve code readability by using Math.clamp(). Improve code readability by using some JS best practices. Improve code readability by using the ?. syntax. Reorder some initialization code for more readable grouping. Revise comments to make them more useful. Add a small amount of doc comments. Add some useful comments. Clean up unused comments. Clean up unused logging. Clean up unused comments about unused logging. --- centrallix-os/sys/js/htdrv_table.js | 466 +++++++++++++++------------- 1 file changed, 251 insertions(+), 215 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_table.js b/centrallix-os/sys/js/htdrv_table.js index 19c5c7d5e..328870cf4 100644 --- a/centrallix-os/sys/js/htdrv_table.js +++ b/centrallix-os/sys/js/htdrv_table.js @@ -12,6 +12,31 @@ window.tbld_touches = []; +const tbld_resize_observer = new ResizeObserver((entries) => entries.forEach(({ + contentRect: { width, height }, + target: table +}) => + { + // Set the new size. + table.param_width = width; + table.param_height = height; + + // Update scroll thumb and reflow the columns. + table.UpdateThumb(false); + table.ReflowWidth(width); + table.UpdateNDM($(table).children('#ndm')); + + // Resize all rows. + const { rows } = table; + for (let i = (table.has_header) ? 0 : rows.first; i <= rows.last; i++) + { + const row = rows[i]; + if (!row) continue; + $(row).css({ width: (row.w = width) }); + } + } +)); + function tbld_log_status() { var rowstr = ''; @@ -60,27 +85,33 @@ function tbld_format_cell(cell, color) var bartext = wgtrGetServerProperty(wgtrFindDescendent(this, this.cols[cell.colnum].name, this.cols[cell.colnum].ns), 'bar_textcolor'); if (!bartext) bartext = 'black'; bartext = String(bartext).replace(/[^a-z0-9A-Z#]/g, ""); - var actpct = '' + (100 * ((val < 0)?0:((val > 1)?1:val))) + '%'; - actpct = String(actpct).replace(/[^0-9.%]/g, ""); - var pct = '' + (Math.round(val * 100 / roundto) * roundto) + '%'; + const width_percent = ('' + (100 * Math.clamp(0, val, 1)) + '%').replace(/[^0-9.%]/g, ""); + const percent = '' + (Math.round(val * 100 / roundto) * roundto) + '%'; if (val >= 0.5) { - innertxt = pct + ' '; + innertxt = percent + ' '; outertxt = ''; } else { innertxt = ' '; - outertxt = ' ' + pct; + outertxt = ' ' + percent; } - txt = '
' + - '
' + - htutil_encode(innertxt) + - '
' + - (outertxt?('' + - htutil_encode(outertxt) + - ''):'') + - '
'; + txt = + '
' + + '
' + + htutil_encode(innertxt) + + '
' + + ((outertxt) ? ('= min) - { - ndm.hide(); - } - else - { - ndm.show(); - ndm.text(wgtrGetServerProperty(this,"nodata_message")); - ndm.css({"top":((this.param_height - ndm.height())/2) + "px", "color":wgtrGetServerProperty(this,"nodata_message_textcolor") }); - } + // Redraw the no data message. + var $ndm = $(this).children('#ndm'); + if (max >= min) $ndm.hide(); + else this.UpdateNDM($ndm); // (re)draw the loaded records var selected_position_changed = false; @@ -566,19 +588,12 @@ function tbld_get_selected_geom() return { x:$(obj).offset().left, y:$(obj).offset().top, width:$(obj).width(), height:$(obj).height() }; } - -function tbld_css_height(element, seth) - { - if (seth == null) - { - return parseFloat($(element).css("height")); - } - else - { - $(element).css("height",seth + "px"); - } - } - +function tbld_get_height(node) { + return parseFloat($(node).css("height")); +} +function tbld_set_height(node, new_height) { + $(node).css("height", new_height + "px"); +} function tbld_update_height(row) { @@ -597,8 +612,8 @@ function tbld_update_height(row) h = this.max_rowheight - this.cellvspacing*2; if (h < this.min_rowheight - this.cellvspacing*2) h = this.min_rowheight - this.cellvspacing*2; - if (tbld_css_height(col) != h) - tbld_css_height(col,h); + if (tbld_get_height(col) != h) + tbld_set_height(col, h); if (h > maxheight) maxheight = h; } @@ -617,10 +632,11 @@ function tbld_update_height(row) } // No change? - if (tbld_css_height(row) == maxheight + this.innerpadding*2) + const new_height = maxheight + this.innerpadding*2; + if (tbld_get_height(row) === new_height) return false; - tbld_css_height(row,maxheight + this.innerpadding*2); + tbld_set_height(row, new_height); return true; } @@ -686,7 +702,6 @@ function tbld_format_row(id, selected, do_new) function tbld_bring_into_view(rownum) { - //this.log.push("tbld_bring_into_view(" + rownum + ")"); this.bring_into_view = null; // Clamp the requested row to the available range @@ -939,10 +954,10 @@ function tbld_detail_showcontainer() } } - +/** @param dw The detail widget DOM node. **/ function tbld_update_detail(dw) { - if (dw.display_for && (this.table.initselect !== 2 || (this.table.initselect == 2 && dw.on_new)) /* 2 = noexpand */ && (!dw.on_new || wgtrGetServerProperty(dw, 'show_on_new', 0))) + if (dw.display_for && ((dw.on_new) ? (this.table.initselect !== 2) : wgtrGetServerProperty(dw, 'show_on_new', 0))) { var found=false; for(var j=0; jl.row.w) - // move = l.row.w - rw - colinfo.xoffset - colinfo.width; - if(colinfo.xoffset+colinfo.width+rw+move<0) - move=0-colinfo.xoffset-rw; - if (colinfo.width + move < 3) - move = 3-colinfo.width; - if(l.resizebdr.xoffset+move<0) - move=0-l.resizebdr.xoffset; - //if(getPageX(l.resizebdr) + t.colsep + t.bdr_width*2 + move >= getPageX(t) + t.param_width) - // move = getPageX(t) + t.param_width - getPageX(l.resizebdr) - t.colsep - t.bdr_width*2; + if (col_info_xoffset + col_info_width + rw + move < 0) + move = 0 - col_info_xoffset - rw; + if (col_info_width + move < 3) + move = 3 - col_info_width; + if (resizebdr && resizebdr.xoffset + move < 0) + move = 0 - resizebdr.xoffset; // Figure how much space on the right of this resize handle we're adjusting, too... - var cols_right = t.colcount - l.colnum - 1; - var adj = []; - var total_right_width = 0; - for(var j=l.colnum+1; j= t.rows.first) - { - if (t.ApplyRowGeom(t.rows[i], l.colnum) && t.min_rowheight != t.max_rowheight) + const updated_rows = []; + const { first, last } = rows; + const inflexible_row_height = (t.min_rowheight === t.max_rowheight); + for (let i = 0; i <= last; i++) + { + if (i < first && i !== 0) continue; + + const rowi = rows[i]; + if (!t.ApplyRowGeom(rowi, colnum) || inflexible_row_height) continue; + + // Need to update height of row? + if (!t.UpdateHeight(rowi)) continue; + + for (let j = i + 1; j <= last; j++) + { + const rowj = rows[j]; + if (rowj.positioned) { - // Need to update height of row? - if (t.UpdateHeight(t.rows[i])) - { - for(var j=i+1; j<=t.rows.last; j++) - { - if (t.rows[j].positioned) - { - t.rows[j].positioned = false; - upd_rows.push(t.rows[j]); - } - } - } + rowj.positioned = false; + updated_rows.push(rowj); } } } - if (upd_rows.length) + + if (updated_rows.length) { - t.PositionRows(upd_rows); + t.PositionRows(updated_rows); t.CheckBottom(); } @@ -1397,40 +1410,34 @@ function tbld_change_width(move, compensate) } -function tbld_reflow_width() +function tbld_reflow_width(new_width) { - if (this.hdrrow) - { - var logstr = 'Before reflow widths:'; - var ttl = 0; - for(var i=0; i 0 || this.dragcols) - new_w -= (this.bdr_width*2 + this.colsep); - $(c).width(new_w); - setRelativeX(c, this.cols[j].xoffset); - if (this.cols[j].wrap != 'no') - change_wrapped_cell = true; + const target_col = target_cols[i], this_col = this_cols[i]; + let new_w = this_col.width; // - this.innerpadding*2; + + if (colsep > 0 || dragcols) new_w -= (bdr_width*2 + colsep); + + $(target_col).width(new_w); + fast_setRelativeX(target_col, this_col.xoffset); + + change_wrapped_cell |= (this_col.wrap != 'no'); } return change_wrapped_cell; } @@ -1604,7 +1615,6 @@ function tbld_remove_row(rowobj) } if (this.rows.firstvis > this.rows.lastvis) { - console.log('TABLE ' + this.__WgtrName + ': resetting firstvis/lastvis to null (firstvis > lastvis)'); this.rows.firstvis = null; this.rows.lastvis = null; } @@ -1687,12 +1697,14 @@ function tbld_display_row(rowobj, rowslot) if (!this.rows.lastvis || this.rows.lastvis < rowslot) this.rows.lastvis = rowslot; } - if (getRelativeY(rowobj) < this.scroll_minheight || this.scroll_minheight == null) - this.scroll_minheight = getRelativeY(rowobj); + const rowY = getRelativeY(rowobj); + if (rowY < this.scroll_minheight || this.scroll_minheight == null) + this.scroll_minheight = rowY; if (rowslot < this.scroll_minrec || this.scroll_minrec == null) this.scroll_minrec = rowslot; - if (rowslot == this.rows.lastosrc || (getRelativeY(rowobj) + $(rowobj).height() + this.cellvspacing*2 > this.scroll_maxheight)) - this.scroll_maxheight = getRelativeY(rowobj) + $(rowobj).height() + this.cellvspacing*2; + const rowHeight = $(rowobj).height(); + if (rowslot == this.rows.lastosrc || (rowY + rowHeight + this.cellvspacing*2 > this.scroll_maxheight)) + this.scroll_maxheight = rowY + rowHeight + this.cellvspacing*2; if (rowslot > this.scroll_maxrec) this.scroll_maxrec = rowslot; } @@ -1843,16 +1855,12 @@ function tbld_osrc_dispatch() case 'ScrollTo': this.osrc_busy = true; this.osrc_last_op = item.type; - //this.log.push("Calling ScrollTo(" + item.start + "," + item.end + ") on osrc, stat=" + (this.osrc.pending?'pending':'not-pending')); - //console.log("Calling ScrollTo(" + item.start + "," + item.end + ") on osrc, stat=" + (this.osrc.pending?'pending':'not-pending')); this.osrc.ScrollTo(item.start, item.end); break; case 'MoveToRecord': this.osrc_busy = true; this.osrc_last_op = item.type; - //this.log.push("Calling MoveToRecord(" + item.rownum + ") on osrc, stat=" + (this.osrc.pending?'pending':'not-pending')); - //console.log("Calling MoveToRecord(" + item.rownum + ") on osrc, stat=" + (this.osrc.pending?'pending':'not-pending')); this.osrc.MoveToRecord(item.rownum, this); break; @@ -1934,8 +1942,11 @@ function tbld_init(param) { var t = param.table; var scroll = param.scroll; + + // Initialize table. ifc_init_widget(t); t.table = t; + t.name = param.name; // Debug value. t.param_width = param.width; t.param_height = param.height; t.dragcols = param.dragcols; @@ -1955,12 +1966,15 @@ function tbld_init(param) t.cr = 0; t.is_new = 0; t.rowdivcache = []; - t.followcurrent = param.followcurrent>0?true:false; t.hdr_bgnd = param.hdrbgnd; htr_init_layer(t, t, "tabledynamic"); + + // Initialize scrollbar. t.scrollbar = scroll; htr_init_layer(t.scrollbar, t, "tabledynamic"); t.scrollbar.Click = tbld_bar_click; + + // Initialize scrollbar images. var imgs = pg_images(t.scrollbar); for(var img in imgs) { @@ -1971,29 +1985,23 @@ function tbld_init(param) else if (imgs[img].name == 'd') t.down = imgs[img]; } - t.box=htr_subel(scroll,param.boxname); - htr_init_layer(t.box, t, "tabledynamic"); - t.scrollbar.b=t.box; + + // Initialize scroll thumb. + t.thumb = htr_subel(scroll, param.thumb_name); + htr_init_layer(t.thumb, t, "tabledynamic"); + t.scrollbar.b = t.thumb; + + // Initialize events for scrolling. t.up.Click=tbld_up_click; t.down.Click=tbld_down_click; - t.box.Click = new Function( ); - t.scrollbar.table = t.up.table = t.down.table = t.box.table = t; + t.thumb.Click = new Function(); + t.scrollbar.table = t.up.table = t.down.table = t.thumb.table = t; t.up.subkind='up'; t.down.subkind='down'; - t.box.subkind='box'; + t.thumb.subkind='thumb'; t.scrollbar.subkind='bar'; - /*t.dispatch_queue = {}; - t.dispatch_parallel_max = 1; - t.Dispatch = tbld_dispatch; - t.Request = tbld_request;*/ - t.osrc_request_queue = []; - t.osrc_busy = false; - t.osrc_last_op = null; - //t.log = []; - t.ttf_string = ''; - t.selected_row = null; - t.selected = null; + // Initialize layout data. t.rowheight=param.min_rowheight>0?param.min_rowheight:15; t.min_rowheight = param.min_rowheight; t.max_rowheight = param.max_rowheight; @@ -2004,9 +2012,6 @@ function tbld_init(param) t.textcolorhighlight=param.textcolorhighlight?param.textcolorhighlight:param.textcolor; t.textcolornew=param.newrow_textcolor; t.titlecolor=param.titlecolor; - t.row_bgnd1=param.rowbgnd1?param.rowbgnd1:"bgcolor='white'"; - t.row_bgnd2=param.rowbgnd2?param.rowbgnd2:t.row_bgnd1; - t.row_bgndhigh=param.rowbgndhigh?param.rowbgndhigh:"bgcolor='black'"; t.row_bgndnew=param.newrow_bgnd; t.cols=param.cols; t.colcount=0; @@ -2017,17 +2022,29 @@ function tbld_init(param) else delete t.cols[i]; } - if (param.osrc) - t.osrc = wgtrGetNode(t, param.osrc, "widget/osrc"); - else - t.osrc = wgtrFindContainer(t, "widget/osrc"); - if(!t.osrc || !(t.colcount>0)) + if (t.colcount <= 0) { - alert('table widget requires an objectsource and at least one column'); + alert('The table widget requires at least one column'); return t; } - - // Main table widget methods + + // Initialize ObjectSource values. + t.osrc_request_queue = []; + t.osrc_busy = false; + t.osrc_last_op = null; + t.ttf_string = ''; + t.selected_row = null; + t.selected = null; + t.osrc = (param.osrc) + ? wgtrGetNode(t, param.osrc, "widget/osrc") + : wgtrFindContainer(t, "widget/osrc"); + if (!t.osrc) + { + alert('The table widget requires an ObjectSource'); + return t; + } + + // Bind table widget functions. t.RedrawAll = tbld_redraw_all; t.InstantiateRow = tbld_instantiate_row; t.DisplayRow = tbld_display_row; @@ -2050,7 +2067,7 @@ function tbld_init(param) t.SchedScroll = tbld_sched_scroll; t.CheckBottom = tbld_check_bottom; t.ApplyRowGeom = tbld_apply_row_geom; - t.InitBH = tbld_init_bh; + t.UpdateNDM = tbld_update_ndm; t.OsrcDispatch = tbld_osrc_dispatch; t.OsrcRequest = tbld_osrc_request; t.EndTTF = tbld_end_ttf; @@ -2071,6 +2088,7 @@ function tbld_init(param) t.ObjectModified = tbld_object_modified; t.osrc.Register(t); + // Set the number or records visible in the table at one time. if (param.windowsize > 0) { t.windowsize = param.windowsize; @@ -2090,12 +2108,15 @@ function tbld_init(param) if (t.datamode != 1 && t.windowsize > t.osrc.replicasize) t.windowsize = t.osrc.replicasize; + // Handle header row. t.totalwindowsize = t.windowsize + 1; if (!t.has_header) t.windowsize = t.totalwindowsize; t.firstdatarow = t.has_header?1:0; - // Handle column resizing and columns without widths + /*** Handle columns without widths by assigning a default and resizing other + *** columns proportionally. + ***/ var total_w = 0; for (var i in t.cols) { @@ -2125,11 +2146,10 @@ function tbld_init(param) t.grpby = i; } + // Set some other values. t.maxwindowsize = t.windowsize; t.maxtotalwindowsize = t.totalwindowsize; t.rows = {first:null, last:null, firstvis:null, lastvis:null, lastosrc:null}; - setClipWidth(t, param.width); - setClipHeight(t, param.height); t.subkind='table'; t.bdr_width = (t.colsep > 0)?3:0; t.target_y = null; @@ -2208,6 +2228,7 @@ function tbld_init(param) } } + // Initialize scrollbar values. t.scroll_maxheight = null; t.scroll_maxrec = null; t.scroll_minheight = null; @@ -2220,6 +2241,7 @@ function tbld_init(param) // set working area height and scrollbar size t.UpdateGeom(); + // Initialize the scrollbar. t.scrolldiv = htr_new_layer(t.param_width, t.scrollctr); htr_init_layer(t.scrolldiv, t, "tabledynamic"); t.scrolldiv.subkind = "scrolldiv"; @@ -2238,11 +2260,23 @@ function tbld_init(param) if (window.tbld_mcurrent == undefined) window.tbld_mcurrent = null; + // Handle resizing. + tbld_resize_observer.observe(t); + // No data message - var ndm = document.createElement("div"); - $(ndm).css({"position":"absolute", "width":"100%", "text-align":"center", "left":"0px"}); - $(ndm).attr({"id":"ndm"}); - $(t).append(ndm); + var $ndm = $('
'); + $ndm[0].table = t; + $ndm.text(wgtrGetServerProperty(t, "nodata_message")); + $ndm.css({ + position: 'absolute', + width: '100%', + left: '0px', + textAlign: 'center', + color: wgtrGetServerProperty(t, "nodata_message_textcolor"), + }); + $ndm.show(); + $(t).append($ndm); + t.UpdateNDM($ndm); // Events var ie = t.ifcProbeAdd(ifEvent); @@ -2293,17 +2327,14 @@ function tbld_init(param) } } - t.InitBH(); - return t; } -function tbld_init_bh() +function tbld_update_ndm($ndm) { - var ndm = $(this).children('#ndm'); - ndm.show(); - ndm.text(wgtrGetServerProperty(this,"nodata_message")); - ndm.css({"top":((this.param_height - ndm.height())/2) + "px", "color":wgtrGetServerProperty(this,"nodata_message_textcolor") }); + $ndm.css({ + top: ((this.param_height - $ndm.height()) / 2) + "px", + }); } function tbld_touchstart(e) @@ -2584,7 +2615,9 @@ function tbld_keydown(e) for(var c in row.cols) { var col = row.cols[c]; - if (t.cols[col.colnum].type != 'check' && t.cols[col.colnum].type != 'image' && t.cols[col.colnum].type != 'checkbox') + if (t.cols[col.colnum].type != 'check' && + t.cols[col.colnum].type != 'image' && + t.cols[col.colnum].type != 'checkbox') { if (t.CheckHighlight(col, t.ttf_string)) { @@ -2675,7 +2708,7 @@ function tbld_mousedown(e) ly=ly.cell.row; } } - if (ly.subkind == 'box') + if (ly.subkind == 'thumb') { tbldx_current = ly; tbldx_start = e.pageY; @@ -2828,7 +2861,10 @@ function tbld_mousedown(e) } ly.row.table.osrc.ifcProbe(ifAction).Invoke("OrderObject", {orderobj:neworder}); } - if(ly.subkind=='up' || ly.subkind=='bar' || ly.subkind=='down' || ly.subkind=='box') + if (ly.subkind === 'up' + || ly.subkind === 'bar' + || ly.subkind === 'down' + || ly.subkind === 'thumb') { ly.Click(e); } @@ -2868,9 +2904,9 @@ function tbld_mousemove(e) var t = tbldx_current.table; if (tbldx_tstart + incr < 18) incr = 18 - tbldx_tstart; - if (tbldx_tstart + incr + $(t.box).height() > $(t.scrollbar).height() - 18 - 3) - incr = $(t.scrollbar).height() - 18 - 3 - tbldx_tstart - $(t.box).height(); - setRelativeY(t.box, tbldx_tstart + incr); + if (tbldx_tstart + incr + $(t.thumb).height() > $(t.scrollbar).height() - 18 - 3) + incr = $(t.scrollbar).height() - 18 - 3 - tbldx_tstart - $(t.thumb).height(); + setRelativeY(t.thumb, tbldx_tstart + incr); if (t.thumb_avail > t.thumb_height) { t.SchedScroll((-t.scroll_minheight) - Math.floor((tbldx_tstart + incr - 18)*t.thumb_sh/(t.thumb_avail - t.thumb_height))); @@ -2915,7 +2951,7 @@ function tbld_mouseup(e) if (t.colsep > 0 || t.dragcols) maxw += (t.bdr_width*2 + t.colsep); l.ChangeWidth(maxw-t.cols[l.colnum].width, true); - t.ReflowWidth(); + t.ReflowWidth(t.hdrrow.w); } else { From 467ac3c55ecae7ba19aae2c0a61c57a9b8f2cb48 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 11 Feb 2026 12:13:08 -0700 Subject: [PATCH 055/107] Improve button layout on radio button panel widgets. Add a spacing attribute to set the space between buttons (with documentation). Reorganize C generation code, greatly reducing unnecessary work and improving code readability and usability. Fix styling failures in htdrv_radiobutton.c. Add comments & clean up. --- centrallix-doc/Widgets/widgets.xml | 4 +- centrallix/htmlgen/htdrv_radiobutton.c | 583 ++++++++++++------------- 2 files changed, 294 insertions(+), 293 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index 1a0104512..6267ffd08 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -3091,7 +3091,9 @@ my_cmp "widget/component-decl" A color, RGB or named, for the panel background. If neither bgcolor nor background transparent. Height, in pixels, of the panel. - + + The maximum height (in pixels) of space allowed between radio buttons on the panel (default: 10px). + An image to be used for the rectangular border drawn around the radio buttons. The color, RGB or named, of the text within the panel. Default: "black". diff --git a/centrallix/htmlgen/htdrv_radiobutton.c b/centrallix/htmlgen/htdrv_radiobutton.c index d5258ed5e..5946e048c 100644 --- a/centrallix/htmlgen/htdrv_radiobutton.c +++ b/centrallix/htmlgen/htdrv_radiobutton.c @@ -44,99 +44,144 @@ /** globals **/ -static struct { - int idcnt; -} HTRB; +static struct + { + int idcnt; + } + HTRB; /** htrbRender - generate the HTML code for the page. **/ -int htrbRender(pHtSession s, pWgtrNode tree, int z) { - char* ptr; - char name[64]; - char title[64]; - char sbuf2[200]; - //char bigbuf[4096]; - char textcolor[32]; - char main_background[128]; - char outline_background[128]; - char form[64]; - pWgtrNode radiobutton_obj, sub_tree; - int x=-1,y=-1,w,h; - int id, i, j; - int is_selected; - int raido_button_count; - char fieldname[32]; - char value[64]; - char label[64]; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) - { - mssError(1,"HTRB","Netscape 4.x or W3C DOM support required"); - return -1; - } - - /** Get an id for this. **/ - id = (HTRB.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { - mssError(1,"HTRB","RadioButtonPanel widget must have a 'width' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { - mssError(1,"HTRB","RadioButtonPanel widget must have a 'height' property"); - return -1; - } - - /** Background color/image? **/ - htrGetBackground(tree,NULL,!s->Capabilities.Dom0NS,main_background,sizeof(main_background)); - - /** Text color? **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(textcolor,ptr,sizeof(textcolor)); - else - strcpy(textcolor,"black"); - - /** Outline color? **/ - htrGetBackground(tree,"outline",!s->Capabilities.Dom0NS,outline_background,sizeof(outline_background)); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Get title **/ - if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(title,ptr,sizeof(title)); - - /** User requesting expression for selected tab? **/ - htrCheckAddExpression(s, tree, name, "value"); - - /** User requesting expression for selected tab using integer index value? **/ - htrCheckAddExpression(s, tree, name, "value_index"); - - /** Get fieldname **/ - if (wgtrGetPropertyValue(tree,"fieldname",DATA_T_STRING,POD(&ptr)) == 0) - { - strtcpy(fieldname,ptr,sizeof(fieldname)); - } - else - { - fieldname[0]='\0'; - } - - if (wgtrGetPropertyValue(tree,"form",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(form,ptr,sizeof(form)); - else - form[0]='\0'; - - htrAddScriptInclude(s, "/sys/js/htdrv_radiobutton.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - +int htrbRender(pHtSession s, pWgtrNode tree, int z) + { + char* ptr; + + /** Verify required capabilities. **/ + if (!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) + { + mssError(1, "HTRB", "Netscape 4.x or W3C DOM support required"); + return -1; + } + + /** Get an id for this widget. **/ + const int id = (HTRB.idcnt++); + + /** Get x,y,w,h of this object. **/ + int x, y, w, h, spacing; + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) x = 0; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) y = 0; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) + { + mssError(1,"HTRB","RadioButtonPanel widget must have a 'width' property"); + return -1; + } + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) + { + mssError(1,"HTRB","RadioButtonPanel widget must have a 'height' property"); + return -1; + } + if (wgtrGetPropertyValue(tree, "spacing", DATA_T_INTEGER, POD(&spacing)) != 0) spacing = 10; + + /** Get the name and title attributes. **/ + char name[64] = "", title[64] = ""; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTRB", "RadioButtonPanel widget must have a 'name' property"); + return -1; + } + strtcpy(name, ptr, sizeof(name)); + if (wgtrGetPropertyValue(tree, "title", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTRB", "RadioButtonPanel widget must have a 'title' property"); + return -1; + } + strtcpy(title, ptr, sizeof(title)); + + /** Get text color attribute. **/ + char textcolor[32]; + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(textcolor, ptr, sizeof(textcolor)); + else + strcpy(textcolor, "black"); + + /** Get background attributes. **/ + char main_background[128] = ""; + char outline_background[128] = ""; + htrGetBackground(tree, NULL, !s->Capabilities.Dom0NS, main_background, sizeof(main_background)); + htrGetBackground(tree, "outline", !s->Capabilities.Dom0NS, outline_background, sizeof(outline_background)); + + /** User requesting expression for selected tab? **/ + htrCheckAddExpression(s, tree, name, "value"); + + /** User requesting expression for selected tab using integer index value? **/ + htrCheckAddExpression(s, tree, name, "value_index"); + + /** Get fieldname and form attributes. **/ + char fieldname[32] = "", form[64] = ""; + if (wgtrGetPropertyValue(tree, "fieldname", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(fieldname, ptr, sizeof(fieldname)); + if (wgtrGetPropertyValue(tree, "form", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(form, ptr, sizeof(form)); + + + /** Include scripts. **/ + htrAddScriptInclude(s, "/sys/js/htdrv_radiobutton.js", 0); + htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); + + /** Link DOM node to widget data. **/ + htrAddWgtrObjLinkage_va(s, tree, "rb%POSparent", id); + htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(htr_subel(_obj, 'rb%POSborder'), 'rb%POScover')", id, id); + + /** Script initialization call. **/ + if (strlen(main_background) > 0) + { + htrAddScriptInit_va(s, "{ " + "const parentPane = wgtrGetNodeRef(ns, '%STR&SYM'); " + "const borderPane = htr_subel(parentPane, 'rb%POSborder'); " + "const coverPane = htr_subel(borderPane, 'rb%POScover'); " + "const titlePane = htr_subel(parentPane, 'rb%POStitle'); " + "radiobuttonpanel_init({ " + "parentPane, borderPane, coverPane, titlePane, " + "fieldname:'%STR&JSSTR', " + "mainBackground:'%STR&JSSTR', " + "outlineBackground:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "}); }\n", + name, id, id, id, + fieldname, + main_background, + outline_background, + form + ); + } + else + { + htrAddScriptInit_va(s, + "radiobuttonpanel_init({ " + "parentPane:wgtrGetNodeRef(ns, '%STR&SYM'), " + "fieldname:'%STR&JSSTR', " + "borderPane:0, " + "coverPane:0, " + "titlePane:0, " + "mainBackground:0, " + "outlineBackground:0, " + "form:'%STR&JSSTR', " + "});\n", + name, + fieldname, + form + ); + } + + /** Add event listenners. **/ + htrAddEventHandlerFunction(s, "document", "MOUSEUP", "radiobutton", "radiobutton_mouseup"); + htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "radiobutton", "radiobutton_mousedown"); + htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "radiobutton", "radiobutton_mouseover"); + htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "radiobutton", "radiobutton_mousemove"); + + /** Write style headers for container DOM nodes. **/ const int para_height = s->ClientInfo->ParagraphHeight; - fprintf(stderr, "h: %d\n", para_height); const int top_offset = (para_height * 3) / 4 + 1; htrAddStylesheetItem_va(s, "#rb%POSparent { " @@ -201,216 +246,173 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) { para_height, z + 3 ); - - htrAddScriptGlobal(s, "radiobutton", "null", 0); - - /** DOM linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "rb%POSparent",id); - htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(htr_subel(_obj,\"rb%POSborder\"),\"rb%POScover\")",id,id); - - /** Loop through each radiobutton and flag it NOOBJECT **/ - raido_button_count = 0; - for (j=0;jChildren));j++) - { - radiobutton_obj = xaGetItem(&(tree->Children), j); - radiobutton_obj->RenderFlags |= HT_WGTF_NOOBJECT; - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (strcmp(ptr,"widget/radiobutton") == 0) raido_button_count++; - } - /** Compute values for laying out radio buttons. **/ - const int cover_height = h - top_offset - 6; - int item_spacing = para_height + 12; - int cover_margin = 10; - if (item_spacing * raido_button_count + 2*cover_margin > cover_height) - item_spacing = (cover_height-2*cover_margin)/raido_button_count; - if (item_spacing * raido_button_count + 2*cover_margin > cover_height) - cover_margin = (cover_height-(item_spacing*raido_button_count))/2; - if (cover_margin < 2) cover_margin = 2; - i = 1; - for (j=0;jChildren));j++) - { - radiobutton_obj = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - htrAddStylesheetItem_va(s, - "#rb%POSoption%POS { " - "position:absolute; " - "visibility:inherit; " - "overflow:hidden; " - "left:7px; " - "top:%INTpx; " - "width:calc(100%% - 7px); " - "height:%POSpx; " - "z-index:%POS; " - "}\n", - id, i, - cover_margin + ((i-1) * item_spacing) + 2, - min(item_spacing, para_height + 4), - z + 2 - ); - i++; - } - } - - /** Script initialization call. **/ - if (strlen(main_background) > 0) { - htrAddScriptInit_va(s, - " var rb = wgtrGetNodeRef(ns, \"%STR&SYM\");\n" - " radiobuttonpanel_init({\n" - " parentPane:rb, fieldname:\"%STR&JSSTR\",\n" - " borderPane:htr_subel(rb,\"rb%POSborder\"),\n" - " coverPane:htr_subel(htr_subel(rb,\"rb%POSborder\"),\"rb%POScover\"),\n" - " titlePane:htr_subel(rb,\"rb%POStitle\"),\n" - " mainBackground:\"%STR&JSSTR\", outlineBackground:\"%STR&JSSTR\", form:\"%STR&JSSTR\"});\n", - name, fieldname, id, id,id, id, main_background, outline_background, form); - } else { - htrAddScriptInit_va(s," radiobuttonpanel_init({parentPane:wgtrGetNodeRef(ns,\"%STR&SYM\"), fieldname:\"%STR&JSSTR\", borderPane:0, coverPane:0, titlePane:0, mainBackground:0, outlineBackground:0, form:\"%STR&JSSTR\"});\n", name, fieldname, form); - } - - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "radiobutton", "radiobutton_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "radiobutton", "radiobutton_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "radiobutton", "radiobutton_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "radiobutton", "radiobutton_mousemove"); - - /* - Now lets loop through and add each radiobutton - */ - i = 1; - for (j=0;jChildren));j++) + + /** Write HTML to contain the radio buttons. **/ + htrAddBodyItem_va(s, "
\n", id); + htrAddBodyItem_va(s, "
\n", id); + htrAddBodyItem_va(s, "
\n", id); + + /** Search child array for radio buttons. **/ + XArray radio_buttons; + xaInit(&radio_buttons, tree->Children.nItems); + for (int i = 0; i < tree->Children.nItems; i++) { - sub_tree = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(sub_tree,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - if (wgtrGetPropertyValue(sub_tree,"value",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(value, ptr, sizeof(value)); - else - value[0] = '\0'; - if (wgtrGetPropertyValue(sub_tree,"label",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(label, ptr, sizeof(label)); - else - label[0] = '\0'; - is_selected = htrGetBoolean(sub_tree, "selected", 0); - if (is_selected < 0) is_selected = 0; - htrAddWgtrObjLinkage_va(s,sub_tree,"rb%POSoption%POS",id,i); - wgtrGetPropertyValue(sub_tree,"name",DATA_T_STRING,POD(&ptr)); - htrAddScriptInit_va(s, - " var rbitem = wgtrGetNodeRef('%STR&SYM', '%STR&SYM');\n" - " add_radiobutton(rbitem, {selected:%INT, buttonset:htr_subel(rbitem, \"rb%POSbuttonset%POS\"), buttonunset:htr_subel(rbitem, \"rb%POSbuttonunset%POS\"), value:htr_subel(rbitem, \"rb%POSvalue%POS\"), label:htr_subel(rbitem, \"rb%POSlabel%POS\"), valuestr:\"%STR&JSSTR\", labelstr:\"%STR&JSSTR\"});\n", - wgtrGetNamespace(sub_tree), ptr, - is_selected, - id, i, id, i, - id, i, id, i, - value, label); - i++; - } - else - { - htrRenderWidget(s, sub_tree, z+1); - } + pWgtrNode child = tree->Children.Items[i]; + + /** Mark child as no-object. **/ + child->RenderFlags |= HT_WGTF_NOOBJECT; + + /** Add radio buttons to the array, render other widgets immediately (so we can forget about them). **/ + wgtrGetPropertyValue(child, "outer_type", DATA_T_STRING, POD(&ptr)); + if (strcmp(ptr, "widget/radiobutton") == 0) xaAddItem(&radio_buttons, child); + else htrRenderWidget(s, child, z + 1); } - - /* - Do the HTML layers - */ - htrAddBodyItem_va(s,"
\n", id); - htrAddBodyItem_va(s,"
\n", id); - htrAddBodyItem_va(s,"
\n", id); - - /* Loop through each radio button and do the option pane and sub layers */ - i = 1; - for (j=0;jChildren));j++) + + /** Write style for radio buttons. **/ + const int top_padding = 12; + const int button_height = para_height + 2; + const int content_height = top_padding + (button_height * radio_buttons.nItems) + 8; + for (int i = 0; i < radio_buttons.nItems; i++) { - radiobutton_obj = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - /** CSS layers **/ - htrAddStylesheetItem_va(s, - "#rb%POSbuttonset%POS, " - "#rb%POSbuttonunset%POS { " - "position:absolute; " - "overflow:hidden; " - "left:5px; " - "top:%INTpx; " - "width:12px; " - "height:12px; " - "z-index:%POS; " - "cursor:pointer; " - "}\n", - id, i, - id, i, - (para_height / 2) - 3, - z + 2 - ); - htrAddStylesheetItem_va(s, - "#rb%POSvalue%POS { " - "position:absolute; " - "visibility:hidden; " - "overflow:hidden; " - "left:5px; " - "top:6px; " - "width:12px; " - "height:12px; " - "z-index:%POS; " - "}\n", - id, i, - z + 2 - ); - htrAddStylesheetItem_va(s, - "#rb%POSlabel%POS { " - "position:absolute; " - "visibility:inherit; " - "overflow:hidden; " - "left:27px; " - "top:3px; " - "width:calc(100%% - 27px); " - "height:%POSpx; " - "z-index:%POS; " - "cursor:pointer; " - "}\n", - id, i, - item_spacing - 1, - z + 2 - ); - - /** Body layers **/ - htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); - - wgtrGetPropertyValue(radiobutton_obj,"label",DATA_T_STRING,POD(&ptr)); - strtcpy(sbuf2,ptr,sizeof(sbuf2)); - htrAddBodyItem_va(s,"
%STR&HTE
\n", - id, i, textcolor, sbuf2); - - /* use label (from above) as default value if no value given */ - if(wgtrGetPropertyValue(radiobutton_obj,"value",DATA_T_STRING,POD(&ptr))==0) - { - strtcpy(sbuf2,ptr,sizeof(sbuf2)); - } - - htrAddBodyItem_va(s," \n", - id, i, sbuf2); - htrAddBodyItem(s, "
\n"); - i++; - } + pWgtrNode radio_button = radio_buttons.Items[i]; + + /** Store data for the individual radio button. **/ + char value_buf[64] = "", label_buf[64] = ""; + if (wgtrGetPropertyValue(radio_button, "value", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(value_buf, ptr, sizeof(value_buf)); + if (wgtrGetPropertyValue(radio_button, "label", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(label_buf, ptr, sizeof(label_buf)); + const int is_selected = (htrGetBoolean(radio_button, "selected", 0) > 0); + wgtrGetPropertyValue(radio_button, "name", DATA_T_STRING, POD(&ptr)); + + /** Create pointers to data. **/ + char* name = ptr; /* Name temporarily stored in the wgtrGetPropertyValue() buffer. */ + char* value = value_buf; + char* label = (label_buf[0] == '\0') ? value_buf : label_buf; + + /** Link the radio button DOM node to widget data. **/ + htrAddWgtrObjLinkage_va(s, radio_button, "rb%POSoption%POS", id, i); + + /** Write the initialization call. **/ + htrAddScriptInit_va(s, "{ " + "const rbitem = wgtrGetNodeRef('%STR&SYM', '%STR&SYM');" + "add_radiobutton(rbitem, { " + "selected:%POS, " + "buttonset:htr_subel(rbitem, 'rb%POSbuttonset%POS'), " + "buttonunset:htr_subel(rbitem, 'rb%POSbuttonunset%POS'), " + "value:htr_subel(rbitem, 'rb%POSvalue%POS'), " + "label:htr_subel(rbitem, 'rb%POSlabel%POS'), " + "valuestr:'%STR&JSSTR', " + "labelstr:'%STR&JSSTR', " + "}); }\n", + wgtrGetNamespace(radio_button), name, + is_selected, + id, i, id, i, + id, i, id, i, + value, label + ); + + /** Write CSS for the radio button container. **/ + const int base_top = top_padding + (button_height * i); + const double percent_space_above = (100.0 / radio_buttons.nItems) * i; + const double content_above = ((double)content_height / radio_buttons.nItems) * i; + htrAddStylesheetItem_va(s, + "#rb%POSoption%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:7px; " + "top:calc(0px " + "+ %POSpx " + "+ min(%DBL%% - %DBLpx, %POSpx) " + "); " + "width:calc(100%% - 14px); " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, i, + base_top, + percent_space_above, content_above, spacing * i, + button_height, + z + 2 + ); + + /** Write CSS for the radio button elements. **/ + htrAddStylesheetItem_va(s, + "#rb%POSbuttonset%POS, " + "#rb%POSbuttonunset%POS { " + "position:absolute; " + "overflow:hidden; " + "left:5px; " + "top:%INTpx; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + id, i, + (para_height / 2) - 3, + z + 2 + ); + htrAddStylesheetItem_va(s, + "#rb%POSvalue%POS { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:5px; " + "top:6px; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "}\n", + id, i, + z + 2 + ); + htrAddStylesheetItem_va(s, + "#rb%POSlabel%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:27px; " + "top:3px; " + "width:calc(100%% - 27px); " + "height:calc(100%% - 1px); " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + z + 2 + ); + + /** Write radio button HTML. **/ + htrAddBodyItem_va(s, "
\n", id, i); + htrAddBodyItem_va(s, " \n", id, i); + htrAddBodyItem_va(s, "
\n", id, i); + htrAddBodyItem_va(s, "
%STR&HTE
\n", id, i, textcolor, label); + htrAddBodyItem_va(s, " \n", id, i, value); + htrAddBodyItem (s, "
\n"); } - - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem_va(s,"
%STR&HTE
\n", id, textcolor, title); - htrAddBodyItem(s, "
\n"); - - return 0; -} + + htrAddBodyItem_va(s, + "
\n" + "
\n" + "
%STR&HTE
\n" + "
\n", + id, textcolor, title + ); + + return 0; + } /** htrbInitialize - register with the ht_render module. **/ int htrbInitialize() { pHtDriver drv; + + /** Initialize globals. */ + HTRB.idcnt = 0; /** Allocate the driver **/ drv = htrAllocDriver(); @@ -430,12 +432,9 @@ int htrbInitialize() { htrAddEvent(drv,"MouseMove"); htrAddEvent(drv,"DataChange"); - /** Register. **/ + /** Register with dhtml support. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); - HTRB.idcnt = 0; - return 0; } From e1f9a634f478d10b74b0b6296580ef58c62a3b50 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 11 Feb 2026 12:13:47 -0700 Subject: [PATCH 056/107] Clean up some JS for the radio button panel widget. --- centrallix-os/sys/js/htdrv_radiobutton.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_radiobutton.js b/centrallix-os/sys/js/htdrv_radiobutton.js index 2d5c9eb77..2902d7525 100644 --- a/centrallix-os/sys/js/htdrv_radiobutton.js +++ b/centrallix-os/sys/js/htdrv_radiobutton.js @@ -37,7 +37,6 @@ function rb_setvalue(v) { } } this.clearvalue(); - //alert('Warning: "'+v+'" is not in the radio button list.'); } function rb_cb_getvalue(p) { @@ -104,12 +103,8 @@ function add_radiobutton(optionPane, param) { var rb = wgtrGetParent(optionPane); rb.rbCount++; optionPane.valueIndex = rb.rbCount; - /*optionPane.kind = 'radiobutton'; - optionPane.document.layer = optionPane;*/ htr_init_layer(optionPane, rb, 'radiobutton'); - //optionPane.mainlayer = rb; optionPane.optionPane = optionPane; - optionPane.isSelected = param.selected; optionPane.valueStr = param.valuestr; optionPane.labelStr = param.labelstr; @@ -349,8 +344,6 @@ function rb_changemode(){ for (var i=0;i Date: Wed, 11 Feb 2026 12:37:42 -0700 Subject: [PATCH 057/107] Add Easter Eggs for code reviewers to find. --- centrallix-doc/Widgets/widgets.xml | 2 +- centrallix-os/sys/js/ht_geom_dom1html.js | 1 + centrallix/htmlgen/htdrv_dropdown.c | 1 + centrallix/htmlgen/htdrv_radiobutton.c | 1 + centrallix/htmlgen/htdrv_scrollpane.c | 2 +- centrallix/htmlgen/htdrv_tab.c | 3 ++- centrallix/wgtr/apos.c | 2 ++ 7 files changed, 9 insertions(+), 3 deletions(-) diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index 6267ffd08..58e5aede8 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -2824,7 +2824,7 @@ osrc1 "widget/osrc" Starts a new app in a new window. - Logs data to the console (using console.log()), for testing and debugging. Set the 'Message' to specify a text string that should appear in the log. + Logs data to the console (using console.log()), for testing and debugging. Set the 'Message' to specify a text string that should appear in the log. Might be useful for logging Easter Egg #8. Loads the page. diff --git a/centrallix-os/sys/js/ht_geom_dom1html.js b/centrallix-os/sys/js/ht_geom_dom1html.js index 71d29db99..f2687cc3c 100644 --- a/centrallix-os/sys/js/ht_geom_dom1html.js +++ b/centrallix-os/sys/js/ht_geom_dom1html.js @@ -405,6 +405,7 @@ function setResponsive(l, value, d) { var fl_parent = l['__fl_parent_' + d2] ?? wgtrGetServerProperty(l, 'fl_parent_' + d2); if (fl_parent == undefined || fl_parent == null) { + /** I wonder if anyone reviewers will see this: Easter egg #7. **/ const warningMsg = 'setResponsive() - FAIL: Missing ' + ((wgtrIsNode(l)) ? 'wgtr.' : '__') + 'fl_parent_' + d2; console.warn(warningMsg, l); } diff --git a/centrallix/htmlgen/htdrv_dropdown.c b/centrallix/htmlgen/htdrv_dropdown.c index f2af95d00..8f3d586ec 100644 --- a/centrallix/htmlgen/htdrv_dropdown.c +++ b/centrallix/htmlgen/htdrv_dropdown.c @@ -52,6 +52,7 @@ static struct { #define HTDD_DYNAMIC 2 #define HTDD_DYNAMIC_CLIENT HTDD_DYNAMIC #define HTDD_OBJECTSOURCE 3 +#define HTDD_EASTER_EGG_4 4 /* htddRender - generate the HTML code for the page. diff --git a/centrallix/htmlgen/htdrv_radiobutton.c b/centrallix/htmlgen/htdrv_radiobutton.c index 5946e048c..27024d363 100644 --- a/centrallix/htmlgen/htdrv_radiobutton.c +++ b/centrallix/htmlgen/htdrv_radiobutton.c @@ -140,6 +140,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) "const borderPane = htr_subel(parentPane, 'rb%POSborder'); " "const coverPane = htr_subel(borderPane, 'rb%POScover'); " "const titlePane = htr_subel(parentPane, 'rb%POStitle'); " + "const easterEgg2 = 'Easter Egg #2';" "radiobuttonpanel_init({ " "parentPane, borderPane, coverPane, titlePane, " "fieldname:'%STR&JSSTR', " diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index 2a4b36154..7b012e95e 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -346,7 +346,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) "" /** Close the scrollpane table (see above). **/ - "\n", + " \n", h - 36 ); diff --git a/centrallix/htmlgen/htdrv_tab.c b/centrallix/htmlgen/htdrv_tab.c index 715a5a6e2..75eaefdee 100644 --- a/centrallix/htmlgen/htdrv_tab.c +++ b/centrallix/htmlgen/htdrv_tab.c @@ -491,7 +491,8 @@ httabRender(pHtSession s, pWgtrNode tree, int z) "box-shadow:%DBLpx %DBLpx %POSpx %STR&CSSVAL; " "text-align:%STR&CSSVAL; " "color:%STR&CSSVAL; " - "font-weight:bold; " + "font-weight:bold; /*" + "easter-egg-6:value;*/ " "background-position: %INTpx %INTpx; " "%STR " "}\n", diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 06550eda0..ea42c2e63 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -81,6 +81,8 @@ *** XArray: This array also stores its size (nAlloc) and the number of items *** stored (nItems), so you don't have to pass that info separately. *** + *** Easter Egg #1: I wonder if any reviewers will find this... + *** *** SWidgets, CWidgets, and EWidgets: Lines record which widgets start, cross, *** and end on them. These categories are exclusive, so a widget which *** starts on a given line will be in the SWidgets list but it will not be From 1b69670637718d0a82f8fa5a2c064f3a4884633d Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 11 Feb 2026 12:48:40 -0700 Subject: [PATCH 058/107] Fix a bug in htdrv_radiobutton.js. --- centrallix-os/sys/js/htdrv_radiobutton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/centrallix-os/sys/js/htdrv_radiobutton.js b/centrallix-os/sys/js/htdrv_radiobutton.js index 2902d7525..9f389ba4a 100644 --- a/centrallix-os/sys/js/htdrv_radiobutton.js +++ b/centrallix-os/sys/js/htdrv_radiobutton.js @@ -384,7 +384,7 @@ function rb_changemode(){ } function radiobuttonpanel_init(param) { - const { parentPane, borderpane, coverpane, titlepane } = param; + const { parentPane, borderPane: borderpane, coverPane: coverpane, titlePane: titlepane } = param; if (cx__capabilities.Dom1HTML) titlepane.styleobj = titlepane.getElementsByTagName('table')[0]; From 59c9fc0f1db819cd3793461e318b4d2f64a12dbf Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 11 Feb 2026 12:49:04 -0700 Subject: [PATCH 059/107] Tweak size of selection area in htdrv_radiobutton.js. --- centrallix-os/sys/js/htdrv_radiobutton.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/centrallix-os/sys/js/htdrv_radiobutton.js b/centrallix-os/sys/js/htdrv_radiobutton.js index 9f389ba4a..5aca57c55 100644 --- a/centrallix-os/sys/js/htdrv_radiobutton.js +++ b/centrallix-os/sys/js/htdrv_radiobutton.js @@ -147,9 +147,9 @@ function add_radiobutton(optionPane, param) { } optionPane.yOffset = getRelativeY(optionPane)+getRelativeY(rb.coverPane)+getRelativeY(rb.borderPane); - optionPane.area = pg_addarea(rb, getRelativeX(optionPane), optionPane.yOffset, getClipWidth(optionPane), pg_parah+4, optionPane, 'rb', 3); + optionPane.area = pg_addarea(rb, getRelativeX(optionPane), optionPane.yOffset - 1, getClipWidth(optionPane), pg_parah+4, optionPane, 'rb', 3); optionPane.area.__defineGetter__('width', () => parseInt(getComputedStyle(optionPane).width)); - optionPane.area.__defineGetter__('height', () => parseInt(getComputedStyle(optionPane).height)); + optionPane.area.__defineGetter__('height', () => parseInt(getComputedStyle(optionPane).height) + 3); } function rb_getfocus(xo,yo,l,c,n,a,from_kbd) From f2f5d7a8f111e3e576790266d49ab18f133573f6 Mon Sep 17 00:00:00 2001 From: Lightning11wins Date: Wed, 11 Feb 2026 14:53:24 -0700 Subject: [PATCH 060/107] Fix some inconsistent style issues with generated HTML & CSS. Fix inconsistent CSS rules in the style tag for the document head. Fix inconsistent indentation of head tags. Fix code style mistakes in generation code. Update examples in docs to use correct styling. Update outdated generation techniques. Clean up unused code in the generation process. --- centrallix/htmlgen/ht_render.c | 15 +- centrallix/htmlgen/htdrv_autolayout.c | 20 +-- centrallix/htmlgen/htdrv_button.c | 208 +++++++++++++++++++++---- centrallix/htmlgen/htdrv_calendar.c | 13 +- centrallix/htmlgen/htdrv_chart.c | 2 +- centrallix/htmlgen/htdrv_checkbox.c | 2 +- centrallix/htmlgen/htdrv_clock.c | 6 +- centrallix/htmlgen/htdrv_component.c | 19 ++- centrallix/htmlgen/htdrv_datetime.c | 8 +- centrallix/htmlgen/htdrv_dropdown.c | 39 +++-- centrallix/htmlgen/htdrv_editbox.c | 38 ++++- centrallix/htmlgen/htdrv_fileupload.c | 8 +- centrallix/htmlgen/htdrv_formstatus.c | 2 +- centrallix/htmlgen/htdrv_frameset.c | 2 +- centrallix/htmlgen/htdrv_html.c | 44 ++++-- centrallix/htmlgen/htdrv_image.c | 19 ++- centrallix/htmlgen/htdrv_imagebutton.c | 2 +- centrallix/htmlgen/htdrv_label.c | 28 +++- centrallix/htmlgen/htdrv_map.c | 15 +- centrallix/htmlgen/htdrv_menu.c | 60 ++++++- centrallix/htmlgen/htdrv_multiscroll.c | 45 +++++- centrallix/htmlgen/htdrv_objcanvas.c | 17 +- centrallix/htmlgen/htdrv_osrc.c | 14 +- centrallix/htmlgen/htdrv_page.c | 74 ++++----- centrallix/htmlgen/htdrv_pane.c | 13 +- centrallix/htmlgen/htdrv_radiobutton.c | 16 +- centrallix/htmlgen/htdrv_scrollbar.c | 43 ++++- centrallix/htmlgen/htdrv_scrollpane.c | 10 +- centrallix/htmlgen/htdrv_spinner.c | 12 +- centrallix/htmlgen/htdrv_tab.c | 13 +- centrallix/htmlgen/htdrv_table.c | 8 +- centrallix/htmlgen/htdrv_terminal.c | 22 ++- centrallix/htmlgen/htdrv_textarea.c | 9 -- centrallix/htmlgen/htdrv_textbutton.c | 24 +-- centrallix/htmlgen/htdrv_treeview.c | 20 ++- centrallix/htmlgen/htdrv_window.c | 25 ++- centrallix/utility/iface_html.c | 3 +- 37 files changed, 673 insertions(+), 245 deletions(-) diff --git a/centrallix/htmlgen/ht_render.c b/centrallix/htmlgen/ht_render.c index 871eb258d..915abfdea 100644 --- a/centrallix/htmlgen/ht_render.c +++ b/centrallix/htmlgen/ht_render.c @@ -1811,7 +1811,6 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe htrAddScriptInit_va(s, "\n var ns = \"%STR&SYM\";\n", /*" var rootname = \"%STR&SYM\";\n", */ s->Namespace->DName /*, s->Namespace->DName */); - /*htrAddStylesheetItem(s, "\tdiv {position:absolute; visibility:inherit; overflow:hidden; }\n");*/ /** Render the top-level widget -- the function that's run * underneath will be dependent upon what the widget @@ -1831,7 +1830,7 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe #ifdef WGTR_DBG_WINDOW htrAddScriptWgtr_va(s, " wgtrWalk(%STR&SYM);\n", tree->Name); htrAddScriptWgtr(s, " ifcLoadDef(\"net/centrallix/button.ifc\");\n"); - htrAddStylesheetItem(s, "\t#dbgwnd {position: absolute; top: 400; left: 50;}\n"); + htrAddStylesheetItem(s, "\t\t#dbgwnd {position: absolute; top: 400; left: 50;}\n"); htrAddBodyItem(s, "
" "" "
\n"); @@ -1868,12 +1867,12 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe htrQPrintf(s, "\n" "\n" - " \n" - " \n" - " \n" + "\t\n" + "\t\n" + "\t\n" , cx__version); - htrWrite(s, " \n", -1); + htrWrite(s, "\t\n", -1); /** Write the HTML header items. **/ for(i=0;iPage.HtmlHeader.nItems;i++) { @@ -2700,7 +2699,7 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y /** Generate the style CSS **/ htrAddStylesheetItem_va(s, - "\t%STR {" + "\t\t%STR { " "left:"ht_flex_format"; " "top:"ht_flex_format"; " "%[width:"ht_flex_format"; %]" // BUG! diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index 02d6d7d2a..f1e62b3d5 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -100,16 +100,16 @@ htalRender(pHtSession s, pWgtrNode tree, int z) /** Add the stylesheet for the layer **/ htrAddStylesheetItem_va(s, - "\t#al%POSbase { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "OVERFLOW:visible; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "HEIGHT:"ht_flex_format"; " - "Z-INDEX:%POS; " - "}\n", + "\t\t#al%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:visible; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n ", id, ht_flex_x(x, tree), ht_flex_y(y, tree), diff --git a/centrallix/htmlgen/htdrv_button.c b/centrallix/htmlgen/htdrv_button.c index 3e3f7c577..0af5a8dfd 100644 --- a/centrallix/htmlgen/htdrv_button.c +++ b/centrallix/htmlgen/htdrv_button.c @@ -151,7 +151,7 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if (!strcmp(type,"image") || !strcmp(type,"textoverimage")) { htrAddStylesheetItem_va(s, - "\t#gb%POSpane { " + "\t\t#gb%POSpane { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " @@ -169,7 +169,9 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) /** Button click animation. **/ if (is_enabled) { htrAddStylesheetItem_va(s, - "\t#tb%POSpane:active { transform: translate(1px, 1px); }\n", + "\t\t#tb%POSpane:active { " + "transform:translate(1px, 1px); " + "}\n", id ); } @@ -215,19 +217,17 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if(!strcmp(type,"textoverimage")) { htrAddStylesheetItem_va(s, - "\t#gb%POSpane2 { " + "\t\t#gb%POSpane2 { " "position:absolute; " "visibility:inherit; " - "left:%POS; " - "top:%POS; " + "left:0px; " + "top:0px; " "width:"ht_flex_format"; " "z-index:%POS; " "}\n", id, - 0, - 0, - ht_flex(w, tree->Parent->width, ht_get_fl_w(tree)), - z+1 + ht_flex_w(w, tree), + z + 1 ); htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h,fgcolor1,text); } @@ -262,13 +262,107 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if(s->Capabilities.Dom0NS) { /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:"ht_flex_format"; TOP:"ht_flex_format"; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,ht_flex(x,tree->Parent->width,ht_get_fl_x(tree)),ht_flex(y,tree->Parent->height,ht_get_fl_y(tree)),ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:%STR; LEFT:-1; TOP:-1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_enabled?"inherit":"hidden",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+1); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_enabled?"hidden":"inherit",ht_flex(w-1,tree->Parent->width,ht_get_fl_w(tree)),z+1); - htrAddStylesheetItem_va(s,"\t#gb%POStop { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSbtm { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:"ht_flex_format"; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",ht_flex(w,tree->Parent->width,ht_get_fl_w(tree)),z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSrgt { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSlft { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane2 { " + "position:absolute; " + "visibility:%STR; " + "left:-1px; " + "top:-1px; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + (is_enabled) ? "inherit" : "hidden", + ht_flex_w(w, tree), + z + 1 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane3 { " + "position:absolute; " + "visibility:%STR; " + "left:0px; " + "top:0px; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + (is_enabled) ? "hidden" : "inherit", + ht_flex_w(w - 1, tree), + z + 1 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POStop { " + "position:absolute; " + "visibility:%STR; " + "left:0px; " + "top:0px; " + "height:1px; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + (is_ts) ? "hidden" : "inherit", + ht_flex_w(w, tree), + z + 2 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSbtm { " + "position:absolute; " + "visibility:%STR; " + "left:0px; " + "top:0px; " + "height:1px; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + (is_ts) ? "hidden" : "inherit", + ht_flex_w(w, tree), + z + 2 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSrgt { " + "position:absolute; " + "visibility:%STR; " + "left:0px; " + "top:0px; " + "height:1px; " + "width:1px; " + "z-index:%POS; " + "}\n", + id, + (is_ts) ? "hidden" : "inherit", + z + 2 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSlft { " + "position:absolute; " + "visibility:%STR; " + "left:0px; " + "top:0px; " + "height:1px; " + "width:1px; " + "z-index:%POS; " + "}\n", + id, + (is_ts) ? "hidden" : "inherit", + z + 2 + ); /** Script initialization call. **/ htrAddScriptInit_va(s, " gb_init({layer:%STR&SYM, layer2:htr_subel(%STR&SYM, \"gb%POSpane2\"), layer3:htr_subel(%STR&SYM, \"gb%POSpane3\"), top:htr_subel(%STR&SYM, \"gb%POStop\"), bottom:htr_subel(%STR&SYM, \"gb%POSbtm\"), right:htr_subel(%STR&SYM, \"gb%POSrgt\"), left:htr_subel(%STR&SYM, \"gb%POSlft\"), width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR', n:\"%STR&JSSTR\", p:\"%STR&JSSTR\", c:\"%STR&JSSTR\", d:\"%STR&JSSTR\", type:\"%STR&JSSTR\"});\n", @@ -344,7 +438,7 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) if(h >=0 ) { htrAddStylesheetItem_va(s, - "\t#gb%POSpane { " + "\t\t#gb%POSpane { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -354,18 +448,30 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) "z-index:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w - 1 - 2 * box_offset, tree), z ); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2, #gb%POSpane3 { height: "ht_flex_format";}\n",id,id,ht_flex(h-3,tree->Parent->height,ht_get_fl_h(tree))); - htrAddStylesheetItem_va(s,"\t#gb%POSpane { height: "ht_flex_format";}\n",id,ht_flex(h-1-2*box_offset,tree->Parent->height,ht_get_fl_h(tree))); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane2, #gb%POSpane3 { " + "height: "ht_flex_format"; " + "}\n", + id, id, + ht_flex_h(h - 3, tree) + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane { " + "height:"ht_flex_format"; " + "}\n", + id, + ht_flex_h(h - 1 - 2 * box_offset, tree) + ); } else { htrAddStylesheetItem_va(s, - "\t#gb%POSpane { " + "\t\t#gb%POSpane { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -375,17 +481,57 @@ htbtnRender(pHtSession s, pWgtrNode tree, int z) "z-index:%POS; " "}\n", id, - ht_flex(x, tree->Parent->width, ht_get_fl_x(tree)), - ht_flex(y, tree->Parent->height, ht_get_fl_y(tree)), - ht_flex(w-1-2*box_offset, tree->Parent->width, ht_get_fl_w(tree)), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w - 1 - 2 * box_offset, tree), z ); } - htrAddStylesheetItem_va(s,"\t#gb%POSpane, #gb%POSpane2, #gb%POSpane3 { cursor:default; text-align: center; }\n",id,id,id); - htrAddStylesheetItem_va(s,"\t#gb%POSpane { %STR border-width: 1px; border-style: solid; border-color: white gray gray white; }\n",id,bgstyle); - /*htrAddStylesheetItem_va(s,"\t#gb%dpane { color: %s; }\n",id,fgcolor2);*/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:-1px; top: -1px; width:"ht_flex_format"; }\n",id,is_enabled?"inherit":"hidden",z+1,ht_flex(w-3,tree->Parent->width,ht_get_fl_w(tree))); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:0px; top: 0px; width:"ht_flex_format"; }\n",id,is_enabled?"hidden":"inherit",z+1,ht_flex(w-3,tree->Parent->width,ht_get_fl_w(tree))); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane, #gb%POSpane2, #gb%POSpane3 { " + "cursor:default; " + "text-align:center; " + "}\n", + id, id, id + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane { " + "%STR " + "border-width:1px; " + "border-style:solid; " + "border-color:white gray gray white; " + "}\n", + id, + bgstyle + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane2 { " + "position:absolute; " + "visibility:%STR; " + "left:-1px; " + "top:-1px; " + "width:"ht_flex_format"; " + "z-index:%INT; " + "}\n", + id, + (is_enabled) ? "inherit" : "hidden", + ht_flex_w(w - 3, tree), + z + 1 + ); + htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane3 { " + "visibility:%STR; " + "position:absolute; " + "left:0px; " + "top:0px; " + "width:"ht_flex_format"; " + "z-index:%INT; " + "}\n", + id, + (is_enabled) ? "hidden" : "inherit", + ht_flex_w(w - 3, tree), + z + 1 + ); if(!strcmp(type,"text")) { diff --git a/centrallix/htmlgen/htdrv_calendar.c b/centrallix/htmlgen/htdrv_calendar.c index 3605135fd..f871ffd1d 100644 --- a/centrallix/htmlgen/htdrv_calendar.c +++ b/centrallix/htmlgen/htdrv_calendar.c @@ -141,7 +141,18 @@ htcaRender(pHtSession s, pWgtrNode tree, int z) strtcpy(name,ptr,sizeof(name)); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#ca%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); + htrAddStylesheetItem_va(s, + "\t\t#ca%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, x, y, w, h, z + ); /** Script include to get functions **/ htrAddScriptInclude(s, "/sys/js/htdrv_calendar.js", 0); diff --git a/centrallix/htmlgen/htdrv_chart.c b/centrallix/htmlgen/htdrv_chart.c index d421643cf..1f4a40eb4 100644 --- a/centrallix/htmlgen/htdrv_chart.c +++ b/centrallix/htmlgen/htdrv_chart.c @@ -335,7 +335,7 @@ htchtGenHTML(pHtSession session, pWgtrNode tree, int z) /** Write style rules for the container div. **/ htrAddStylesheetItem_va(session, - "\t#%STR&SYMdiv { " + "\t\t#%STR&SYMdiv { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_checkbox.c b/centrallix/htmlgen/htdrv_checkbox.c index d5f164716..dbc054e83 100644 --- a/centrallix/htmlgen/htdrv_checkbox.c +++ b/centrallix/htmlgen/htdrv_checkbox.c @@ -93,7 +93,7 @@ int htcbRender(pHtSession s, pWgtrNode tree, int z) { /** Write style header. **/ htrAddStylesheetItem_va(s, - "\t#cb%POSmain { " + "\t\t#cb%POSmain { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_clock.c b/centrallix/htmlgen/htdrv_clock.c index 4799127c0..5a4cfd954 100644 --- a/centrallix/htmlgen/htdrv_clock.c +++ b/centrallix/htmlgen/htdrv_clock.c @@ -157,7 +157,7 @@ htclRender(pHtSession s, pWgtrNode tree, int z) /** Write style headers. **/ htrAddStylesheetItem_va(s, - "\t#cl%POSbase { " + "\t\t#cl%POSbase { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " @@ -172,7 +172,7 @@ htclRender(pHtSession s, pWgtrNode tree, int z) z ); htrAddStylesheetItem_va(s, - "\t.cl%POScon { " + "\t\t.cl%POScon { " "position:absolute; " "left:0px; " "top:0px; " @@ -180,8 +180,6 @@ htclRender(pHtSession s, pWgtrNode tree, int z) "z-index:%POS; " "}\n", id, - 0, - 0, z + 2 ); diff --git a/centrallix/htmlgen/htdrv_component.c b/centrallix/htmlgen/htdrv_component.c index c1460c2cf..6fe5c9a2d 100644 --- a/centrallix/htmlgen/htdrv_component.c +++ b/centrallix/htmlgen/htdrv_component.c @@ -286,7 +286,7 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Write styles for the enclosing div. **/ htrAddStylesheetItem_va(s, - "\t#cmp%POSbase { " + "\t\t#cmp%POSbase { " "position:absolute; " "visibility:inherit; " "overflow:visible; " @@ -306,10 +306,10 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Write enclosing div event CSS. **/ htrAddStylesheetItem_va(s, - "\t#cmp%POSbase { " + "\t\t#cmp%POSbase { " "pointer-events:none; " "}\n" - "\t#cmp%POSbase > * { " + "\t\t#cmp%POSbase > * { " "pointer-events:auto; " "}\n", id, id @@ -489,7 +489,18 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** Dynamic mode -- load from client **/ htrAddWgtrCtrLinkage(s, tree, "_parentctr"); htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "cmp%POS", id, NULL, ""); - htrAddStylesheetItem_va(s,"\t#cmp%POS { position:absolute; visibility:hidden; left:0px; top:0px; width:0px; height:0px; z-index:0;}\n", id); + htrAddStylesheetItem_va(s, + "\t\t#cmp%POS { " + "position:absolute; " + "visibility:hidden; " + "left:0px; " + "top:0px; " + "width:0px; " + "height:0px; " + "z-index:0; " + "}\n", + id + ); } htrRenderSubwidgets(s, tree, z+1); diff --git a/centrallix/htmlgen/htdrv_datetime.c b/centrallix/htmlgen/htdrv_datetime.c index 2161cda7b..9d50625cc 100644 --- a/centrallix/htmlgen/htdrv_datetime.c +++ b/centrallix/htmlgen/htdrv_datetime.c @@ -205,10 +205,10 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) /** Write style headers. **/ htrAddStylesheetItem_va(s, - "\t#dt%POSbtn { " - "overflow:hidden; " + "\t\t#dt%POSbtn { " "position:absolute; " "visibility:inherit; " + "overflow:hidden; " "left:"ht_flex_format"; " "top:"ht_flex_format"; " "width:"ht_flex_format"; " @@ -227,9 +227,9 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) bgcolor ); htrAddStylesheetItem_va(s, - "\t.dt%POScon { " - "overflow:hidden; " + "\t\t.dt%POScon { " "position:absolute; " + "overflow:hidden; " "left:1px; " "top:1px; " "width:calc(100%% - 20px); " diff --git a/centrallix/htmlgen/htdrv_dropdown.c b/centrallix/htmlgen/htdrv_dropdown.c index 8f3d586ec..965ce6f30 100644 --- a/centrallix/htmlgen/htdrv_dropdown.c +++ b/centrallix/htmlgen/htdrv_dropdown.c @@ -153,15 +153,15 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { /** Write basic element CSS. **/ htrAddStylesheetItem_va(s, - "\t#dd%POSbtn { " - "OVERFLOW:hidden; " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "HEIGHT:"ht_flex_format"; " - "Z-INDEX:%POS; " + "\t\t#dd%POSbtn { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " "cursor:default; " "background-color: %STR&CSSVAL; " "border:1px outset #e0e0e0; " @@ -174,21 +174,28 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { z, bgstr ); - if (*textcolor) { - htrAddStylesheetItem_va(s,"\t#dd%POSbtn { color: %STR&CSSVAL; }\n",id,textcolor); - } + if (*textcolor) + { + htrAddStylesheetItem_va(s, + "\t\t#dd%POSbtn { " + "color:%STR&CSSVAL; " + "}\n", + id, + textcolor + ); + } htrAddStylesheetItem_va(s, - "\t.dd%POScon { " - "overflow:hidden; " + "\t\t.dd%POScon { " "position:absolute; " + "overflow:hidden; " "left:1px; " "top:1px; " "width:1024px; " - "height:"ht_flex_format"; " + "height:%POS; " "z-index:%POS; " "}\n", id, - ht_flex(h - 2, h, 0.0), + h - 2, z + 1 ); diff --git a/centrallix/htmlgen/htdrv_editbox.c b/centrallix/htmlgen/htdrv_editbox.c index f4b85c397..5655e273a 100644 --- a/centrallix/htmlgen/htdrv_editbox.c +++ b/centrallix/htmlgen/htdrv_editbox.c @@ -151,7 +151,7 @@ htebRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ const int base_w = w - (2 * box_offset); htrAddStylesheetItem_va(s, - "\t#eb%POSbase { " + "\t\t#eb%POSbase { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -167,7 +167,7 @@ htebRender(pHtSession s, pWgtrNode tree, int z) z ); htrAddStylesheetItem_va(s, - "\t#eb%POScon1 { " + "\t\t#eb%POScon1 { " "position:absolute; " "visibility:inherit; " "left:5px; " @@ -230,14 +230,40 @@ htebRender(pHtSession s, pWgtrNode tree, int z) /** Use CSS border for drawing **/ if (is_raised) - htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: white gray gray white; %STR }\n",id, main_bg); + { + htrAddStylesheetItem_va(s, + "\t\t#eb%POSbase { " + "border-style:solid; " + "border-width:1px; " + "border-color: " + "white gray gray white; " + "%STR " + "}\n", + id, + main_bg + ); + } else - htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: gray white white gray; %STR }\n",id, main_bg); + { + htrAddStylesheetItem_va(s, + "\t\t#eb%POSbase { " + "border-style:solid; " + "border-width:1px; " + "border-color:gray white white gray; " + "%STR " + "}\n", + id, + main_bg + ); + } if (h >= 0) { htrAddStylesheetItem_va(s, - "\t#eb%POSbase { height:"ht_flex_format"; }\n", - id, ht_flex(h - (2 * box_offset), ht_get_parent_h(tree), ht_get_fl_h(tree)) + "\t\t#eb%POSbase { " + "height:"ht_flex_format"; " + "}\n", + id, + ht_flex_h(h - (2 * box_offset), tree) ); } diff --git a/centrallix/htmlgen/htdrv_fileupload.c b/centrallix/htmlgen/htdrv_fileupload.c index a3ed1a5c2..88953e857 100755 --- a/centrallix/htmlgen/htdrv_fileupload.c +++ b/centrallix/htmlgen/htdrv_fileupload.c @@ -94,7 +94,13 @@ htfuRender(pHtSession s, pWgtrNode tree, int z) htrAddScriptInit_va(s, " fu_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), pane:document.getElementById(\"fu%POSform\"), input:document.getElementById(\"fu%POSinput\"), iframe:document.getElementById(\"fu%POSiframe\"), target:\"%STR&JSSTR\"});\n", name, id, id, id, target); /** style header items **/ - htrAddStylesheetItem_va(s,"#fu%POSbase { POSITION:absolute; VISIBILITY:hidden; }\n", id); + htrAddStylesheetItem_va(s, + "\t\t#fu%POSbase { " + "position:absolute; " + "visibility:hidden; " + "}\n", + id + ); htrAddBodyItem_va(s,"
", id, id, id, id, id, id, id, multiselect?"MULTIPLE":""); /** Check for more sub-widgets **/ diff --git a/centrallix/htmlgen/htdrv_formstatus.c b/centrallix/htmlgen/htdrv_formstatus.c index b4b0d504c..1000a22cd 100644 --- a/centrallix/htmlgen/htdrv_formstatus.c +++ b/centrallix/htmlgen/htdrv_formstatus.c @@ -90,7 +90,7 @@ int htfsRender(pHtSession s, pWgtrNode tree, int z) { /** Ok, write the style header items. **/ htrAddStylesheetItem_va(s, - "\t#fs%POSmain { " + "\t\t#fs%POSmain { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_frameset.c b/centrallix/htmlgen/htdrv_frameset.c index 7ed9215d3..6d88a613a 100644 --- a/centrallix/htmlgen/htdrv_frameset.c +++ b/centrallix/htmlgen/htdrv_frameset.c @@ -58,7 +58,7 @@ htsetRender(pHtSession s, pWgtrNode tree, int z) /** Check for a title. **/ if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddHeaderItem_va(s," %STR&HTE\n",ptr); + htrAddHeaderItem_va(s, "\t%STR&HTE\n", ptr); } /** Loop through the frames (widget/page items) for geometry data **/ diff --git a/centrallix/htmlgen/htdrv_html.c b/centrallix/htmlgen/htdrv_html.c index 509438673..670af981b 100644 --- a/centrallix/htmlgen/htdrv_html.c +++ b/centrallix/htmlgen/htdrv_html.c @@ -103,11 +103,11 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) if (x < 0 || y < 0) { htrAddStylesheetItem_va(s, - "\t#ht%POSpane, #ht%POSpane, #ht%POSfader { " - "POSITION:relative; " - "VISIBILITY:inherit; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "\t\t#ht%POSpane, #ht%POSpane, #ht%POSfader { " + "position:relative; " + "visibility:inherit; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, id, id, ht_flex_w(w, tree), @@ -117,13 +117,13 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) else { htrAddStylesheetItem_va(s, - "\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { " - "POSITION:absolute; " - "VISIBILITY:inherit; " - "LEFT:"ht_flex_format"; " - "TOP:"ht_flex_format"; " - "WIDTH:"ht_flex_format"; " - "Z-INDEX:%POS; " + "\t\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " "}\n", id, id, id, ht_flex_x(x, tree), @@ -135,8 +135,24 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) if (s->Capabilities.CSS1) { - htrAddStylesheetItem_va(s,"\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { overflow:hidden; }\n", id, id, id); - htrAddStylesheetItem_va(s,"\t#ht%POSloader { overflow:hidden; visibility:hidden; position:absolute; top:0px; left:0px; width:0px; height:0px; }\n", id); + htrAddStylesheetItem_va(s, + "\t\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { " + "overflow:hidden; " + "}\n", + id, id, id + ); + htrAddStylesheetItem_va(s, + "\t\t#ht%POSloader { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "top:0px; " + "left:0px; " + "width:0px; " + "height:0px; " + "}\n", + id + ); } /** Write named global **/ diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index b9fe1585f..a1c6e6722 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -56,10 +56,23 @@ htimgSetup(pHtSession s) { /** Global style code for all image widget "img" tags **/ - htrAddStylesheetItem(s, " img.wimage { display:block; position:relative; left:0px; top:0px; }\n"); + htrAddStylesheetItem(s, + "\t\timg.wimage { " + "display:block; " + "position:relative; " + "left:0px; " + "top:0px; " + "}\n" + ); /** Global style code for all image widget "div" containers **/ - htrAddStylesheetItem(s, " div.wimage { visibility:inherit; position:absolute; overflow:hidden; }\n"); + htrAddStylesheetItem(s, + "\t\tdiv.wimage { " + "visibility:inherit; " + "position:absolute; " + "overflow:hidden; " + "}\n" + ); return 0; } @@ -167,7 +180,7 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) /** Write the style for the image container div. **/ htrAddStylesheetItem_va(s, - "\t#img%POS { " + "\t\t#img%POS { " "left:"ht_flex_format"; " "top:"ht_flex_format"; " "width:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_imagebutton.c b/centrallix/htmlgen/htdrv_imagebutton.c index 3f0bb5390..908947e08 100644 --- a/centrallix/htmlgen/htdrv_imagebutton.c +++ b/centrallix/htmlgen/htdrv_imagebutton.c @@ -138,7 +138,7 @@ htibtnRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ htrAddStylesheetItem_va(s, - "\t#ib%POSpane { " + "\t\t#ib%POSpane { " "position:absolute; " "visibility:inherit; " "overflow:hidden;" diff --git a/centrallix/htmlgen/htdrv_label.c b/centrallix/htmlgen/htdrv_label.c index 77ea3ffdc..084bbd97a 100644 --- a/centrallix/htmlgen/htdrv_label.c +++ b/centrallix/htmlgen/htdrv_label.c @@ -198,7 +198,7 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ htrAddStylesheetItem_va(s, - "\t#lbl%POS { " + "\t\t#lbl%POS { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " @@ -233,12 +233,32 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) ); if (is_link) - htrAddStylesheetItem_va(s,"\t#lbl%POS:hover { %[color:%STR&CSSVAL; %]text-decoration:underline; cursor:pointer; }\n", id, *pfgcolor, pfgcolor); + { + htrAddStylesheetItem_va(s, + "\t\t#lbl%POS:hover { " + "%[color:%STR&CSSVAL; %]" + "text-decoration:underline; " + "cursor:pointer; " + "}\n", + id, + (*pfgcolor), pfgcolor + ); + } if (is_link && *cfgcolor) - htrAddStylesheetItem_va(s,"\t#lbl%POS:active { color:%STR&CSSVAL; text-decoration:underline; cursor:pointer; }\n", id, cfgcolor); + { + htrAddStylesheetItem_va(s, + "\t\t#lbl%POS:active { " + "color:%STR&CSSVAL; " + "text-decoration:underline; " + "cursor:pointer; " + "}\n", + id, + cfgcolor + ); + } htrAddStylesheetItem_va(s, - "\t#lbl%POS p { " + "\t\t#lbl%POS p { " "text-align:%STR&CSSVAL; " "%[position:relative; top:50%%; transform:translateY(-50%%); %]" "padding:0px; " diff --git a/centrallix/htmlgen/htdrv_map.c b/centrallix/htmlgen/htdrv_map.c index 73f0d5518..28f23f3f7 100755 --- a/centrallix/htmlgen/htdrv_map.c +++ b/centrallix/htmlgen/htdrv_map.c @@ -104,7 +104,20 @@ int htmapRender(pHtSession s, pWgtrNode map_node, int z) strtcpy(name, ptr, sizeof(name)); /** Add css item for the layer **/ - htrAddStylesheetItem_va(s, "\t#map%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; overflow: hidden; %STR}\n", id, x, y, w, h, z, main_bg); + htrAddStylesheetItem_va(s, + "\t\t#map%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "%STR " + "}\n", + id, x, y, w, h, z, main_bg + ); htrAddWgtrObjLinkage_va(s, map_node, "map%POSbase", id); diff --git a/centrallix/htmlgen/htdrv_menu.c b/centrallix/htmlgen/htdrv_menu.c index 331b8bb92..75188cba8 100644 --- a/centrallix/htmlgen/htdrv_menu.c +++ b/centrallix/htmlgen/htdrv_menu.c @@ -286,7 +286,7 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) /** Write styles for the main DOM element. **/ htrAddStylesheetItem_va(s, - "\t#mn%POSmain { " + "\t\t#mn%POSmain { " "position:absolute; " "visibility:%STR; " "left:"ht_flex_format"; " @@ -305,14 +305,34 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) ); if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#mn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", id, shadow_offset, shadow_offset, shadow_radius, shadow_color); + htrAddStylesheetItem_va(s, + "\t\t#mn%POSmain { " + "box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; " + "}\n", + id, + shadow_offset, shadow_offset, shadow_radius, shadow_color + ); } if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POSmain { overflow:hidden; border-style: solid; border-width: 1px; border-color: white gray gray white; color:%STR; %STR }\n", id, textcolor, bgstr); + { + htrAddStylesheetItem_va(s, + "\t\t#mn%POSmain { " + "overflow:hidden; " + "border-style:solid; " + "border-width:1px; " + "border-color:white gray gray white; " + "color:%STR; " + "%STR " + "}\n", + id, + textcolor, + bgstr + ); + } /** Write styles for the content container. **/ htrAddStylesheetItem_va(s, - "\t#mn%POScontent { " + "\t\t#mn%POScontent { " "position:absolute; " "visibility:inherit; " "left:0px; " @@ -325,12 +345,38 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) z + 1 ); if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POScontent { overflow:hidden; cursor:default; }\n", id ); + { + htrAddStylesheetItem_va(s, + "\t\t#mn%POScontent { " + "overflow:hidden; " + "cursor:default; " + "}\n", + id + ); + } /** Write styles for the highlight bar. **/ - htrAddStylesheetItem_va(s, "\t#mn%POShigh { POSITION:absolute; VISIBILITY: hidden; LEFT:0px; TOP:0px; Z-INDEX:%POS; }\n", id, z); + htrAddStylesheetItem_va(s, + "\t\t#mn%POShigh { " + "position:absolute; " + "visibility: " + "hidden; " + "left:0px; " + "top:0px; " + "z-index:%POS; " + "}\n", + id, + z + ); if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POShigh { overflow:hidden; }\n", id ); + { + htrAddStylesheetItem_va(s, + "\t\t#mn%POShigh { " + "overflow:hidden; " + "}\n", + id + ); + } /** Get name **/ if (wgtrGetPropertyValue(menu,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; diff --git a/centrallix/htmlgen/htdrv_multiscroll.c b/centrallix/htmlgen/htdrv_multiscroll.c index 2db7a3d66..5829fa8be 100644 --- a/centrallix/htmlgen/htdrv_multiscroll.c +++ b/centrallix/htmlgen/htdrv_multiscroll.c @@ -107,12 +107,36 @@ htmsRender(pHtSession s, pWgtrNode tree, int z) /** Ok, write the style header items. **/ if(s->Capabilities.Dom0NS) { - htrAddStylesheetItem_va(s,"\t#ms%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); + htrAddStylesheetItem_va(s, + "\t\t#ms%POSmain { " + "position:absolute; " + "visibility:inherit; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, x, y, w, h, z + ); } else if(s->Capabilities.CSS1) { - htrAddStylesheetItem_va(s,"\t#ms%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); - htrAddStylesheetItem_va(s,"\t#ms%POSmain { %STR}\n",id,main_bg); + htrAddStylesheetItem_va(s, + "\t\t#ms%POSmain { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "%STR " + "}\n", + id, x, y, w, h, z, + main_bg + ); } else { @@ -163,7 +187,20 @@ htmsRender(pHtSession s, pWgtrNode tree, int z) else cy = h - total_h + total_h_accum; total_h_accum += ch; - htrAddStylesheetItem_va(s,"\t#ms%POSpart%POS { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:0px; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%INTpx,%INTpx,0px);}\n",id,i,cy,w,ch,z+1,w,ch); + htrAddStylesheetItem_va(s, + "\t\t#ms%POSpart%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:0px; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "clip:rect(0px, %INTpx, %INTpx, 0px); " + "}\n", + id, i, cy, w, ch, z + 1, w, ch + ); if (wgtrGetPropertyValue(child,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; if (always_visible && ch == 0) { diff --git a/centrallix/htmlgen/htdrv_objcanvas.c b/centrallix/htmlgen/htdrv_objcanvas.c index 5a1150533..bbd88d22e 100644 --- a/centrallix/htmlgen/htdrv_objcanvas.c +++ b/centrallix/htmlgen/htdrv_objcanvas.c @@ -108,7 +108,7 @@ htocRender(pHtSession s, pWgtrNode oc_node, int z) if (s->Capabilities.CSS2) { htrAddStylesheetItem_va(s, - "\t#oc%POSbase { " + "\t\t#oc%POSbase { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -129,7 +129,20 @@ htocRender(pHtSession s, pWgtrNode oc_node, int z) ); } else - htrAddStylesheetItem_va(s,"\t#oc%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; HEIGHT:%POS; Z-INDEX:%POS; }\n",id,x,y,w,h,z); + { + htrAddStylesheetItem_va(s, + "\t\t#oc%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:%INT; " + "top:%INT; " + "width:%POS; " + "height:%POS; " + "z-index:%POS; " + "}\n", + id, x, y, w, h, z + ); + } htrAddWgtrObjLinkage_va(s, oc_node, "oc%POSbase",id); diff --git a/centrallix/htmlgen/htdrv_osrc.c b/centrallix/htmlgen/htdrv_osrc.c index 9ba4bbfd9..5719e7e8e 100644 --- a/centrallix/htmlgen/htdrv_osrc.c +++ b/centrallix/htmlgen/htdrv_osrc.c @@ -250,7 +250,19 @@ htosrcRender(pHtSession s, pWgtrNode tree, int z) htrAddScriptGlobal(s, "osrc_relationships", "[]", 0); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s," #osrc%POSloader { overflow:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:1px; WIDTH:1px; HEIGHT:1px; Z-INDEX:0; }\n",id); + htrAddStylesheetItem_va(s, + "\t\t#osrc%POSloader { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:0px; " + "top:1px; " + "width:1px; " + "height:1px; " + "z-index:0; " + "}\n", + id + ); /** Script initialization call. **/ htrAddScriptInit_va(s," osrc_init({loader:wgtrGetNodeRef(ns,\"%STR&SYM\"), readahead:%INT, scrollahead:%INT, replicasize:%INT, sql:\"%STR&JSSTR\", filter:\"%STR&JSSTR\", baseobj:\"%STR&JSSTR\", name:\"%STR&SYM\", autoquery:%INT, requestupdates:%INT, ind_act:%INT, use_having:%INT, qy_reveal_only:%INT, send_updates:%INT, key_objname:\"%STR&JSSTR\", refresh:%INT});\n", diff --git a/centrallix/htmlgen/htdrv_page.c b/centrallix/htmlgen/htdrv_page.c index e79f7b47e..9b91fe72d 100755 --- a/centrallix/htmlgen/htdrv_page.c +++ b/centrallix/htmlgen/htdrv_page.c @@ -103,13 +103,13 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) /** Page icon? **/ if (wgtrGetPropertyValue(tree, "icon", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddHeaderItem_va(s, " \n",ptr); + htrAddHeaderItem_va(s, "\t\n",ptr); } /** Check for a title. **/ - if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) + if (wgtrGetPropertyValue(tree, "title", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddHeaderItem_va(s, " %STR&HTE\n",ptr); + htrAddHeaderItem_va(s, "\t%STR&HTE\n", ptr); } /** Check for page load status **/ @@ -324,60 +324,62 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) /** Shutdown **/ htrAddScriptCleanup_va(s, " pg_cleanup();\n"); + /** Add focus box. **/ if(s->Capabilities.HTML40) { - /** Add focus box **/ - htrAddStylesheetItem(s,"\ttd img { display: block; }\n"); - htrAddStylesheetItem(s,"\t#pgtop { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pglft { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgtvl { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:0px;WIDTH:1px;HEIGHT:1px; Z-INDEX:0; }\n"); - htrAddStylesheetItem(s,"\t#pgktop { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgkbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgkrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgklft { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pginpt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:20px; Z-INDEX:20; }\n"); - htrAddStylesheetItem(s,"\t#pgping { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n"); - htrAddStylesheetItem(s,"\t#pgmsg { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n"); + htrAddStylesheetItem(s, + "\t\ttd img { display: block; }\n" + "\t\t#pgtop { position:absolute; visibility:hidden; left:-1000px; top:0px; width:1152px; height:1px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgbtm { position:absolute; visibility:hidden; left:-1000px; top:0px; width:1152px; height:1px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgrgt { position:absolute; visibility:hidden; left:0px; top:-1000px; width:1px; height:864px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pglft { position:absolute; visibility:hidden; left:0px; top:-1000px; width:1px; height:864px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgtvl { position:absolute; visibility:hidden; left:0px; top:0px; width:1px; height:1px; z-index:0; }\n" + "\t\t#pgktop { position:absolute; visibility:hidden; left:-1000px; top:0px; width:1152px; height:1px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgkbtm { position:absolute; visibility:hidden; left:-1000px; top:0px; width:1152px; height:1px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgkrgt { position:absolute; visibility:hidden; left:0px; top:-1000px; width:1px; height:864px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pgklft { position:absolute; visibility:hidden; left:0px; top:-1000px; width:1px; height:864px; z-index:1000; clip:rect(0px,0px,0px,0px); overflow:hidden; }\n" + "\t\t#pginpt { position:absolute; visibility:hidden; left:0px; top:20px; z-index:20; }\n" + "\t\t#pgping { position:absolute; visibility:hidden; left:0px; top:0px; width:0px; height:0px; z-index:0; }\n" + "\t\t#pgmsg { position:absolute; visibility:hidden; left:0px; top:0px; width:0px; height:0px; z-index:0; }\n" + ); } else { - /** Add focus box **/ htrAddStylesheetItem(s, - "\t#pgtop { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pglft { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgtvl { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:1; Z-INDEX:0; }\n" - "\t#pgktop { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgkbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgkrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgklft { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pginpt { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:20; Z-INDEX:20; }\n" - "\t#pgping { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; Z-INDEX:0; }\n" - "\t#pgmsg { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; Z-INDEX:0; }\n"); + "\t\t#pgtop { position:absolute; visibility:hidden; left:0; top:0; width:1152; height:1; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgbtm { position:absolute; visibility:hidden; left:0; top:0; width:1152; height:1; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgrgt { position:absolute; visibility:hidden; left:0; top:0; width:1; height:864; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pglft { position:absolute; visibility:hidden; left:0; top:0; width:1; height:864; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgtvl { position:absolute; visibility:hidden; left:0; top:0; width:1; height:1; z-index:0; }\n" + "\t\t#pgktop { position:absolute; visibility:hidden; left:0; top:0; width:1152; height:1; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgkbtm { position:absolute; visibility:hidden; left:0; top:0; width:1152; height:1; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgkrgt { position:absolute; visibility:hidden; left:0; top:0; width:1; height:864; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pgklft { position:absolute; visibility:hidden; left:0; top:0; width:1; height:864; z-index:1000; clip:rect(1,1); }\n" + "\t\t#pginpt { position:absolute; visibility:hidden; left:0; top:20; z-index:20; }\n" + "\t\t#pgping { position:absolute; visibility:hidden; left:0; top:0; z-index:0; }\n" + "\t\t#pgmsg { position:absolute; visibility:hidden; left:0; top:0; z-index:0; }\n" + ); } if (show == 1) { - htrAddStylesheetItem(s, "\t#pgstat { POSITION:absolute; VISIBILITY:visible; LEFT:0;TOP:0;WIDTH:100%;HEIGHT:99%; Z-INDEX:100000;}\n"); + htrAddStylesheetItem(s, "\t\t#pgstat { position:absolute; visibility:visible; left:0;top:0;width:100%;height:99%; z-index:100000;}\n"); htrAddBodyItemLayerStart(s,0,"pgstat",0, NULL); htrAddBodyItem_va(s, "", bgstr); htrAddBodyItem (s, "
\n"); htrAddBodyItemLayerEnd(s,0); } - htrAddStylesheetItem_va(s, "\thtml { overflow:hidden; }\n"); - htrAddStylesheetItem_va(s, "\tbody { overflow:hidden; %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", + htrAddStylesheetItem_va(s, "\t\thtml { overflow:hidden; }\n"); + htrAddStylesheetItem_va(s, "\t\tbody { overflow:hidden; %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", font_size > 0, font_size, *font_name, font_name); - htrAddStylesheetItem(s, "\tpre { font-size:90%; }\n"); + htrAddStylesheetItem(s, "\t\tpre { font-size:90%; }\n"); if (s->Capabilities.Dom0NS) { - htrAddStylesheetItem_va(s, "\ttd { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", + htrAddStylesheetItem_va(s, "\t\ttd { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", font_size > 0, font_size, *font_name, font_name); - htrAddStylesheetItem_va(s, "\tfont { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", + htrAddStylesheetItem_va(s, "\t\tfont { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", font_size > 0, font_size, *font_name, font_name); } diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 6536a9689..ef64ab7b6 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -162,7 +162,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) { offset = -2 * box_offset; htrAddStylesheetItem_va(s, - "\t#pn%POSmain {" + "\t\t#pn%POSmain {" "border-style: solid; " "border-width: 1px; " "border-color: %STR %STR %STR %STR; " @@ -175,7 +175,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) { offset = -2 * box_offset; htrAddStylesheetItem_va(s, - "\t#pn%POSmain {" + "\t\t#pn%POSmain {" "border-style: solid;" "border-width: 1px; " "border-color:%STR&CSSVAL; " @@ -191,7 +191,7 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) /** Write the main CSS for the pane DOM node. **/ htrAddStylesheetItem_va(s, - "\t#pn%POSmain {" + "\t\t#pn%POSmain {" "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -216,8 +216,11 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) if (shadow_radius > 0) { htrAddStylesheetItem_va(s, - "\t#pn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", - id, shadow_offset, shadow_offset, shadow_radius, shadow_color + "\t\t#pn%POSmain { " + "box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; " + "}\n", + id, + shadow_offset, shadow_offset, shadow_radius, shadow_color ); } diff --git a/centrallix/htmlgen/htdrv_radiobutton.c b/centrallix/htmlgen/htdrv_radiobutton.c index 27024d363..c201a0a16 100644 --- a/centrallix/htmlgen/htdrv_radiobutton.c +++ b/centrallix/htmlgen/htdrv_radiobutton.c @@ -185,7 +185,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) const int para_height = s->ClientInfo->ParagraphHeight; const int top_offset = (para_height * 3) / 4 + 1; htrAddStylesheetItem_va(s, - "#rb%POSparent { " + "\t\t#rb%POSparent { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -203,7 +203,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) z ); htrAddStylesheetItem_va(s, - "#rb%POSborder { " + "\t\t#rb%POSborder { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -219,7 +219,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) z + 1 ); htrAddStylesheetItem_va(s, - "#rb%POScover { " + "\t\t#rb%POScover { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -233,7 +233,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) z + 2 ); htrAddStylesheetItem_va(s, - "#rb%POStitle { " + "\t\t#rb%POStitle { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -319,7 +319,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) const double percent_space_above = (100.0 / radio_buttons.nItems) * i; const double content_above = ((double)content_height / radio_buttons.nItems) * i; htrAddStylesheetItem_va(s, - "#rb%POSoption%POS { " + "\t\t#rb%POSoption%POS { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " @@ -341,7 +341,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) /** Write CSS for the radio button elements. **/ htrAddStylesheetItem_va(s, - "#rb%POSbuttonset%POS, " + "\t\t#rb%POSbuttonset%POS, " "#rb%POSbuttonunset%POS { " "position:absolute; " "overflow:hidden; " @@ -358,7 +358,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) z + 2 ); htrAddStylesheetItem_va(s, - "#rb%POSvalue%POS { " + "\t\t#rb%POSvalue%POS { " "position:absolute; " "visibility:hidden; " "overflow:hidden; " @@ -372,7 +372,7 @@ int htrbRender(pHtSession s, pWgtrNode tree, int z) z + 2 ); htrAddStylesheetItem_va(s, - "#rb%POSlabel%POS { " + "\t\t#rb%POSlabel%POS { " "position:absolute; " "visibility:inherit; " "overflow:hidden; " diff --git a/centrallix/htmlgen/htdrv_scrollbar.c b/centrallix/htmlgen/htdrv_scrollbar.c index e9b5b65e4..5b304443e 100644 --- a/centrallix/htmlgen/htdrv_scrollbar.c +++ b/centrallix/htmlgen/htdrv_scrollbar.c @@ -163,11 +163,48 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) } /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#sb%POSpane { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; clip:rect(0px,%POSpx,%POSpx,0px); Z-INDEX:%POS; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); + htrAddStylesheetItem_va(s, + "\t\t#sb%POSpane { " + "position:absolute; " + "visibility:%STR; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + x, y, w, h, + z + ); if (is_horizontal) - htrAddStylesheetItem_va(s,"\t#sb%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:18px; TOP:0px; WIDTH:18px; Z-INDEX:%POS; }\n",id,z+1); + htrAddStylesheetItem_va(s, + "\t\t#sb%POSthum { " + "position:absolute; " + "visibility:inherit; " + "left:18px; " + "top:0px; " + "width:18px; " + "z-index:%POS; " + "}\n", + id, + z + 1 + ); else - htrAddStylesheetItem_va(s,"\t#sb%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:18px; Z-INDEX:%POS; }\n",id,z+1); + htrAddStylesheetItem_va(s, + "\t\t#sb%POSthum { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:18px; " + "width:18px; " + "z-index:%POS; " + "}\n", + id, + z + 1 + ); /** Write globals for internal use **/ htrAddScriptGlobal(s, "sb_target_img", "null", 0); diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index 7b012e95e..84e98fadb 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -167,7 +167,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) /** Write shared CSS for the following UI elements. **/ htrAddStylesheetItem_va(s, - "\t.sp%POS_scroll { " + "\t\t.sp%POS_scroll { " "position:absolute; " "left:"ht_flex_format"; " "}\n", @@ -267,14 +267,14 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) { /** Write CSS for everything. **/ htrAddStylesheetItem_va(s, - "\t#sp%POSpane { " + "\t\t#sp%POSpane { " "position:absolute; " "visibility:%STR; " + "overflow:hidden; " "left:"ht_flex_format"; " "top:"ht_flex_format"; " "width:"ht_flex_format"; " "height:"ht_flex_format"; " - "overflow:clip; " "z-index:%POS; " "}\n", id, @@ -286,7 +286,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) z ); htrAddStylesheetItem_va(s, - "\t#sp%POSarea { " + "\t\t#sp%POSarea { " "position:absolute; " "visibility:inherit; " "left:0px; " @@ -299,7 +299,7 @@ htspaneRender(pHtSession s, pWgtrNode tree, int z) z + 1 ); htrAddStylesheetItem_va(s, - "\t#sp%POSthumb { " + "\t\t#sp%POSthumb { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_spinner.c b/centrallix/htmlgen/htdrv_spinner.c index 7252b86a5..fcb0a7776 100644 --- a/centrallix/htmlgen/htdrv_spinner.c +++ b/centrallix/htmlgen/htdrv_spinner.c @@ -114,12 +114,12 @@ htspnrRender(pHtSession s, pWgtrNode tree, int z) } /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#spnr%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#spnr%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-12,z); - htrAddStylesheetItem_va(s,"\t#spnr%POScon1 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-2-12,z+1); - htrAddStylesheetItem_va(s,"\t#spnr%POScon2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-2-12,z+1); - htrAddStylesheetItem_va(s,"\t#spnr_button_up { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",1+w-12,1,w,z); - htrAddStylesheetItem_va(s,"\t#spnr_button_down { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",1+w-12,1+9,w,z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POSmain { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, x, y, w, z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POSbase { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-12, z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POScon1 { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-2-12, z+1); + htrAddStylesheetItem_va(s, "\t\t#spnr%POScon2 { position:absolute; visibility:hidden; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-2-12, z+1); + htrAddStylesheetItem_va(s, "\t\t#spnr_button_up { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", 1+w-12, 1, w, z); + htrAddStylesheetItem_va(s, "\t\t#spnr_button_down { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", 1+w-12, 1+9,w, z); /** DOM Linkage **/ htrAddWgtrObjLinkage_va(s, tree, "spnr%POSmain",id); diff --git a/centrallix/htmlgen/htdrv_tab.c b/centrallix/htmlgen/htdrv_tab.c index 75eaefdee..ba6e02a0f 100644 --- a/centrallix/htmlgen/htdrv_tab.c +++ b/centrallix/htmlgen/htdrv_tab.c @@ -470,14 +470,14 @@ httabRender(pHtSession s, pWgtrNode tree, int z) int tab_x = (x + xtoffset) + (i_offset_x * i); int tab_y = (y + ytoffset) + (i_offset_y * i); htrAddStylesheetItem_va(s, - "\t#tc%POStab%POS { " + "\t\t#tc%POStab%POS { " "position:absolute; " "visibility:inherit; " + "overflow:hidden; " "left:"ht_flex_format"; " "top:"ht_flex_format"; " "%[width:%POSpx; %]" /* Tab width has 0 flexibility. */ "%[height:%POSpx; %]" /* Tab height has 0 flexibility. */ - "overflow:hidden; " "z-index:%POS; " "cursor:default; " "border-radius:" @@ -517,8 +517,11 @@ httabRender(pHtSession s, pWgtrNode tree, int z) ); htrAddStylesheetItem_va(s, - "\t#tc%POStab%POS.tab_selected { transform: translate(%INTpx, %INTpx); }\n", - id, i + 1, select_x_offset, select_y_offset + "\t\t#tc%POStab%POS.tab_selected { " + "transform:translate(%INTpx, %INTpx); " + "}\n", + id, i + 1, + select_x_offset, select_y_offset ); /** Write tab HTML content. **/ @@ -544,7 +547,7 @@ httabRender(pHtSession s, pWgtrNode tree, int z) /** Write tab control CSS and HTML. **/ htrAddStylesheetItem_va(s, - "#tc%POSctrl {" + "\t\t#tc%POSctrl {" "position:absolute; " "overflow:hidden; " "left:"ht_flex_format"; " diff --git a/centrallix/htmlgen/htdrv_table.c b/centrallix/htmlgen/htdrv_table.c index 5876bf658..a7475d3a2 100644 --- a/centrallix/htmlgen/htdrv_table.c +++ b/centrallix/htmlgen/htdrv_table.c @@ -145,7 +145,7 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) /** Write CSS for the table base element. **/ const int content_width = (t->overlap_scrollbar) ? (t->w) : (t->w - 18); htrAddStylesheetItem_va(s, - "\t#tbld%POSbase { " + "\t\t#tbld%POSbase { " "position:absolute; " "visibility:inherit; " "left:"ht_flex_format"; " @@ -165,7 +165,7 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) /** Write CSS for the table scrollbar. **/ const int row_start_y = (t->has_header) ? (t->min_rowheight + t->cellvspacing) : 0; htrAddStylesheetItem_va(s, - "\t#tbld%POSscroll { " + "\t\t#tbld%POSscroll { " "position:absolute; " "visibility:%STR; " "left:"ht_flex_format"; " @@ -184,7 +184,7 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) /** Write CSS for the table scroll thumb. **/ htrAddStylesheetItem_va(s, - "\t#tbld%POSthumb { " + "\t\t#tbld%POSthumb { " "position:absolute; " "visibility:inherit; " "left:0px; " @@ -366,7 +366,7 @@ httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) int h; if (wgtrGetPropertyValue(sub_tree, "height", DATA_T_INTEGER, POD(&h)) != 0) h = t->min_rowheight; htrAddStylesheetItem_va(s, - "\t#tbld%POSsub%POS { " + "\t\t#tbld%POSsub%POS { " "position:absolute; " "visibility:hidden; " "left:0px; " diff --git a/centrallix/htmlgen/htdrv_terminal.c b/centrallix/htmlgen/htdrv_terminal.c index a4a8ec44a..77b01da00 100644 --- a/centrallix/htmlgen/htdrv_terminal.c +++ b/centrallix/htmlgen/htdrv_terminal.c @@ -144,10 +144,24 @@ httermRender(pHtSession s, pWgtrNode tree, int z) htrAddBodyItem_va(s," \n",id); /** write the stylesheet header element **/ - htrAddStylesheetItem_va(s," #term%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; HEIGHT:%POS; Z-INDEX:%POS; }\n",id,x,y,cols*fontsize,rows*fontsize,z); - htrAddStylesheetItem_va(s," #term%POSreader { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; WIDTH:1; HEIGHT:1; Z-INDEX:-20; }\n",id); - htrAddStylesheetItem_va(s," #term%POSwriter { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; WIDTH:1; HEIGHT:1; Z-INDEX:-20; }\n",id); - htrAddStylesheetItem_va(s," .fixed%POS {font-family: fixed; }\n",id); + htrAddStylesheetItem_va(s, + "\t\t#term%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POS; " + "z-index:%POS; " + "}\n", + id, x, y, + cols * fontsize, + rows * fontsize, + z + ); + htrAddStylesheetItem_va(s, "\t\t#term%POSreader { position:absolute; visibility:hidden; left:0px; top:0px; width:1px; height:1; z-index:-20; }\n", id); + htrAddStylesheetItem_va(s, "\t\t#term%POSwriter { position:absolute; visibility:hidden; left:0px; top:0px; width:1px; height:1; z-index:-20; }\n", id); + htrAddStylesheetItem_va(s, "\t\t.fixed%POS {font-family: fixed; }\n",id); /** init line **/ htrAddScriptInit_va(s," terminal_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), rdr:\"term%POSreader\", wtr:\"term%POSwriter\", fxd:\"fixed%POS\", source:'%STR&JSSTR', rows:%INT, cols:%INT, colors:new Array(", diff --git a/centrallix/htmlgen/htdrv_textarea.c b/centrallix/htmlgen/htdrv_textarea.c index bd10c2efb..66884fd5c 100644 --- a/centrallix/htmlgen/htdrv_textarea.c +++ b/centrallix/htmlgen/htdrv_textarea.c @@ -143,7 +143,6 @@ httxRender(pHtSession s, pWgtrNode tree, int z) x, y, w-2*box_offset, h-2*box_offset, z, "", (char*[]){"border_color","#e0e0e0", "border_style",(is_raised?"outset":"inset"), NULL}, "overflow:hidden; position:absolute;"); - //htrAddStylesheetItem_va(s,"\t#tx%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%INT; overflow:hidden; }\n",id,x,y,w-2*box_offset,z); /** DOM Linkage **/ htrAddWgtrObjLinkage_va(s, tree, "tx%POSbase",id); @@ -171,14 +170,6 @@ httxRender(pHtSession s, pWgtrNode tree, int z) /** HTML body
element for the base layer. **/ htrAddBodyItem_va(s, "