From 73ad4f1bfdf104055104907c11f97315d6fb2ebe Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Tue, 09 Jul 2013 17:41:40 -0400
Subject: [PATCH] Finally: make message list header stay on top when scrolling (#1295420)

---
 program/js/list.js              |   57 +++++++++++
 skins/larry/templates/mail.html |    2 
 program/js/app.js               |    2 
 skins/larry/mail.css            |  228 +++++++++++++++++++++++++--------------------
 skins/larry/ui.js               |    7 +
 5 files changed, 191 insertions(+), 105 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 04fb9cd..45a17d6 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -6437,6 +6437,7 @@
 
           if ((response.action == 'list' || response.action == 'search') && this.message_list) {
             this.msglist_select(this.message_list);
+            this.message_list.resize();
             this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount });
           }
         }
@@ -6447,6 +6448,7 @@
             this.enable_command('search-create', this.env.source == '');
             this.enable_command('search-delete', this.env.search_id);
             this.update_group_commands();
+            this.contact_list.resize();
             this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount });
           }
         }
diff --git a/program/js/list.js b/program/js/list.js
index 8b8a719..e8f7210 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -33,6 +33,7 @@
   this.tagname = this.list ? this.list.nodeName.toLowerCase() : 'table';
   this.thead;
   this.tbody;
+  this.fixed_header;
   this.frame = null;
   this.rows = [];
   this.selection = [];
@@ -152,6 +153,14 @@
   if (this.thead) {
     this.colcount = 0;
 
+    if (this.fixed_header) {  // copy (modified) fixed header back to the actual table
+      $(this.list.tHead).replaceWith($(this.fixed_header).find('thead').clone());
+      $(this.list.tHead).find('tr td').attr('style', '');  // remove fixed widths
+    }
+    else if (!bw.ie7 && this.list.className.indexOf('fixedheader') >= 0) {
+      this.init_fixed_header();
+    }
+
     var col, r, p = this;
     // add events for list columns moving
     if (this.column_movable && this.thead && this.thead.rows) {
@@ -166,6 +175,47 @@
   }
 },
 
+init_fixed_header: function()
+{
+  var clone = $(this.list.tHead).clone();
+
+  if (!this.fixed_header) {
+    this.fixed_header = $('<table>')
+      .attr('class', this.list.className)
+      .css({ position:'fixed' })
+      .append(clone)
+      .append('<tbody></tbody>');
+    $(this.list).before(this.fixed_header);
+
+    var me = this;
+    $(window).resize(function(){ me.resize() });
+  }
+  else {
+    $(this.fixed_header).find('thead').replaceWith(clone);
+  }
+
+  this.thead = clone.get(0);
+  this.resize();
+},
+
+resize: function()
+{
+    if (!this.fixed_header)
+      return;
+
+    var column_widths = [];
+
+    // get column widths from original thead
+    $(this.tbody).parent().find('thead tr td').each(function(index) {
+      column_widths[index] = $(this).width();
+    });
+
+    // apply fixed widths to fixed table header
+    $(this.thead).parent().width($(this.tbody).parent().width());
+    $(this.thead).find('tr td').each(function(index) {
+      $(this).css('width', column_widths[index]);
+    });
+},
 
 /**
  * Remove all list rows
@@ -494,6 +544,7 @@
     new_row = new_row.nextSibling;
   }
 
+  this.resize();
   this.triggerEvent('listupdate');
   return false;
 },
@@ -542,6 +593,7 @@
     new_row = new_row.nextSibling;
   }
 
+  this.resize();
   this.triggerEvent('listupdate');
   return false;
 },
@@ -585,6 +637,7 @@
     new_row = new_row.nextSibling;
   }
 
+  this.resize();
   this.triggerEvent('listupdate');
   return false;
 },
@@ -623,6 +676,7 @@
     new_row = new_row.nextSibling;
   }
 
+  this.resize();
   this.triggerEvent('listupdate');
   return false;
 },
@@ -1531,6 +1585,9 @@
   else if (this.subject_col > from && to >= this.subject_col)
     this.subject_col--;
 
+  if (this.fixed_header)
+    this.init_header();
+
   this.triggerEvent('column_replace');
 }
 
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index 138fad7..6e90ea6 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -79,6 +79,25 @@
 	overflow: auto;
 }
 
+/* Real browsers accept this (not IE) */
+html>/**/body #messagelist>tbody {
+	overflow: auto;
+	overflow-x: hidden;
+}
+/*
+#messagelistcontainer::-webkit-scrollbar {
+	-webkit-appearance: none;
+	width: 14px;
+	border-left: 1px solid #e4e4e4;
+	background: #fafafa;
+}
+#messagelistcontainer::-webkit-scrollbar-thumb {
+	border-radius: 10px;
+	border: 3px solid #f2f2f2;
+	background-color: #c2c2c2;
+	-webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
+}
+*/
 #messagelistfooter {
 	position: absolute;
 	bottom: 0;
