Subversion Repositories DIN Is Noise

Rev

Rev 2199 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* slider.h
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/



#ifndef __slider
#define __slider

#include "widget.h"
#include "viewwin.h"
#include "utils.h"
#include "filled_button.h"
#include "input.h"

#include <algorithm>
#include <vector>

extern int mousex;
extern viewport view;
extern int lmb;

template <typename S> struct after_slide_lis {
  virtual void slided (S& s) = 0;
};

template <typename S> struct start_slide_lis {
  virtual void start_slide (S& s) = 0;
};

template <typename T> struct values {
 
  T low, high, delta, val;
  float amount;

  values (T l = 0, T h = 1, T v = 0) {
    set_limits (l, h);
    *this = v;
  }
 
  operator T() {
    return ((T) val);
  }

  void set_limits (T l, T h) {
    low = l;
    high = h;
    delta = high - low;
  }
 
  void get_limits (T& l, T& h) {
    l = low;
    h = high;
  }
     
  T operator() () const { return val; }
  values& operator= (const T& v) {
    val = v;
    clamp<T> (low, val, high);
    if (delta) amount = (val - low) * 1.0f / delta; else amount = 0;
    return *this;
  }
 
  void set_amount (float a) {
    amount = a;
    clamp<float> (0.0f, amount, 1.0f);
    val = (T) (low + delta * amount);
  }
 
  float get_amount () const { return amount; }
 
};

template <typename T> struct slider : widget, move_listener {
 
  values<T> vx;
  int dx;
  std::vector<int> dxa;
 
  int sliding;
  int lmb_clicked;

  change_listener<slider> *chgl;
  after_slide_lis<slider> *asl;
  start_slide_lis<slider> *ssl;

  filled_button sizer;
  static const int spc = 10;
     
  slider (int sv = 0, int w = 64, int h = 16) : widget (0, 0, w, h), vx (0, 0, 0), sliding(0), lmb_clicked(0) {
    chgl = 0;
    asl = 0;
    set_sizer_visible (sv);
  }

  void set_sizer_visible (int sv) {
    sizer.visible = sv;
    if (sizer.visible) {
      sizer.set_moveable (1);
      sizer.movlis = this;
    } else {
      sizer.set_moveable (0);
      sizer.movlis = 0;
    }
  }

  void set_pos (int x, int y) {
    widget::set_pos (x, y);
    sizer.set_pos (extents.right + spc, extents.bottom);
  }

  void set_right (int r) {
    set_extents (extents.left, extents.bottom, r, extents.top);
    dx = vx.get_amount () * extents.width;
  }

  void set_size (int w) {
    set_extents (extents.left, extents.bottom, extents.left + w, extents.top);
    dx = vx.get_amount () * extents.width;
  }

  void moved () {
    int mpx = mousex - movr.prevx;
    if (mpx) {
      sizer.set_pos (mousex, extents.bottom);
      int msx = mousex - spc;
      if (msx > extents.left) set_right (msx);
    }
  }

  int handle_input () {
   
    widget::handle_input ();

    if (sizer.visible && sizer.handle_input ()) return 1;
   
    int ret = 0;

    if (lmb) {
      if (lmb_clicked == 0) {
        if (sliding) {
          sliding = 0;
          if (asl) asl->slided (*this);
          defocus (this);
          ret = 1;
        } else {
          if (hover) {
            sliding = 1;
            if (ssl) ssl->start_slide (*this);
            widget::focus = this;
            ret = 1;
          }
        }
        lmb_clicked = 1;
      }
    } else {
      if (sliding) {
        int nu_dx = mousex - extents.left;
        clamp (0, nu_dx, extents.width);
        if (nu_dx != dx) {
          dx = nu_dx;
          vx.set_amount (dx * extents.width_1);
          if (chgl) chgl->changed (*this);
        }
        if (keypressed(SDLK_f)) dxa.push_back (dx);
        if (keypressed(SDLK_BACKSPACE)) dxa.clear ();
      } else
        ret = 0;
      lmb_clicked = 0;
    }


    return ret;
   
  }

  void draw () {
   
    widget::draw ();

    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   
    const color& c = clr;
   
    glColor4f (c.r, c.g, c.b, (GLfloat) 0.25);
   
    const box<int>& e = extents;
      glRecti (e.left, e.bottom, e.right, e.top);
   
    glColor4f (c.r, c.g, c.b, GLfloat (1));
      glRecti (e.left, e.bottom, e.left + dx, e.top);

    glDisable (GL_BLEND);

    glColor3f (1, 1, 1);
    for (int i = 0, j = dxa.size (); i < j; ++i) {
      int dxi = extents.left + dxa[i];
      glBegin (GL_LINES);
        glVertex2i (dxi, extents.top + spc);
        glVertex2i (dxi, extents.bottom - spc);
      glEnd ();
    }

    if (sizer.visible) sizer.draw ();

  }

  void update () {
    float amount = vx.get_amount ();
    const box<int>& e = extents;
    dx = (int) (amount * e.width);  
  }
 
  void set_listener (change_listener<slider>* _chgl, start_slide_lis<slider>* _ssl = 0, after_slide_lis<slider>* _asl = 0) {
    chgl = _chgl;
    ssl = _ssl;
    asl = _asl;
  }
 
  T operator() () const {return vx (); }
 
  void set_val (const T& t) {
    vx = t;
    update ();
  }
 
  void set_limits (T l, T h) {
    vx = values<T> (l, h, vx());
    update ();
  }
 
  void get_limits (T& l, T& h) {
    vx.get_limits (l, h);
  }
};

#define MAKE_SLIDER_LISTENER(name,var) struct name : change_listener< slider<float> > { void changed (slider<float>& s); }; name var;
#define SLIDER_CHANGED(scope,name) void scope::name::changed (slider<float>& s)
#define MAKE_AFTER_SLIDE_LISTENER(name,type,var) struct name : after_slide_lis< type > { void slided (type& s); }; name var;
#define SLIDED(scope,name,type) void scope::name::slided (type& s)
#define MAKE_START_SLIDE_LISTENER(name,type,var) struct name : start_slide_lis< type > { void start_slide (type& s); }; name var;
#define START_SLIDE(scope,name,type) void scope::name::start_slide (type& s)

#endif