2

I am using the Motif Library for my job. If anybody is not familiar with this library, you can find a list of files here https://packages.ubuntu.com/xenial/amd64/libmotif-dev/filelist, as well as another question here Motif 1.2 X11 on using RowColumn widget inside a ScrolledWindow.

My issue is that the xmRowColumnWidgetClass is drawn vertically, which means it draws it column by column (starts at top left moving to bottom left, then moves right to the next column, etc.)

Doing something like this:

Widget parentWidget, rowColumn;

XtAppContext app;

parentWidget = XtVaAppInitialize(&app, "rowColumn", NULL, 0,
&argc, argv, NULL, NULL);

and then like this:

rowColumn = XtVaCreateManagedWidget("rowcolumn",
            xmRowColumnWidgetClass,
            parentWidget,
            XmNnumColumns, 3,
            XmNpacking, XmPACK_COLUMN,
            XmNspacing, 6,
            NULL);

(void) XtVaCreateManagedWidget("button 1",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 2",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 3",
xmPushButtonWidgetClass, rowColumn, NULL);
        
(void) XtVaCreateManagedWidget("button 4",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 5",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 6",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 7",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 8",
xmPushButtonWidgetClass, rowColumn, NULL);

(void) XtVaCreateManagedWidget("button 9",
xmPushButtonWidgetClass, rowColumn, NULL);

So the above code would create the Widgets like this:

button 1    button 4    button 7

button 2    button 5    button 8

button 3    button 6    button 9

My problem is that I want certain ones to be side-by-side rather than above-below.

And suppose as a way around this I specify an order like this: button 1, button 4, button 7, button 2, button 5, button 8, button 3, button 6, button 9. That would look like this:

button 1    button 2    button 3

button 4    button 5    button 6

button 7    button 8    button 9

However, if I wanted to remove a button or add a new button, this messes everything up. Let's say I remove the "button 4" from the above example, so the order is like this: button 1, button 7, button 2, button 5, button 8, button 3, button 6, button 9. That would look like this:

button 1    button 5    button 6

button 7    button 8    button 9

button 2    button 3

As you can see, practically nothing that should be side-by-side next to each other is next to each other now.

This problem also happens when you add a new button: Let's say we want to add a button 10 to the end: button 1, button 4, button 7, button 2, button 5, button 8, button 3, button 6, button 9, button 10. This would look like this:

button 1    button 5    button 9

button 4    button 8    button 10

button 7    button 3

button 2    button 6

This by far looks the worst, since adding a new button increases the normal of rows to 4 instead of 3, which changes the entire layout.


To anybody familiar with this library:

How do I create a xmRowColumnWidgetClass instance that draws horizontally (row-by-row) instead of vertically? (column-by-column)

Or, how can I extend this library and possibly make a xmHorRowColumnWidgetClass that does what I want?


Another group of examples of this library:
https://github.com/spartrekus/Motif-C-Examples
https://github.com/spartrekus/Motif-C-Examples/blob/master/rowcol.c

Community
  • 1
  • 1
overlord
  • 141
  • 7
  • Did you find any solution to this? I'm in the same scenario as you: I need a RowColumn arranged horizontally and limiting the number of columns. It's weird that the RowColumn widget cannot do this (and XmForm is not comparable: you need to give the constraints manually, while the RowColumn automatically resizes itself to fit the strict size required by the children without any manual constraints). – cesss Sep 11 '20 at 10:43
  • I found an alternative widget to RowColumn that is fine for my purposes. I'll post it as an answer to your other question. – cesss Sep 11 '20 at 22:32

2 Answers2

1

Use XmForm class, and play with the constrained resources attached to the children. You can generate XmATTACH_POSITION in the child, and set a grid in the form to make it (the grid) to stretch and compress with the parent resizing. For example you can attatch the four borders of the child to a 4x3 cell imaginary grid coveing the parent (XmForm) widget and attach the children to fixed positions in the parent, so if you delete one of them, the hole is not filled by the neighbour widgets. See the doc for XmForm, IMHO is the widget you should use.

keypad.c

Below there's and example of a simple keypad usint XmForm to illustrate how the resources have to be initialized. It shows the minimum hardwired resources to touch.

/* main.c -- main program to create a keypad with an XmForm
                         widget.
 * Author: Luis Colorado <luiscoloradourcola@gmail.com>
 * Date: Tue Apr 21 16:15:28 EEST 2020
 * Copyright: (C) 2020 Luis Colorado.  All rights reserved.
 * License: BSD.
 */

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

#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>

#define F(_fmt) __FILE__":%d:%s: " _fmt, __LINE__, __func__

/* XmForm has a coordinate set based on a common denominator
 * that it applies to x and y coordinates, in order to subdivide
 * the grid (that has the same subdivisions along the x axis, as
 * it has along the y axis, so in order to make a 4x3 grid we
 * calculate the MCM(3, 4) = 12 to use as the denominator to
 * divide the x and y sides of it.  This makes the coordinates 
 * to go as the drawing marks.
 *      0   4   8  12
 *      +---+---+---+  0
 *      | 1 | 2 | 3 |
 *      +---+---+---+  3
 *      | 4 | 5 | 6 |
 *      *---+---+---+  6
 *      | 7 | 8 | 9 |
 *      +---+---+---+  9
 *      | * | 0 | # |
 *      +---+---+---+ 12
 */