@@ -447,141 +466,146 @@
 
 /*** message list ***/
 
-#messagelist thead td:first-child {
+/* this is necessary to make FF3 display borders */
+body:-moz-last-node #messagelist {
+	border-collapse: separate;
+}
+
+.messagelist thead td:first-child {
 	border-radius: 4px 0 0 0; /* for Chrome */
 }
 
-#messagelist tr td.attachment,
-#messagelist tr td.threads,
-#messagelist tr td.status,
-#messagelist tr td.flag,
-#messagelist tr td.priority {
+.messagelist tr td.attachment,
+.messagelist tr td.threads,
+.messagelist tr td.status,
+.messagelist tr td.flag,
+.messagelist tr td.priority {
 	width: 20px;
 	padding: 2px 3px;
 }
 
-.webkit #messagelist tr td.attachment,
-.webkit #messagelist tr td.threads,
-.webkit #messagelist tr td.status,
-.webkit #messagelist tr td.flag,
-.webkit #messagelist tr td.priority {
+.webkit .messagelist tr td.attachment,
+.webkit .messagelist tr td.threads,
+.webkit .messagelist tr td.status,
+.webkit .messagelist tr td.flag,
+.webkit .messagelist tr td.priority {
 	width: 26px;
 }
 
-#messagelist tr td.threads {
+.messagelist tr td.threads {
 	width: 26px;
 }
 
-.webkit #messagelist tr td.threads {
+.webkit .messagelist tr td.threads {
 	width: 30px;
 }
 
-#messagelist tr td.threads,
-#messagelist tr td.threads + td {
+.messagelist tr td.threads,
+.messagelist tr td.threads + td {
 	border-left: 0;
 }
 
-#messagelist tr td.size {
+.messagelist tr td.size {
 	width: 60px;
 	text-align: right;
 }
 
-#messagelist thead tr td.size {
+.messagelist thead tr td.size {
 	text-align: left;
 }
 
-#messagelist tr td.fromto,
-#messagelist tr td.from,
-#messagelist tr td.to,
-#messagelist tr td.cc,
-#messagelist tr td.replyto {
+.messagelist tr td.fromto,
+.messagelist tr td.from,
+.messagelist tr td.to,
+.messagelist tr td.cc,
+.messagelist tr td.replyto {
 	width: 200px;
 }
 
-#messagelist tr td.date {
+.messagelist tr td.date {
 	width: 135px;
 }
 
-#messagelist tr.message {
+.messagelist tr.message {
 /*	background-color: #fff; */
 }
 
-#messagelist tr.thread.expanded td {
+.messagelist tr.thread.expanded td {
 	background-color: #ededed;
 }
 
-#messagelist tr.unread {
+.messagelist tr.unread {
 	font-weight: bold;
 /*	background-color: #fff; */
 }
 
-#messagelist tr.flagged td,
-#messagelist tr.flagged td a {
+.messagelist tr.flagged td,
+.messagelist tr.flagged td a {
 	color: #f30;
 }
 
-#messagelist thead tr td.sortedASC a,
-#messagelist thead tr td.sortedDESC a {
+.messagelist thead tr td.sortedASC a,
+.messagelist thead tr td.sortedDESC a {
 	color: #004458;
 	text-decoration: underline;
 	background: url(images/listicons.png) right -912px no-repeat;
 }
 
-#messagelist thead tr td.sortedASC a {
+.messagelist thead tr td.sortedASC a {
 	background-position: right -944px;
 }
 
-#messagelist td img {
+.messagelist td img {
 	vertical-align: middle;
 	display: inline-block;
 }
 
-#messagelist tbody td a {
+.messagelist tbody td a {
 	color: #333;
 	text-decoration: none;
 	white-space: nowrap;
 	cursor: default;
 }
 
-#messagelist tbody tr td.flag,
-#messagelist tbody tr td.status,
-#messagelist tbody tr td.subject span.status {
+.messagelist tbody tr td.flag,
+.messagelist tbody tr td.status,
+.messagelist tbody tr td.subject span.status {
 	cursor: pointer;
 }
 
