#include "../include/xcv_license.h" //PCH namespace Widgets95 {//-. //<-' //Adding this logic for _tick_cb with do_click in order to //not repeat on the first click until held down for a time. extern int xcv_spinner_cogwheel; /****************************** WIDGETS_95_Scrollbar::common_construct() **********/ void ui::bar::_members_init() { //_horizontal = true; //h = ui_bar_arrow_size; //w = ui_wordproc_span; if(_horizontal) { _h = ui_bar_arrow_size; _w = ui_textbox_span; //ui_wordproc_span } else { _h = ui_textbox_span; //ui_wordproc_drop _w = ui_bar_arrow_size; } _alignment = center; _placement = right; //_x_off = 0; //_y_off_top = 0; //_y_off_bot = 0; _can_activate = true; _space_clicks = false; //2019 _val_end = _val_start = 0; associated_object = nullptr; //_last_update_time = 0; //_velocity_limit = 50; /* Change value by at most 50 per second */ //_box_length = 0; //_box_start_position = 0; //_box_end_position = 0; //_track_length = 0; switch(live_type) { default: _data_type = spin_int; break; case ui_live_float: case ui_live_double: _data_type = spin_float; break; } // make sure limits are wide enough to hold live value if(_ptr_val) live_type->init_live(); double lo = 0; double hi = _data_type==spin_float?1:100; if(_ptr_val) lo = std::min(lo,_float_val); if(_ptr_val) hi = std::max(hi,_float_val); set_range(lo,hi); } int ui::bar::midpoint() //2022 { int m = _horizontal?_x_lr:_y_off_top; //NOTE: It's important this lands on an //odd pixel that's centered. return _box_pos()+ui_bar_arrow_size*3/2; } inline int ui::bar::_box_len() { if(!empty()/*&&_enabled*/) { //NOTE: midpoint ASSUMES THIS IS SO. const int min_tab = ui_bar_arrow_size; ////box_length = int(track_length/double(visible_range)); ////if(box_lengthactivate(ACTIVATE_MOUSE); } _state = state_none; redraw(); return false; //2019 } /****************************** WIDGETS_95_Scrollbar::mouse_down_handler() **********/ bool ui::bar::_mouse_down_handler(int local_x, int local_y) { /*Prefer there be a tactile response. if(empty()) return true; //2019*/ //last_update_time = WIDGETS_95_Time()-1; _state = find_arrow(local_x,local_y); if(!_state) return true; //_x_lr? if(needs_idle()) e::xcv_setIdleFuncIfNecessary(true); /* printf("bar: mouse down : %d/%d arrow:%d\n",local_x,local_y, find_arrow(local_x,local_y)); */ if(_state!=state_up&&_state!=state_down) { return false; //return true; //??? } _reset_growth(); /*** ints and floats behave a bit differently. When you click on an int spinner, you expect the value to immediately go up by 1, whereas for a float it'll go up only by a fractional amount. Therefore, we go ahead and increment by one for int spinners ***/ //#if 1 /*2019: I think do_click implements this logic? if(_data_type==spin_int) { // Allow for possibility of reversed limits int lo = val_min(); int hi = val_max(); int increase = int_min=lo&&new_val<=hi&&new_val!=int_val) { set_int_val(new_val); do_callbacks(); //??? } }*/ //#endif xcv_spinner_cogwheel = 0; do_click(); //2019: This is new... it should probably be GLUT_ELAPSED_TIME based. xcv_spinner_cogwheel = 45; return false; } /***************************** WIDGETS_95_Scrollbar::mouse_held_down_handler() ******/ bool ui::bar::_mouse_held_down_handler(int local_x, int local_y, bool inside) { //This interferes with being able to hover in and out of the buttons boxes. //if(_state==state_none) return false; /* printf("bar: mouse held: %d/%d inside: %d\n",local_x,local_y,inside); */ if(_state==state_scroll) /* dragging? */ { do_drag(local_x-_x_abs-_x_lr,local_y-_y_abs-_y_off_top); } else /* not dragging */ { //_idle just calls do_click... though I'm uncertain why it's required if the //mouse is stationary. (It can cleanup when GLUT fails to send the up event.) //int new_state = find_arrow(local_x,local_y); //if(new_state==_state) { /** Still in same arrow **/ do_click(); } } return false; } /****************************** WIDGETS_95_Scrollbar::draw() **********/ void ui::bar::_draw() { bool horz = _horizontal; int y = _y_off_top; //0 int xlr = _x_lr; int yob = _y_off_bot; int bsp = _box_pos(); int box = _box_len(); int bep = bsp+box; //(_enabled?box:0); int sas = ui_bar_arrow_size; int run = (horz?_w-_x_rl:_h-yob); int opp = (horz?_h-y-yob:_w-xlr-_x_rl); int bar = run-2*sas; //Darkening somewhat to make it distinguisable from //the white on the buttons. Maybe if the track were //not white it wouldn't be an issue? enum{ shadow=233-10, white=255-10 }; int sh1 = state_up&_state?0:shadow; int sh2 = bspsas) xoff+=(w-sas)/2; if(h>sas) yoff+=(h-sas)/2; gl::glTranslated(xoff,yoff+press,0); window::draw_color(enable?0:255); gl::glBegin(GL_TRIANGLES); gl::glVertex2f(tri[0],tri[1]); gl::glVertex2f(tri[2],tri[3]); gl::glVertex2f(tri[4],tri[5]); gl::glEnd(); gl::glTranslated(-xoff,-yoff-press,0); if(!enable) // once more! { xoff-=1; yoff-=1; //offset-=1; gl::glTranslated(xoff,yoff,0); window::draw_color(128); gl::glBegin(GL_TRIANGLES); gl::glVertex2f(tri[0],tri[1]); gl::glVertex2f(tri[2],tri[3]); gl::glVertex2f(tri[4],tri[5]); gl::glEnd(); gl::glTranslated(-xoff,-yoff,0); } } void ui::bar::draw_scroll_arrow(int arrowtype, int x, int y, int sas) { xcv_bar_arrow(arrowtype,x,y,_state,_enabled,sas); } void ui::bar::draw_scroll(int xx, int yy, int ww, int hh, int checker, int shadow) { //2019: Adding parameter list. int x0 = xx, y0 = yy, x1 = xx+ww, y1 = yy+hh; int colors = checker; //2021: Trying to avoid OpenGL. if(shadow&&ww&&hh) colors|=shadow<<(_horizontal?8:16); //I developed this to make the white pixel not to //extend the white highlight on the button. But a //bigger defect is the gray pixel on the opposite //side seems to carve out a notch. A shadow helps //for the white highlight problem. //int odd = _y_abs%2?32/CHAR_BIT:0; if(_y_abs%2) colors = -colors; //??? window::draw_dotted_rect(x0,y0,x1,y1,colors); } /****************************** WIDGETS_95_Scrollbar::draw_scroll_box() **********/ void ui::bar::draw_scroll_box(int x, int y, int w, int h) { //NOTE: Both cases are functionally disabled. //draw_raised_box can't draw a zero-size box. //if(!_enabled||empty()) return; if(w<6||h<6) return; window::draw_raised_rect(x,y,x+w,y+h); if(this==e::get_active()) { //2019: spin_interface::_activate is preventing tab //navigation. Page, Arrow, Home, End should be used. if(!associated_object||_state==state_scroll) { if(_state==state_scroll) { //NOTE: I developed this alternative because //draw_active_rect wouldn't work. But I made //it work by increasing it to 17. Still this //is useful I think. //2022: THIS IS SENSITIVE TO Xming_x_server_y //BEING POSTIVE AND NONZERO, IF ONLY A LITTLE. draw_scroll(x+3,y+3,w-6,h-6,96,0); } else { //REMINDER: My Intel chipset glitches out if //ui_bar_arrow_size is not odd. button is //fine with even sizes, though it looks best //if they can be odd. I guess it's too small. window::draw_active_rect(x+3,y+3,x+w-3,y+h-3,false); } } } } /************************************ WIDGETS_95_Scrollbar::find_arrow() ************/ int ui::bar::find_arrow(int local_x, int local_y) { int x = local_x-=_x_abs+_x_lr, y = local_y-=_y_abs+_y_off_top; if(x<0) return state_none; //_x_lr? int hh = _h, ww = _w; if(_horizontal) { std::swap(x,y); std::swap(hh,ww); } //Not sure if this is correct... but it is getting in the way //of (new) non-square sliders. //if(x>=ww-ui_bar_arrow_size-3&&x<=ww) { int pos = ui_bar_arrow_size+_box_pos(); if(y>=0&&y<=pos) { //FIX ME //return state_up; return _horizontal?state_down:state_up; //WHY DIFFER????? } if(y>=pos+_box_len()&&y<=(hh+ui_bar_arrow_size)) { //FIX ME //return state_down; return _horizontal?state_up:state_down; //WHY DIFFER????? } return state_scroll; } return state_none; } /***************************************** WIDGETS_95_Scrollbar::do_click() **********/ void ui::bar::do_click() { //NEW: Reevaluate "_state" so box interrupts holding //down the tracks. if(e.curr_button_down) { if(_state!=state_scroll) { int swap = find_arrow(e.curr_x,e.curr_y); /*if(0&&swap==state_scroll) { _state = state_none; return; }*/ if(swap!=_state) { //TODO? Try to stop box on cursor's center. //NEW: Prevent fighting in extreme cases where //box is smaller than spaces between positions. if((swap==state_up||swap==state_down) &&(_state==state_up||_state==state_down)) { swap = state_scroll; } } _state = swap; } } else //NEW: In case GLUT fails to send mouse-up event. { _state = state_none; redraw(); return; } //NOTE: Adding so _tick_cb doesn't repeat initially. if(xcv_spinner_cogwheel--) return; xcv_spinner_cogwheel = 10; int direction = 1; if(_state==state_down) { direction = -1; } else if(_state!=state_up) { //direction = 0; //NONE, BOTH, SCROLL? //NEW: No movement? May as well return unless the //bar needs inertia (not implemented below.) return; } if(_val_start>_val_end) direction*=-1; double cmp = _float_val, new_val = cmp; if(_tick_cb) { double t = new_val; int i = _tick_cb(this,-1,new_val,true); if(i==-1) goto _1; //NEW: This condition is designed to go to the next //valid tick when the current value isn't on a tick. if(direction<0?new_val>=t:new_val<=t) _tick_cb(this,i+direction,new_val,true); } else _1: { double incr = direction; //NEW: Use direction only for unbounded (0~INT_MAX) value. if(_speed) incr*=_increase_growth()*_speed; //2019: I don't see a significant difference here, so I'm //knocking this feature out, because I think tuning it is //probably a souce of bad user-experience. //double et = WIDGETS_95_Time(); //double frame_time = et-last_update_time; //last_update_time = et; //double frame_limit = velocity_limit*frame_time; //if(incr>frame_limit) //incr = frame_limit; /* don't scroll faster than limit */ new_val+=incr; //direction*incr if(_data_type==spin_int) //2024 { if((int)new_val==(int)cmp) { new_val = cmp+direction; //move at least 1 integer } } } set_val(new_val); //printf("do_click: incr %f val=%f _float_val=%f\n",incr,new_val,_float_val); if(cmp!=_float_val) do_callbacks(); } /***************************************** WIDGETS_95_Scrollbar::do_drag() **********/ void ui::bar::do_drag(int x, int y) { int box = _box_len(); int free_len = _track_len()-box; if(free_len==0) return; //zero-divide double new_val = _float_val; int hbox = box/2; int track_v = (_horizontal?x:y)-ui_bar_arrow_size; double same = (track_v-hbox)*(_val_end-_val_start)/double(free_len); //WHY DO THESE DIFFER IN THIS RESPECT??? new_val = _horizontal?_val_start+same:_val_end-same; double cmp = _float_val; if(_tick_cb) _tick_cb(this,-1,new_val,true); set_val(new_val); if(cmp!=_float_val) do_callbacks(); } /********************************** WIDGETS_95_Scrollbar::set_int_val() ************/ void ui::bar::_update_live() { //set_float_val //set_int_val { double new_val = _float_val; // Allow for the possibility that the limits are reversed double hi = val_max(); double lo = val_min(); if(new_val>hi) new_val = hi; if(new_val_w-c->_x_lr-c->_x_rl; } else _h = c->_h-c->_y_off_top-c->_y_off_bot; } } /*********************************** WIDGETS_95_Scrollbar::set_float_limits() *********/ spin_interface &spin_interface::set_range(double start, double end) { _val_start = start; _val_end = end; // Allow for possiblitly of reversed limits double lo = val_min(); if(_float_valhi) set_val(hi); redraw(); //HACK: just for scrollbar return *this; } //---. }//<-'