struct but_desc {
        int left_x, top_y;
        int right_x, bot_y;
        char *name;
        char *out;
        void (*cb)(Widget, XtPointer, XtPointer);
};

void callback(Widget w, XtPointer data, XtPointer cd);

static struct but_desc
pos_tab[] = {
    { 4, 9,  8, 12, "b0",        "0", callback, },
        { 0, 0,  4,  3, "b1",        "1", callback, },
        { 4, 0,  8,  3, "b2",        "2", callback, },
        { 8, 0, 12,  3, "b3",        "3", callback, },
        { 0, 3,  4,  6, "b4",        "4", callback, },
        { 4, 3,  8,  6, "b5",        "5", callback, },
        { 8, 3, 12,  6, "b6",        "6", callback, },
        { 0, 6,  4,  9, "b7",        "7", callback, },
        { 4, 6,  8,  9, "b8",        "8", callback, },
        { 8, 6, 12,  9, "b9",        "9", callback, },
        { 8, 9, 12, 12, "bhash",     "#", callback, },
        { 0, 9,  4, 12, "basterisk", "*", callback, },
        { 0, 0,  0,  0, NULL, NULL, },
};

int
main(int argc, char **argv)
{
        XtAppContext app_ctx;
        char *class_name = "XKeypad";

        Widget topLevel = XtVaAppInitialize(
                &app_ctx,
                class_name,
                NULL, 0, /* XrmOptionDescRec array */
                &argc, argv, /* args to main */
                NULL, /* default resources */
                NULL); /* empty list of arguments */

        if (!topLevel) {
                fprintf(stderr,
                F("Couldnt create main widget"));
                exit(EXIT_FAILURE);
        }

        char *form_name = "form";
        Widget form = XtVaCreateManagedWidget(
        form_name,
                xmFormWidgetClass,
                topLevel,
                XmNfractionBase, 12,
                NULL);

        if (!form) {
                fprintf(stderr,
                        F("Couldn't create form '%s'\n"),
                        form_name);
                exit(EXIT_FAILURE);
        }

        struct but_desc *p;
        for (p = pos_tab; p->name; ++p) {
                printf(F("Creando botón '%s'@{(%d,%d),(%d,%d)}\n"),
                        p->name, p->left_x, p->top_y, p->right_x, p->bot_y);
                Widget button =
                        XtVaCreateManagedWidget(
                                        p->name,
                                        xmPushButtonGadgetClass,
                                        form,
                                        XmNleftAttachment, XmATTACH_POSITION,
                                        XmNrightAttachment, XmATTACH_POSITION,
                                        XmNtopAttachment, XmATTACH_POSITION,
                                        XmNbottomAttachment, XmATTACH_POSITION,
                                        XmNleftPosition, p->left_x,
                                        XmNrightPosition, p->right_x,
                                        XmNtopPosition, p->top_y,
                                        XmNbottomPosition, p->bot_y,
                                        NULL, NULL);
                if (!button) {
                        fprintf(stderr,
                                F("Couldnt create button '%s'\n"),
                                p->name);
                        exit(EXIT_FAILURE);
                }
                XtAddCallback(button, XmNactivateCallback, p->cb, p);
        }

        XtRealizeWidget(topLevel);
        XtAppMainLoop(app_ctx);

} /* main */

void callback(Widget w, XtPointer p, XtPointer cd)
{
        struct but_desc *data = p;
        write(1, data->out, strlen(data->out));
}

I have added this repository with the example code below. The repository includes a Makefile and a default resources file (XKeypad).

Community
  • 1
  • 1
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • Would you be able to give an example, or pseudocode? – overlord Apr 20 '20 at 16:23
  • I had some. One showing you a screen numeric keyPad (on a 4x3 matrix) and another allowing you to play minesweeper. But they were written before the era of `git`, I don't know where they are. Give me some time and I'll give you an example (a phone keypad can be an illustrating example) – Luis Colorado Apr 21 '20 at 13:14
  • @overlord, here you have the sample code you requested. Good luck. – Luis Colorado Apr 21 '20 at 17:12
0

To make the xmRowColumnWidgetClass instance draw horizontally, add XmNorientation, XmHORIZONTAL to the code:

rowColumn = XtVaCreateManagedWidget("rowcolumn",
            xmRowColumnWidgetClass,
            parentWidget,
            XmNnumColumns, 3,
            XmNorientation, XmHORIZONTAL,
            XmNpacking, XmPACK_COLUMN,
            XmNspacing, 6,
            NULL);

This makes it draw row-by-row rather than column-by-column.

XmNnumColumns now refers to the number of rows rather than columns for some reason, but this is a separate issue and so the above code does indeed answer my original question.

overlord
  • 141
  • 7