-#messagelist tr td.flag span,
-#messagelist tr td.status span,
-#messagelist tr td.attachment span,
-#messagelist tr td.priority span {
+.messagelist tr td.flag span,
+.messagelist tr td.status span,
+.messagelist tr td.attachment span,
+.messagelist tr td.priority span {
 	display: block;
 	width: 20px;
 }
 
-#messagelist tr td div.collapsed,
-#messagelist tr td div.expanded,
-#messagelist tr td.threads div.listmenu,
-#messagelist tr td.attachment span.attachment,
-#messagelist tr td.attachment span.report,
-#messagelist tr td.priority span.priority,
-#messagelist tr td.priority span.prio1,
-#messagelist tr td.priority span.prio2,
-#messagelist tr td.priority span.prio3,
-#messagelist tr td.priority span.prio4,
-#messagelist tr td.priority span.prio5,
-#messagelist tr td.flag span.flagged,
-#messagelist tr td.flag span.unflagged,
-#messagelist tr td.flag span.unflagged:hover,
-#messagelist tr td.status span.status,
-#messagelist tr td.status span.msgicon,
-#messagelist tr td.status span.deleted,
-#messagelist tr td.status span.unread,
-#messagelist tr td.status span.unreadchildren,
-#messagelist tr td.subject span.msgicon,
-#messagelist tr td.subject span.deleted,
-#messagelist tr td.subject span.unread,
-#messagelist tr td.subject span.replied,
-#messagelist tr td.subject span.forwarded,
-#messagelist tr td.subject span.unreadchildren {
+.messagelist tr td div.collapsed,
+.messagelist tr td div.expanded,
+.messagelist tr td.threads div.listmenu,
+.messagelist tr td.attachment span.attachment,
+.messagelist tr td.attachment span.report,
+.messagelist tr td.priority span.priority,
+.messagelist tr td.priority span.prio1,
+.messagelist tr td.priority span.prio2,
+.messagelist tr td.priority span.prio3,
+.messagelist tr td.priority span.prio4,
+.messagelist tr td.priority span.prio5,
+.messagelist tr td.flag span.flagged,
+.messagelist tr td.flag span.unflagged,
+.messagelist tr td.flag span.unflagged:hover,
+.messagelist tr td.status span.status,
+.messagelist tr td.status span.msgicon,
+.messagelist tr td.status span.deleted,
+.messagelist tr td.status span.unread,
+.messagelist tr td.status span.unreadchildren,
+.messagelist tr td.subject span.msgicon,
+.messagelist tr td.subject span.deleted,
+.messagelist tr td.subject span.unread,
+.messagelist tr td.subject span.replied,
+.messagelist tr td.subject span.forwarded,
+.messagelist tr td.subject span.unreadchildren {
 	display: inline-block;
 	vertical-align: middle;
 	height: 18px;
@@ -590,135 +614,135 @@
 	background: url(images/listicons.png) -100px 0 no-repeat;
 }
 
-#messagelist tbody tr td.attachment span.attachment {
+.messagelist tbody tr td.attachment span.attachment {
 	background-position: 0 -996px;
 }
 
-#messagelist thead tr td.attachment span.attachment {
+.messagelist thead tr td.attachment span.attachment {
 	background-position: -24px -997px;
 }
 
-#messagelist tbody tr td.attachment span.report {
+.messagelist tbody tr td.attachment span.report {
 	background-position: -24px -1116px;
 }
 
-#messagelist tr td.priority span.prio5 {
+.messagelist tr td.priority span.prio5 {
 	background-position: 0 -1905px;
 }
 
-#messagelist tr td.priority span.prio4 {
+.messagelist tr td.priority span.prio4 {
 	background-position: 0 -1885px;
 }
 
-#messagelist tr td.priority span.prio2 {
+.messagelist tr td.priority span.prio2 {
 	background-position: 0 -1865px;
 }
 
-#messagelist tr td.priority span.prio1 {
+.messagelist tr td.priority span.prio1 {
 	background-position: 0 -1845px;
 }
 
-#messagelist tbody tr td.flag span.flagged {
+.messagelist tbody tr td.flag span.flagged {
 	background-position: 0 -1036px;
 }
 
-#messagelist thead tr td.flag span.flagged {
+.messagelist thead tr td.flag span.flagged {
 	background-position: -24px -1036px;
 }
 
-#messagelist tr td.status span.msgicon:hover {
+.messagelist tr td.status span.msgicon:hover {
 	background-position: -23px -1056px;
 }
 
-#messagelist tr td.flag span.unflagged:hover {
+.messagelist tr td.flag span.unflagged:hover {
 	background-position: -23px -1076px;
 }
 
