changeset 1926:5a0a49770247

Made a brand new CustTabs class (to allow fine control of tabs).
author Jorge Arellano Cid <jcid@dillo.org>
date Wed, 09 Mar 2011 14:20:19 -0300
parents 72bfd5e7edcd
children e7abafb75d0e
files src/ui.hh src/uicmd.cc
diffstat 2 files changed, 173 insertions(+), 323 deletions(-) [+]
line wrap: on
line diff
--- a/src/ui.hh	Wed Mar 09 14:18:58 2011 -0300
+++ b/src/ui.hh	Wed Mar 09 14:20:19 2011 -0300
@@ -35,7 +35,7 @@
 
 // Private classes
 class CustProgressBox;
-class CustTabGroup;
+class CustTabs;
 
 
 // Class definition ----------------------------------------------------------
@@ -115,7 +115,7 @@
 // UI class definition -------------------------------------------------------
 //
 class UI : public Fl_Pack {
-   CustTabGroup *Tabs;
+   CustTabs *Tabs;
    char *TabTooltip;
 
    Fl_Group *TopGroup;
@@ -172,8 +172,8 @@
    Fl_Widget *fullscreen_button() { return FullScreen; }
    void fullscreen_toggle() { FullScreen->do_callback(); }
 
-   CustTabGroup *tabs() { return Tabs; }
-   void tabs(CustTabGroup *tabs) { Tabs = tabs; }
+   CustTabs *tabs() { return Tabs; }
+   void tabs(CustTabs *tabs) { Tabs = tabs; }
    int pointerOnLink() { return PointerOnLink; }
    void pointerOnLink(int flag) { PointerOnLink = flag; }
 
--- a/src/uicmd.cc	Wed Mar 09 14:18:58 2011 -0300
+++ b/src/uicmd.cc	Wed Mar 09 14:20:19 2011 -0300
@@ -16,10 +16,11 @@
 #include <stdarg.h>
 #include <math.h>       /* for rint */
 
+#include <FL/Fl.H>
 #include <FL/Fl_Widget.H>
 #include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Tabs.H>
-#include <FL/Fl_Tooltip.H>
+#include <FL/Fl_Wizard.H>
+#include <FL/Fl_Box.H>
 
 #include "paths.hh"
 #include "keys.hh"
@@ -57,321 +58,193 @@
 
 
 //----------------------------------------------------------------------------
-#if 0
-
-#define BTN_W 25
-#define BTN_H 20
-
-static int btn_x;
 
 /*
- * Adds a tab-close button at the rightmost part
+ * CustTabs ---------------------------------------------------------------
  */
-class CustShrinkTabPager : public TabGroupPager {
-   bool btn_hl;
-   TabGroup *tg;
+
+/*
+ * stores the respective UI pointer
+ */
+class CustTabButton : public Fl_Button {
+   UI *ui_;
 public:
-   int update_positions(
-          TabGroup *g, int numchildren, int &selected,
-          int &cumulated_width, int &available_width,
-          int *tab_pos, int *tab_width);
-   virtual int which(TabGroup* g, int m_x,int m_y);
-   virtual TabGroupPager* clone() const;
-   virtual const char * mode_name() const {return "Shrink";}
-   virtual int id() const {return PAGER_SHRINK;}
-   virtual int available_width(TabGroup *g) const;
-   virtual bool draw_tabs(TabGroup* g, int selected, int* tab_pos,
-                          int* tab_width) {
-      if (!tg) tg = g;
-      if (g->children() > 1) {
-         fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
-         setcolor(btn_hl ? 206 : GRAY75);
-         fillrect(r);
-         if (btn_hl) {
-            setcolor(FL_WHITE);
-            strokerect(r);
+   CustTabButton (int x,int y,int w,int h, const char* label = 0) :
+      Fl_Button (x,y,w,h,label) { ui_ = NULL; };
+   void ui(UI *pui) { ui_ = pui; }
+   UI *ui(void) { return ui_; }
+};
+
+/*
+ * Allows fine control of the tabbed interface
+ */
+class CustTabs : public CustGroup {
+   int tab_w, tab_h, tab_n;
+   Fl_Wizard *Wizard;
+   int tabcolor_inactive, tabcolor_active, curtab_idx;
+public:
+   CustTabs (int ww, int wh, int th, const char *lbl=0) :
+      CustGroup(0,0,ww,th,lbl) {
+      tab_w = 80, tab_h = th, tab_n = 0, curtab_idx = -1;
+      tabcolor_active = FL_DARK_CYAN; tabcolor_inactive = 206;
+      //Fl_Box *w = new Fl_Box(tab_w,0,ww-tab_w,tab_h,"i n v i s i b l e");
+      Fl_Box *w = new Fl_Box(0,0,0,0,"i n v i s i b l e");
+      w->box(FL_NO_BOX);
+      resizable(0);
+      end();
+
+      Wizard = new Fl_Wizard(0,tab_h,ww,wh-tab_h);
+      Wizard->end();
+      add_new_tab(1);
+   };
+   int handle(int e);
+   UI *add_new_tab(int focus);
+   void remove_tab();
+   Fl_Wizard *wizard(void) { return Wizard; }
+   int get_btn_idx(UI *ui);
+   void switch_tab(CustTabButton *cbtn);
+   void prev_tab(void);
+   void next_tab(void);
+};
+
+int CustTabs::handle(int e)
+{
+   int ret = 0;
+
+   //printf("CustTabs::handle e=%s\n", fl_eventnames[e]);
+   if (e == FL_KEYUP) {
+      int k = Fl::event_key();
+      // We're only interested in some flags
+      unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
+      if (k == FL_Up || k == FL_Down || k == FL_Tab) {
+         ;
+      } else if (k == FL_Left || k == FL_Right) {
+         if (modifier == FL_SHIFT) {
+            (k == FL_Left) ? prev_tab() : next_tab();
+            ret = 1;
          }
-         setcolor(GRAY10);
-         //fltk::setfont(fltk::getfont()->bold(), fltk::getsize());
-         r.h(r.h()-2);
-         drawtext("X", r, ALIGN_CENTER);
-         return false;
-      } else {
-         // WORKAROUND: for http://fltk.org/str.php?L2062
-         // By returning true we avoid a call to TabGroup::draw_tab()
-         // in TabGroup::draw() in case we don't show the tabs.
-         return true;
+      } else if (modifier == FL_CTRL) {
+         if (k == ' ') {
+            //printf("CustTabs::handle FL_CTRL + Space\n");
+            //toggle_cb(NULL, Wizard->value());
+            ret = 1;
+         } else if (k == 't') {
+            add_new_tab(1);
+         } else if (k == 'q') {
+            remove_tab();
+         }
       }
    }
 
-   void btn_highlight(bool flag) {
-      if (btn_hl != flag) {
-         btn_hl = flag;
-         if (tg)
-            tg->redraw(DAMAGE_VALUE);
-      }
-   };
-   bool btn_highlight() { return btn_hl; };
-
-   CustShrinkTabPager() : TabGroupPager() {
-      noclip(true);
-      btn_hl = false;
-      tg = NULL;
-   }
-};
-
-int CustShrinkTabPager::available_width(TabGroup *g) const
-{
-   _MSG("CustShrinkTabPager::available_width\n");
-   int w = MAX (g->w() - this->slope()-1 - BTN_W, 0);
-   btn_x = w + 6;
-   return w;
+   return (ret) ? ret : CustGroup::handle(e);
 }
 
-int CustShrinkTabPager::which(TabGroup* g, int event_x,int event_y)
+/*
+ * Create a new tab with its own UI
+ */
+UI *CustTabs::add_new_tab(int focus)
 {
-   int H = g->tab_height();
-   if (!H) return -1;
-   if (H < 0) {
-      if (event_y > g->h() || event_y < g->h()+H) return -1;
-   } else {
-      if (event_y > H || event_y < 0) return -1;
-   }
-   if (event_x < 0) return -1;
-   int p[128], w[128];
-   int selected = g->tab_positions(p, w);
-   int d = (event_y-(H>=0?0:g->h()))*slope()/H;
-   for (int i=0; i<g->children(); i++) {
-      if (event_x < p[i+1]+(i<selected ? slope() - d : d)) return i;
+   char tab_label[64];
+
+   current(0);
+   UI *new_ui = new UI(0,tab_h,Wizard->w(),Wizard->h());
+   new_ui->tabs(this);
+   Wizard->add(new_ui);
+
+   int ntabs = children();
+   snprintf(tab_label, 64,"ctab%d", ++tab_n);
+   CustTabButton *btn = new CustTabButton((ntabs-1)*tab_w,0,tab_w,tab_h);
+   btn->copy_label(tab_label);
+   btn->clear_visible_focus();
+   btn->box(FL_PLASTIC_ROUND_UP_BOX);
+   btn->color(tabcolor_active);
+   btn->ui(new_ui);
+   add(btn);
+   btn->redraw();
+   //btn->callback(tab_btn_cb, this);
+
+   if (focus)
+      switch_tab(btn);
+   rearrange();
+
+   return new_ui;
+}
+
+void CustTabs::remove_tab()
+{
+   CustTabButton *btn;
+   UI *ui = (UI*)Wizard->value();
+
+   // the invisible box is also a child
+   if (children() == 2)
+      exit(0);
+
+   // remove label button
+   int idx = get_btn_idx(ui);
+   btn = (CustTabButton*)child(idx);
+   idx > 1 ? prev_tab() : next_tab();
+   remove(idx);
+   delete btn;
+   rearrange();
+   redraw();
+
+   Wizard->remove(ui);
+   delete(ui);
+}
+
+int CustTabs::get_btn_idx(UI *ui)
+{
+   for (int i = children()-1; i; --i) {
+      CustTabButton *btn = (CustTabButton*)child(i);
+      if (btn->ui() == ui)
+         return i;
    }
    return -1;
 }
 
-/*
- * Prevents tabs from going over the close-tab button.
- * Modified from fltk-2.0.x-r6525.
- */
-int CustShrinkTabPager::update_positions(
-       TabGroup *g, int numchildren, int &selected,
-       int &cumulated_width, int &available_width,
-       int *tab_pos, int *tab_width)
+void CustTabs::switch_tab(CustTabButton *cbtn)
 {
-   available_width-=BTN_W;
-
-   // uh oh, they are too big, we must move them:
-   // special case when the selected tab itself is too big, make it fill
-   // cumulated_width:
-   int i;
-
-   if (tab_width[selected] >= available_width) {
-      tab_width[selected] = available_width;
-      for (i = 0; i <= selected; i++)
-         tab_pos[i] = 0;
-      for (i = selected + 1; i <= numchildren; i++)
-         tab_pos[i] = available_width;
-      return selected;
-   }
-
-   int w2[128];
-
-   for (i = 0; i < numchildren; i++)
-      w2[i] = tab_width[i];
-   i = numchildren - 1;
-   int j = 0;
+   int idx;
+   CustTabButton *btn;
+   UI *old_ui = (UI*)Wizard->value();
 
-   int minsize = 5;
-
-   bool right = true;
-
-   while (cumulated_width > available_width) {
-      int n;                    // which one to shrink
-
-      if (j < selected && (!right || i <= selected)) {  // shrink a left one
-         n = j++;
-         right = true;
-      } else if (i > selected) {        // shrink a right one
-         n = i--;
-         right = false;
-      } else {                  // no more space, start making them zero
-         minsize = 0;
-         i = numchildren - 1;
-         j = 0;
-         right = true;
-         continue;
+   if (cbtn->ui() != old_ui) {
+      // Set old tab label to normal color
+      if ((idx = get_btn_idx(old_ui)) > 0) {
+         btn = (CustTabButton*)child(idx);
+         btn->color(tabcolor_inactive);
+         btn->redraw();
       }
-      cumulated_width -= w2[n] - minsize;
-      w2[n] = minsize;
-      if (cumulated_width < available_width) {
-         w2[n] = available_width - cumulated_width + minsize;
-         cumulated_width = available_width;
-         break;
-      }
+      Wizard->value(cbtn->ui());
+      cbtn->color(tabcolor_active);
+      cbtn->redraw();
    }
-   // re-sum the positions:
-   cumulated_width = 0;
-   for (i = 0; i < numchildren; i++) {
-      cumulated_width += w2[i];
-      tab_pos[i+1] = cumulated_width;
-   }
-   return selected;
 }
 
-TabGroupPager* CustShrinkTabPager::clone() const {
-   return new CustShrinkTabPager(*this);
-}
-#endif /* custom pager */
-//----------------------------------------------------------------------------
-
-/*
- * For custom handling of keyboard
- */
-class CustTabGroup : public Fl_Tabs {
-  Fl_Tooltip *toolTip;
-  bool tooltipEnabled;
-  bool buttonPushed;
-public:
-   CustTabGroup (int x, int y, int ww, int wh, const char *lbl=0) :
-      Fl_Tabs(x,y,ww,wh,lbl) {
-         Fl_Group::current(0);
-         box(FL_NO_BOX);
-         // The parameter pager is cloned, so free it.
-//       CustShrinkTabPager *cp = new CustShrinkTabPager();
-//       this->pager(cp);
-//       delete cp;
-         toolTip = new Fl_Tooltip;
-         tooltipEnabled = false;
-         buttonPushed = false;
-      };
-      ~CustTabGroup() { delete toolTip; }
-   int handle(int e) {
-      // Don't focus with arrow keys
-      _MSG("CustTabGroup::handle %d\n", e);
-//    fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
-      if (e == FL_KEYBOARD) {
-         int k = Fl::event_key();
-         // We're only interested in some flags
-         unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
-         if (k == FL_Up || k == FL_Down || k == FL_Tab) {
-            return 0;
-         } else if (k == FL_Left || k == FL_Right) {
-            if (modifier == FL_SHIFT) {
-               int i = find(value());
-               if (k == FL_Left) {i = i ? i-1 : children()-1;}
-               else {i++; if (i >= children()) i = 0;}
-               value(child(i));
-               return 1;
-            }
-            // Avoid focus change.
-            return 0;
-         }
-      } else if (e == FL_RELEASE) {
-         Fl_Widget *new_focus = which(Fl::event_x(), Fl::event_y());
-
-         if (new_focus && new_focus != value()) {
-            // Update the window title
-            BrowserWindow *bw = a_UIcmd_get_bw_by_widget(new_focus);
-            const char *title = a_History_get_title(NAV_TOP_UIDX(bw), 1);
+void CustTabs::prev_tab()
+{
+   int idx;
 
-            a_UIcmd_set_page_title(bw, title ? title : "");
-      }
-// custom pager
-#if 0
-      } else if (e == FL_MOVE) {
-         CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
-         if (Fl::event_inside(r) && children() > 1) {
-            /* We're inside the button area */
-            cstp->btn_highlight(true);
-            if (prefs.show_tooltip) {
-               /* Prepare the tooltip for pop-up */
-               tooltipEnabled = true;
-               /* We use parent() if available because we are returning 0.
-                * Returning without having TabGroup processing makes the
-                * popup event never reach 'this', but it reaches parent() */
-               toolTip->enter(parent() ?parent():this, r, "Close current Tab");
-            }
-            return 0;              // Change focus
-         } else {
-            cstp->btn_highlight(false);
+   if ((idx = get_btn_idx((UI*)Wizard->value())) > 1)
+      switch_tab( (CustTabButton*)child(idx-1) );
+}
 
-            if (prefs.show_tooltip) {
-               /* Hide the tooltip or enable it again.*/
-               if (tooltipEnabled) {
-                  tooltipEnabled = false;
-                  toolTip->exit();
-               } else {
-                  toolTip->enable();
-               }
-            }
-         }
-      } else if (e == FL_PUSH && Fl::event_inside(r) &&
-                 Fl::event_button() == 1 && children() > 1) {
-         buttonPushed = true;
-         return 1;                 /* non-zero */
-      } else if (e == FL_RELEASE) {
-         if (Fl::event_inside(r) && Fl::event_button() == 1 &&
-             children() > 1 && buttonPushed) {
-            a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(value()));
-         } else {
-            CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
-            cstp->btn_highlight(false);
-         }
-         buttonPushed = false;
-#endif
-      } else if (e == FL_DRAG) {
-         /* Ignore this event */
-         return 1;
-      }
-      int ret = Fl_Tabs::handle(e);
+void CustTabs::next_tab()
+{
+   int idx;
 
-      if (e == FL_PUSH) {
-         /* WORKAROUND: FLTK raises the window on unhandled clicks,
-          * which we do not want.
-          */
-         ret = 1;
-      }
-      return ret;
-   }
-
-   void remove (Fl_Widget *w) {
-      Fl_Tabs::remove (w);
-      /* fixup resizable in case we just removed it */
-      if (resizable () == w) {
-         if (children () > 0)
-            resizable (child (children () - 1));
-         else
-            resizable (NULL);
-      }
+   if ((idx = get_btn_idx((UI*)Wizard->value())) > 0 && idx+1 < children())
+      switch_tab( (CustTabButton*)child(idx+1) );
+}
 
-      if (children () < 2) {
-         box(FL_NO_BOX);
-         hideLabels ();
-      }
-   }
 
-   void add (Fl_Widget *w) {
-      Fl_Tabs::add (w);
-      if (children () > 1) {
-         box(FL_THIN_UP_BOX);
-         showLabels ();
-      }
-   }
 
-   void hideLabels() {
-      for (int i = children () - 1; i >= 0; i--)
-         child(i)->resize(x(), y(), w(), h());
-   }
-
-   void showLabels() {
-      for (int i = children () - 1; i >= 0; i--)
-         child(i)->resize(x(), y() + 20, w(), h() - 20);
-   }
-};
 
 //----------------------------------------------------------------------------
 
 static void win_cb (Fl_Widget *w, void *cb_data) {
    int choice = 1;
-   CustTabGroup *tabs = (CustTabGroup*) cb_data;
+   CustTabs *tabs = (CustTabs*) cb_data;
 
    if (tabs->children () > 1)
       choice = a_Dialog_choice5("Window contains more than one tab.",
@@ -434,26 +307,19 @@
       win = new Fl_Double_Window(ww, wh);
 
    Fl_Group::current(0);
-//may need a handler for this
-// win->shortcut(0); // Ignore Escape
-   CustTabGroup *DilloTabs = new CustTabGroup(0, 0, ww, wh);
+   CustTabs *DilloTabs = new CustTabs(ww, wh, 16);
    DilloTabs->selection_color(156);
    win->add(DilloTabs);
 
    // Create and set the UI
-   UI *new_ui = new UI(0, 0, ww, wh, DEFAULT_TAB_LABEL,
-                       old_bw ? BW2UI(old_bw) : NULL);
-   new_ui->set_status("http://www.dillo.org/");
-   new_ui->tabs(DilloTabs);
-
-   DilloTabs->add(new_ui);
-   DilloTabs->resizable(new_ui);
-   DilloTabs->window()->resizable(new_ui);
-   DilloTabs->window()->show();
+   UI *new_ui = DilloTabs->add_new_tab(1);
+   win->resizable(new_ui);
+   win->show();
+   //DilloTabs->window()->show();
 
    if (old_bw == NULL && prefs.xpos >= 0 && prefs.ypos >= 0) {
       // position the first window according to preferences
-      new_ui->window()->position(prefs.xpos, prefs.ypos);
+      DilloTabs->window()->position(prefs.xpos, prefs.ypos);
    }
 
    // Now create the Dw render layout and viewport
@@ -492,7 +358,7 @@
  * Create a new Tab.
  * i.e the new UI and its associated BrowserWindow data structure.
  */
-static BrowserWindow *UIcmd_tab_new(const void *vbw)
+static BrowserWindow *UIcmd_tab_new(const void *vbw, int focus)
 {
    _MSG(" UIcmd_tab_new vbw=%p\n", vbw);
 
@@ -508,13 +374,7 @@
                                         0, vbw);
 
    // Create and set the UI
-   UI *new_ui = new UI(0, 0, ui->w(), ui->h(), DEFAULT_TAB_LABEL, ui);
-   new_ui->tabs(ui->tabs());
-
-   new_ui->tabs()->add(new_ui);
-   new_ui->tabs()->resizable(new_ui);
-   new_ui->tabs()->window()->resizable(new_ui);
-   new_ui->tabs()->window()->show();
+   UI *new_ui = ui->tabs()->add_new_tab(focus);
 
    // Now create the Dw render layout and viewport
    FltkPlatform *platform = new FltkPlatform ();
@@ -553,14 +413,8 @@
    a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
    delete(layout);
    if (ui->tabs()) {
-      ui->tabs()->remove(ui);
-      if (ui->tabs()->value())
-         ui->tabs()->value()->take_focus();
-      else
-         ui->tabs()->window()->hide();
+      ui->tabs()->remove_tab();
    }
-   delete(ui);
-
    a_Bw_free(bw);
 }
 
@@ -663,11 +517,7 @@
  */
 void a_UIcmd_open_url_nt(void *vbw, const DilloUrl *url, int focus)
 {
-   BrowserWindow *new_bw = UIcmd_tab_new(vbw);
-
-   if (focus)
-      BW2UI(new_bw)->tabs()->value(BW2UI(new_bw));
-
+   BrowserWindow *new_bw = UIcmd_tab_new(vbw, focus);
    UIcmd_open_url_nbw(new_bw, url);
 }
 
@@ -1234,7 +1084,7 @@
    const int size = 128;
    char title[size];
 
-   if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->value()) == bw) {
+   if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->wizard()->value()) == bw) {
       // This is the focused bw, set window title
       if (snprintf(title, size, "Dillo: %s", label) >= size) {
          uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8));