ion-general  

Proportional tab sizes (ugly patch).

ebik
Sun, 15 Mar 2009 08:25:32 -0700

Here I send patch for proportional tab sizes.

The patch itself is not acceptable because of two (interdependent)
issues:
  1.) I haven't time to examine source code to optimize for speed, and
      correct flow. (See using of 'complete' flag of
      grbrush_draw_textboxes, etc.)
  2.) It is the 'first shot' programming, i.e. it just works, but the
      code itself is rather ugly (e.g., I haven't put the new function
      in header file.)

But I haven't time for cleaning the code for next month or two, so I
send the patch as is.

--------------------------------------------------------------------

The algorithm now works in following way (I omit some border
widths here in the description as well as rounding errors.):

 * There are two constants:
   - float_tab_min_w (former tab_min_w, default 100)
       ... that is soft minimum width
   - propor_tab_min_w (new, default 30, I did not write code for
       redefinig) ... that is hard minimum width
 Let "n" be number of tabs.

 * If n*propor_tab_min_w is greater than space allocated to tabs
   ( ~ width of the frame in tiled workspace ), then tabs get sizes
   proportionaly like it was before this patch.

   |<--->| ... propor_tab_min_w
   _____________________________________________
   |T|A|B|S|_|T|H|A|T|_|A|R|E|_|T|O|O|_|M|A|N|Y|
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 * Else each tab gets at least propor_tab_min_w (even if it contains
   shorter label).

 * Now we set max_width to propor_tab_min_w, and we keep incrementing
   max_width until all tabs have their width or we use all the space
   reserved for tabs. In the latter case we stop and assign the widths
   to the tabs.

   |<--->| ... propor_tab_min_w
   |<-------------->| ... float_tab_min_w
   _____________________________________________
   | tab |  x  |a long ta...|another l...| xx  |
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 * At this point all tabs have sizes of their labels (but at least
   propor_tab_min_w), but we may have some space left, now
   flow_tab_min_w comes to play. We set min_width to propor_tab_min_w
   and again we increment it until float_tab_min_w or all the space
   reserved for tabs is used. In the latter case we again stop and
   assign the widths.

   |<-------------->| ... float_tab_min_w
   _____________________________________________
   |here is a long tab|    tab    |     x      |
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


 * Now, each tab has at least float_tab_min_w width. If the frame is
   floating, then the tab sizes are used. If not, then remaining space
   is distributed among tabs, where we want padded space to be same
   for all tabs (but we don't shorten tabs that have float_tab_min_w
   width)

   |<-------------->| ... float_tab_min_w
   tiled:
   _____________________________________________
   |      tab       |    here is a long tab    |
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   float:
   _______________________________________
   |      tab       | here is a long tab |
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