-#messagelist tr td.subject span.msgicon,
-#messagelist tr td.subject span.unreadchildren {
+.messagelist tr td.subject span.msgicon,
+.messagelist tr td.subject span.unreadchildren {
 	background-position: 0 -1056px;
 	margin: 0 1px 0 0;
 	width: 24px;
 }
 
-#messagelist tr td.subject span.replied {
+.messagelist tr td.subject span.replied {
 	background-position: 0 -1076px;
 }
 
-#messagelist tr td.subject span.forwarded {
+.messagelist tr td.subject span.forwarded {
 	background-position: 0 -1096px;
 }
 
-#messagelist tr td.subject span.replied.forwarded {
+.messagelist tr td.subject span.replied.forwarded {
 	background-position: 0 -1116px;
 }
 
-#messagelist tr td.status span.msgicon,
-#messagelist tr td.flag span.unflagged,
-#messagelist tr td.status span.unreadchildren {
+.messagelist tr td.status span.msgicon,
+.messagelist tr td.flag span.unflagged,
+.messagelist tr td.status span.unreadchildren {
 	background-position: 0 1056px; /* no icon */
 }
 /*
-#messagelist tr td.status span.msgicon:hover {
+.messagelist tr td.status span.msgicon:hover {
 	background-position: 0 -272px;
 }
 */
-#messagelist tr td.status span.deleted,
-#messagelist tr td.status span.deleted:hover,
-#messagelist tr td.subject span.deleted {
+.messagelist tr td.status span.deleted,
+.messagelist tr td.status span.deleted:hover,
+.messagelist tr td.subject span.deleted {
 	background-position: -22px -1096px;
 }
 
-#messagelist tr td.status span.status,
-#messagelist tr td.status span.unread,
-#messagelist tr td.subject span.unread,
-#messagelist tr td.status span.unread:hover {
+.messagelist tr td.status span.status,
+.messagelist tr td.status span.unread,
+.messagelist tr td.subject span.unread,
+.messagelist tr td.status span.unread:hover {
 	background-position: 0 -1016px;
 }
 
-#messagelist thead tr td.status span.status {
+.messagelist thead tr td.status span.status {
 	background-position: -24px -1016px;
 }
 
-#messagelist tr td div.collapsed {
+.messagelist tr td div.collapsed {
 	background-position: 0 -1137px;
 	cursor: pointer;
 }
 
-#messagelist tr td div.expanded {
+.messagelist tr td div.expanded {
 	background-position: 0 -1157px;
 	cursor: pointer;
 }
 
-#messagelist tr td.threads div.listmenu {
+.messagelist tr td.threads div.listmenu {
 	background-position: 0 -976px;
 	cursor: pointer;
 	width: 26px;
 }
 
-#messagelist thead tr td.subject,
-#messagelist tbody tr td.subject {
+.messagelist thead tr td.subject,
+.messagelist tbody tr td.subject {
 	width: 99%;
 	white-space: nowrap;
 }
 
-#messagelist tbody tr td.subject a {
+.messagelist tbody tr td.subject a {
 	cursor: default;
 	vertical-align: middle; /* #1487091 */
 }
 
 /* thread parent message with unread children */
-#messagelist tbody tr.unroot td.subject a {
+.messagelist tbody tr.unroot td.subject a {
 	text-decoration: underline;
 }
 
 /**** tree indicators ****/
 
-#messagelist tbody tr td span.branch div {
+.messagelist tbody tr td span.branch div {
 	display: inline-block;
 }
 
-#messagelist tbody tr td span.branch div.tree {
+.messagelist tbody tr td span.branch div.tree {
 	width: 15px;
 }
 
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index 6b6ffd7..6015054 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -70,7 +70,7 @@
 <div id="messagelistcontainer" class="boxlistcontent">
 <roundcube:object name="messages"
 	id="messagelist"
-	class="records-table sortheader"
+	class="records-table messagelist sortheader fixedheader"
 	optionsmenuIcon="true" />
 </div>
 
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 19f05cc..e5733bd 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -600,8 +600,11 @@
         mailviewsplit.handle.hide();
     }
 
-    if (visible && uid && rcmail.message_list)
-      rcmail.message_list.scrollto(uid);
+    if (rcmail.message_list) {
+      if (visible && uid)
+          rcmail.message_list.scrollto(uid);
+      rcmail.message_list.resize();
+    }
 
     rcmail.command('save-pref', { name:'preview_pane', value:(visible?1:0) });
   }

--
Gitblit v1.9.1