From: Tomáš E. <eb...@dr...> - 2011-02-07 03:51:15
|
From: Tomáš Ebenlendr <eb...@uc...> Proportional and elastic algorithms for calculating widths of tabs. --- ioncore/frame-tabs-recalc.c | 195 +++++++++++++++++++++++++++++++++++++++++++ ioncore/frame-tabs-recalc.h | 10 ++- 2 files changed, 204 insertions(+), 1 deletions(-) diff --git a/ioncore/frame-tabs-recalc.c b/ioncore/frame-tabs-recalc.c index b6aba01..f2b6679 100644 --- a/ioncore/frame-tabs-recalc.c +++ b/ioncore/frame-tabs-recalc.c @@ -7,6 +7,7 @@ * See the included file LICENSE for details. */ #include <string.h> +#include <stdlib.h> /* qsort */ #include "common.h" #include "names.h" @@ -142,10 +143,197 @@ static bool tab_sizes_equal(WFrame * frame, bool complete) /*}}}*/ +/*{{{ Proportional/Elastic tab sizes algorithms + * This gives tabs sizes proportional to the contained text. + */ + +/* Failsafes to 'equal' algorithm if there are more tabs. + * The result will be same in most cases of many tabs anyway. + */ +#define PROPOR_MAX_TABS 30 + +static int intcompare (const void *a, const void *b) { + return *(int *)a-*(int *)b; +} + +static bool tab_sizes_propor_elastic(WFrame * frame, bool complete, bool proportional) +{ + int bar_w, titles_total=0; + int titles_padded_total1=0, titles_padded_total2=0; + WRectangle bg; + GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT; + int i, m=frame->titles_n; + int iw, min_w=0, max_w, w; + int sorted_sizes[PROPOR_MAX_TABS]; + uint bdtotal; + + if (m>PROPOR_MAX_TABS) + return tab_sizes_equal(frame, complete); + + + frame_bar_geom(frame, &bg); + grbrush_get_border_widths(frame->bar_brush, &bdw); + + if (frame->barmode==FRAME_BAR_SHAPED) { + bar_w=frame->tabs_params.bar_max_width_q*REGION_GEOM(frame).w; + } else { + bar_w=bg.w; + frame->bar_w=bar_w; + } + + get_titles_text_width(frame); + + /* Calculate thresholds. */ + for (i=0;i<m;i++) { + iw=sorted_sizes[i]=frame->titles[i].iw; + titles_total+= + (iw<frame->tabs_params.propor_tab_min_w ? + frame->tabs_params.propor_tab_min_w : iw); + titles_padded_total1+= + (iw < frame->tabs_params.propor_tab_min_w-2*frame->tabs_params.requested_pad ? + frame->tabs_params.propor_tab_min_w : iw+2*frame->tabs_params.requested_pad); + titles_padded_total2+= + (iw < frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad ? + frame->tabs_params.tab_min_w : iw+2*frame->tabs_params.requested_pad); + } + bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing) + +bdw.right+bdw.left); + w=bar_w-bdtotal; + + /* Do different things based on thresholds. */ + if (m*frame->tabs_params.propor_tab_min_w>=w) { + /*Equal sizes (tiny tabs). Base width is zero.*/ + for(i=0;i<m;i++) + frame->titles[i].iw=0; + } else if (titles_total>=w) { + /*Truncate long tabs.*/ + qsort(sorted_sizes, m, sizeof(int), intcompare); + min_w=frame->tabs_params.propor_tab_min_w; + max_w=sorted_sizes[m-1];titles_total=0; + for(i=0;i<m;i++){ + if (sorted_sizes[i]>frame->tabs_params.propor_tab_min_w) + break; + titles_total+=frame->tabs_params.propor_tab_min_w; + } + for(;i<m;i++){ + if (titles_total+sorted_sizes[i]*(m-i)>=w) { + max_w=(w-titles_total)/(m-i); + break; + } + titles_total+=sorted_sizes[i]; + } + for(i=0;i<m;i++) + if (frame->titles[i].iw>max_w) + frame->titles[i].iw=max_w; + } else if (titles_padded_total1>=w) { + /*Just a little padding. + *Pad equally, no tab shorter than tabs_params.propor_tab_min_w-1. + */ + max_w=frame->tabs_params.propor_tab_min_w; +equal_pad: + qsort(sorted_sizes, m, sizeof(int), intcompare); + titles_total=m*max_w; + i=m-1;min_w=0; + while((i>=0) && + (sorted_sizes[i]>=max_w)){ + titles_total+=sorted_sizes[i]; + i--; + } + while(i>=0){ + /*Test padding by max_w-sorted_sizes[i].*/ + if(titles_total-(m-i-1)*sorted_sizes[i]>=w){ + min_w=(titles_total-w)/(m-i-1); + break; + } + titles_total+=sorted_sizes[i]; + i--; + } + /* After expanding to min_w: equal padding will extend short tabs to + * required size (+- 1pixel). + */ + + } else if (titles_padded_total2>=w) { + /* Expand as many short tabs as possible to equal size, + * they will be shorter than tabs_params.tab_min_w anyway. + * Long tabs should be padded by 2*tabs_params.requested_pad. + */ +equal_tab: + qsort(sorted_sizes, m, sizeof(int), intcompare); + min_w=0;titles_total=2*m*frame->tabs_params.requested_pad; + for(i=m-1;i>=0;i--){ + if (sorted_sizes[i]*(i+1)+titles_total<=w){ + min_w=(w-titles_total)/(i+1); + break; + } + titles_total+=sorted_sizes[i]; + } + /* After expanding to min_w: it should remain + * 2*m*tabs_params.requested_pad +- m + */ + } else if (frame->barmode==FRAME_BAR_SHAPED) { + /* Shorter bar. */ + w=titles_padded_total2; + bar_w=w+bdtotal; + min_w=frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad; + /* After expanding to min_w: it should remain + * 2*m*tabs_params.requested_pad exactly + */ + } else { + if(proportional){ + /* Pad equally, no tab shorter than tabs_params.tab_min_w-1. */ + max_w=frame->tabs_params.tab_min_w; + goto equal_pad; + }else{ + /* Expand as many short tabs as possible to equal size, + * Long tabs should be padded by 2*tabs_params.requested_pad. + */ + goto equal_tab; + } + } + if (min_w>0) { + for(i=0;i<m;i++) + if (frame->titles[i].iw<min_w) + frame->titles[i].iw=min_w; + } + + + /* Calculate remaining space */ + titles_total=0; + for(i=0;i<m;i++) + titles_total+=frame->titles[i].iw; + w-=titles_total; + /* Distribute remaining space equally up to 1 pixel */ + for(i=0;i<m;i++) + frame->titles[i].iw+=((i+1)*w)/m-(i*w)/m; + + if((frame->bar_w!=bar_w)){ + frame->bar_w=bar_w; + return TRUE; + } + return complete; +} + + +static bool tab_sizes_proportional(WFrame * frame, bool complete) +{ + return tab_sizes_propor_elastic(frame, complete, TRUE); +} + +static bool tab_sizes_elastic(WFrame * frame, bool complete) +{ + return tab_sizes_propor_elastic(frame, complete, FALSE); +} + + + +/*}}}*/ + DECLFUNPTRMAP(TabCalcPtr); static StringTabCalcPtrMap frame_tabs_width_algorithms[] = { { "equal", tab_sizes_equal }, + { "elastic", tab_sizes_elastic }, + { "proportional", tab_sizes_proportional }, END_STRINGPTRMAP }; @@ -156,6 +344,7 @@ static TabCalcPtr default_frame_tabs_width_algorithm=tab_sizes_equal; static void param_init(TabCalcParams *pars) { pars->tab_min_w=100; + pars->propor_tab_min_w=50; pars->bar_max_width_q=0.95; pars->requested_pad=10; pars->alg=default_frame_tabs_width_algorithm; @@ -177,6 +366,12 @@ void frame_tabs_calc_brushes_updated(WFrame *frame) pars->tab_min_w=1; } + if(grbrush_get_extra(frame->brush, "frame_propor_tab_min_w", + 'i', &(pars->propor_tab_min_w))){ + if(pars->propor_tab_min_w<=0) + pars->propor_tab_min_w=1; + } + if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", 'd', &(pars->bar_max_width_q))){ if(pars->bar_max_width_q<=0.0 || pars->bar_max_width_q>1.0) diff --git a/ioncore/frame-tabs-recalc.h b/ioncore/frame-tabs-recalc.h index 2472aac..2c60701 100644 --- a/ioncore/frame-tabs-recalc.h +++ b/ioncore/frame-tabs-recalc.h @@ -43,10 +43,18 @@ DECLSTRUCT(TabCalcParams){ TabCalcPtr alg; /* Maximum size of shaped bar. */ double bar_max_width_q; - /* Requested size of the tab. */ + /* Minimum width of a tab in shaped frame. + * For 'proportional' and 'elastic' algorithms also minimum width of a tab + * provided that no title has to be truncated + */ int tab_min_w; /* Requested empty space to be added before and after text. */ int requested_pad; + /* Minimum width of a tab for 'proportional' and 'elastic' algorithms. + * Long titles will be truncated instead of shortening a short tab below + * this length. + */ + int propor_tab_min_w; }; void frame_tabs_calc_brushes_updated(WFrame *frame); -- 1.7.1 |