-- 
diff -rN -ubp old-ion-3plus/ioncore/frame.c new-ion-3plus/ioncore/frame.c
--- old-ion-3plus/ioncore/frame.c	2009-03-15 11:57:04.000000000 +0100
+++ new-ion-3plus/ioncore/frame.c	2009-03-15 11:57:04.000000000 +0100
@@ -100,7 +100,8 @@ bool frame_init(WFrame *frame, WWindow *
     frame->brush=NULL;
     frame->bar_brush=NULL;
     frame->mode=mode;
-    frame->tab_min_w=0;
+    frame->float_tab_min_w=0;
+    frame->propor_tab_min_w=0;
     frame->bar_max_width_q=1.0;
     
     gr_stylespec_init(&frame->baseattr);
@@ -282,29 +283,61 @@ int frame_nth_tab_x(WFrame *frame, int n
 }
 
 
+void frame_get_max_width_and_elastic(WFrame * frame,int bar_w,int *maxw,int *elastic,int *minw);//FIXME
+
 static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
 {
     WRectangle bg;
     GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
     int m=FRAME_MCOUNT(frame);
+    int maxw,elastic,textw,minw;
     uint w;
+    WRegion *sub;
+    const char * p;
     
-    frame_bar_geom(frame, &bg);
+//fprintf(stderr,"HERE\n");
 
-    if(m==0)
-        m=1;
+    frame_bar_geom(frame, &bg);
 
     if(frame->bar_brush!=NULL)
         grbrush_get_border_widths(frame->bar_brush, &bdw);
     
+    if(m==0){
+	m=1;
+	w=bg.w-bdw.left-bdw.right;
+    }else{
+	frame_get_max_width_and_elastic(frame, bg.w, &maxw, &elastic, &minw);
+	if ((maxw > 0) && (maxw <= frame->propor_tab_min_w)) {
     /* Remove borders */
     w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
-    
+//fprintf(stderr,"HEREmin maxw:%i minw:%i totalw:%i pad:%i w:%i (????)\n",maxw,minw,bg.w,elastic,w);
     if(w<=0)
         return 0;
-    
     /* Get n:th tab's portion of free area */
     w=(((n+1)*w)/m-(n*w)/m);
+	}else{
+    
+	    /* Get n:th tab's portion of elastic area */
+	    elastic=(((n+1)*elastic)/m-(n*elastic)/m);
+
+	    sub = mplex_mx_nth((WMPlex*)frame, n);
+	    p=region_displayname(sub);
+	    if(p==NULL)
+		textw=0;
+	    else
+		textw=grbrush_get_text_width(frame->bar_brush,
+					     p, strlen(p));
+	    if (textw<minw)
+		textw=minw;
+	    if (textw<frame->propor_tab_min_w)
+		textw=frame->propor_tab_min_w;
+
+	    if (maxw>0 && (textw>maxw))
+		textw=maxw;
+	    w=elastic+textw;
+//fprintf(stderr,"HEREsub %p maxw:%i minw:%i totalw:%i pad:%i w:%i (%s)\n",(void *)sub,maxw,minw,bg.w,elastic,w,p);
+	}
+    }
     
     /* Add n:th tab's borders back */
     if(!inner){
@@ -312,6 +345,7 @@ static int frame_nth_tab_w_iw(WFrame *fr
         w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
     }
             
+//fprintf(stderr,"THERE\n");
     return w;
 }
 
diff -rN -ubp old-ion-3plus/ioncore/frame-draw.c new-ion-3plus/ioncore/frame-draw.c
--- old-ion-3plus/ioncore/frame-draw.c	2009-03-15 11:57:04.000000000 +0100
+++ new-ion-3plus/ioncore/frame-draw.c	2009-03-15 11:57:04.000000000 +0100
@@ -225,10 +225,11 @@ void frame_clear_shape(WFrame *frame)
 
 #define CF_TAB_MAX_TEXT_X_OFF 10
 
+void frame_get_max_width_and_elastic(WFrame * frame,int bar_w,int *maxw,int *elastic, int *minw);//FIXME
 
 static void frame_shaped_recalc_bar_size(WFrame *frame, bool complete)
 {
-    int bar_w=0, textw=0, tmaxw=frame->tab_min_w, tmp=0;
+    int bar_w=0, textw, w, tmaxw, tminw, tmp=0;
     WLListIterTmp itmp;
     WRegion *sub;
     const char *p;
@@ -241,43 +242,41 @@ static void frame_shaped_recalc_bar_size
         return;
     
     m=FRAME_MCOUNT(frame);
+    bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
     
     if(m>0){
-        grbrush_get_border_widths(frame->bar_brush, &bdw);
-        bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)
-                 +bdw.right+bdw.left);
+
+	frame_get_max_width_and_elastic(frame, bar_w, &tmaxw, &tmp, &tminw);
+//fprintf(stderr," * WID total:%i maxw:%i minw:%i elastic:%i m:%i\n",bar_w,tmaxw,tminw,tmp,m);
+	if ((tmaxw < 0) && (tminw == frame->float_tab_min_w)) {
+	    /* No label truncation needed, good. See how much can be padded. */
+	    w=bar_w-tmp;
 
         FRAME_MX_FOR_ALL(sub, frame, itmp){
             p=region_displayname(sub);
             if(p==NULL)
                 continue;
             
-            textw=grbrush_get_text_width(frame->bar_brush,
-                                         p, strlen(p));
-            if(textw>tmaxw)
-                tmaxw=textw;
+		textw=2*CF_TAB_MAX_TEXT_X_OFF+
+		      grbrush_get_text_width(frame->bar_brush,
+					     p, strlen(p))-
+		      frame->float_tab_min_w;
+//fprintf(stderr," * WID addtextw:%i\n",textw);
+		if (textw>=2*CF_TAB_MAX_TEXT_X_OFF)
+		    w+=2*CF_TAB_MAX_TEXT_X_OFF;
+		else if (textw >=0)
+		    w+=textw;
         }
+	    if (bar_w>w)/*Padded to much*/
+		bar_w=w;
 
-        bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
-        if(bar_w<frame->tab_min_w && 
-           REGION_GEOM(frame).w>frame->tab_min_w)
-            bar_w=frame->tab_min_w;
-        
-        tmp=bar_w-bdtotal-m*tmaxw;
-        
-        if(tmp>0){
-            /* No label truncation needed, good. See how much can be padded. */
-            tmp/=m*2;
-            if(tmp>CF_TAB_MAX_TEXT_X_OFF)
-                tmp=CF_TAB_MAX_TEXT_X_OFF;
-            bar_w=(tmaxw+tmp*2)*m+bdtotal;
-        }else{
+	} else {
             /* Some labels must be truncated */
         }
+
     }else{
-        bar_w=frame->tab_min_w;
-        if(bar_w>frame->bar_max_width_q*REGION_GEOM(frame).w)
-            bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
+	if(bar_w>frame->float_tab_min_w)
+	    bar_w=frame->float_tab_min_w;
     }
 
     if(complete || frame->bar_w!=bar_w){
@@ -302,9 +301,133 @@ static int init_title(WFrame *frame, int
 }
 
 
+/* Proportional tabs algorithm:
+  * Sort tabs by text sizes.
+  * From smallest to largest do:
+    * Compare current tab width with remaining width (incl. current)
+      divided by no. of remaining tabs (incl. current).
+      * If larger or equal, then set maximum width to the number computed
+	above and set elastic space to zero (resp. to remain after division).
+	(Return.)
+      * If smaller, subctract current width from remaining width and
+	subtract one from no. of remaining tabs. If this is the last
+	tab, then set additional padding to remaining width divided
+	by total number of tabs.
+
+  Do not use w for shaped frames.
+*/
+void frame_get_max_width_and_elastic(WFrame * frame,int bar_w,int *maxw,int *elastic, int *minw){
+    /* Dummy implementation O(n^2), instead of O(n*log(n)) */
+    int textw=0,tmp,tmaxw,tminw=frame->propor_tab_min_w;
+    WLListIterTmp itmp;
+    WRegion *sub;
+    const char *p;
+    GrBorderWidths bdw;
+    //char *title;
+    uint bdtotal,curw,nextw;
+    int i, m, n;
+
+    m=FRAME_MCOUNT(frame);
+    *minw=0;
+    
+//    if(frame->bar_brush==NULL)
+//        *elastic=0;
+//        *maxw=-1;
+//fprintf(stderr,"ZERR\n");
+//        return;
+    if(m>0){
+	grbrush_get_border_widths(frame->bar_brush, &bdw);
+	bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)
+		 +bdw.right+bdw.left);
+	tmp = bar_w - bdtotal;
+//fprintf(stderr,"TMPST:%i barw:%i bdt:%i\n",tmp,bar_w,bdtotal);
+       
+	curw=0;
+	n=m;
+	
+	while (n>0) {
+	    nextw=(uint)-1;/*FIXME: MAXINT*/
+	    if ((signed)curw*n >= tmp) {
+		/*Remainig tabs are too large => equal width.*/
+		*maxw=tmp/n;
+		*elastic=tmp-(*maxw)*n;
+//fprintf(stderr,"TRUNC maxw:%i elastic:%i tmp:%i n:%i\n",*maxw,*elastic,tmp,n);
+		return;
+	    }
+	    FRAME_MX_FOR_ALL(sub, frame, itmp){
+		p=region_displayname(sub);
+		if(p==NULL)
+		    continue;
+		
+		textw=grbrush_get_text_width(frame->bar_brush,
+					     p, strlen(p));
+		if (textw<tminw)
+		    textw=tminw;
+		if((unsigned)textw == curw){
+//fprintf(stderr,"TW:%i (%s)\n",textw,p);
+		    tmp-=textw;
+		    n--;
+		} else if((unsigned)textw>curw){
+		    if ((unsigned)textw<nextw)
+			nextw=textw;
+		}
+	    }
+	    curw = nextw;
+	}
+//fprintf(stderr,"TMP elastic:%i\n",tmp);
+
+	n=0;
+	curw=0;
+	/*We have some padding left. Try to enlarge small tabs*/
+	while (n<m) {
+	    nextw=(uint)-1;/*FIXME: MAXINT*/
+	    FRAME_MX_FOR_ALL(sub, frame, itmp){
+		p=region_displayname(sub);
+		if(p==NULL)
+		    continue;
+		
+		textw=grbrush_get_text_width(frame->bar_brush,
+					     p, strlen(p));
+		if (textw<tminw)
+		    textw=tminw;
+		if((unsigned)textw == curw){
+		    n++;
+		} else if((unsigned)textw>curw){
+		    if ((unsigned)textw<nextw)
+			nextw=textw;
+		}
+	    }
+	    if (nextw>(unsigned)frame->float_tab_min_w)
+	      nextw=frame->float_tab_min_w;
+//fprintf(stderr,"TMP --- tmp:%i n:%i curw:%i nextw:%i min:%i\n",tmp,n,curw,nextw,*minw);
+	    if (n*(nextw-curw)<(unsigned)tmp) {
+		/*we can extend small tabs to 'nextw'*/
+		*minw=nextw;
+		tmp-=n*(nextw-curw);
+//fprintf(stderr,"TMPSUBGO tmp:%i n:%i curw:%i nextw:%i min:%i\n",tmp,n,curw,nextw,*minw);
+	    } else {
+		/*we can extend small tabs only to 'curw+tmp/n'*/
+		*minw+=tmp/n;
+		tmp-=(*minw-curw)*n;
+//fprintf(stderr,"TMPSUBBRK tmp:%i n:%i curw:%i nextw:%i min:%i\n",tmp,n,curw,nextw,*minw);
+		break;
+	    }
+	    if (nextw==(unsigned)frame->float_tab_min_w)
+		break;
+	    curw=nextw;
+	}
+	
+//fprintf(stderr,"TMP elastic:%i min:%i\n",tmp,*minw);
+	*elastic=tmp;
+    } else {
+	*elastic=0;
+    }
+    *maxw=-1;
+}
+
 void frame_recalc_bar(WFrame *frame, bool complete)
 {
-    int textw, i;
+    int textw, i, maxw, padding;
     WLListIterTmp tmp;
     WRegion *sub;
     char *title;
@@ -357,7 +480,8 @@ void frame_draw_bar(const WFrame *frame,
     grbrush_init_attr(frame->bar_brush, &frame->baseattr);
     
     grbrush_draw_textboxes(frame->bar_brush, &geom, frame->titles_n, 
-                           frame->titles, complete);
+			   //frame->titles, complete);
+			   frame->titles, TRUE);
     
     grbrush_end(frame->bar_brush);
 }
@@ -427,14 +551,17 @@ void frame_brushes_updated(WFrame *frame
         frame->bar_h=bdw.top+bdw.bottom+fnte.max_height;
     }
     
+    //FIXME
+    frame->propor_tab_min_w=30;
+
     /* shaped mode stuff */
-    frame->tab_min_w=100;
+    frame->float_tab_min_w=100;
     frame->bar_max_width_q=0.95;
     
     if(grbrush_get_extra(frame->brush, "floatframe_tab_min_w",
-                         'i', &(frame->tab_min_w))){
-        if(frame->tab_min_w<=0)
-            frame->tab_min_w=1;
+			 'i', &(frame->float_tab_min_w))){
+	if(frame->float_tab_min_w<=0)
+	    frame->float_tab_min_w=1;
     }
     
     if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", 
diff -rN -ubp old-ion-3plus/ioncore/frame.h new-ion-3plus/ioncore/frame.h
--- old-ion-3plus/ioncore/frame.h	2009-03-15 11:57:04.000000000 +0100
+++ new-ion-3plus/ioncore/frame.h	2009-03-15 11:57:04.000000000 +0100
@@ -76,7 +76,8 @@ DECLCLASS(WFrame){
     WFrameBarMode barmode;
     int bar_w, bar_h;
     double bar_max_width_q;
-    int tab_min_w;
+    int float_tab_min_w;
+    int propor_tab_min_w;
 };
 
 
  • Proportional tab sizes (ugly patch). ebik