some more headers tweakage, handle all sorts of wrap/flow cases, add global wrap option, make height persistence better.

This commit is contained in:
alta88@gmail.com 2010-01-02 20:36:38 -07:00
Родитель 15f5061a60
Коммит b090bad5db
7 изменённых файлов: 242 добавлений и 67 удалений

Просмотреть файл

@ -295,6 +295,27 @@ let Snowl = {
}
},
onSetHeaderWrap: function(aEvent) {
let contentDoc = gBrowser.selectedBrowser.contentDocument;
let messageHeader = contentDoc.getElementById("messageHeader");
let viewHdrChecked = document.getElementById("viewSnowlHeader").
getAttribute("checked") == "true";
let body = messageHeader.contentDocument.getElementById("body");
let headerDeck = messageHeader.contentDocument.getElementById("headerDeck");
// If a snowl message is in the tab, showing header, resize for wrapped flow.
if (messageHeader) {
if (aEvent.target.getAttribute("checked") == "true")
body.classList.add("wrap");
else
body.classList.remove("wrap");
}
if (viewHdrChecked)
messageHeader.contentWindow.wrappedJSObject.
messageHeaderUtils.setHeaderSize(body, headerDeck);
},
_toggleToolbar: function(event) {
let doc = document.getElementById("sidebar").contentDocument;
let toolbar = doc.getElementById("snowlToolbar");

Просмотреть файл

@ -155,6 +155,14 @@
<menuitem observes="viewSnowlHeader"
label="&showHeader.label;"
accesskey="&showHeader.accesskey;"/>
<menuitem id="viewSnowlHeaderWrap"
label="&wrapHeader.label;"
autoCheck="false"
type="checkbox"
accesskey="&wrapHeader.accesskey;"
checked="false"
persist="checked"
oncommand="Snowl.onSetHeaderWrap(event)"/>
<menuseparator/>
<menuitem id="snowlToolbarMenuitem"
label="&toolbar.label;"

Просмотреть файл

@ -41,7 +41,7 @@
#messageFrame[header="brief"] > #messageHeader,
#messageFrame[header="basic"] > #messageHeader {
overflow-x: hidden;
overflow: hidden;
}
/* Set visibility when header collapsed to prevent tab stops */
@ -75,23 +75,10 @@ table {
#noHeader[collapsed="true"],
#headerDeck:not([header="brief"]) .briefHeader,
#headerDeck[header="brief"] .basicHeaderRow,
#headerDeck[header="brief"] + #headerFull,
#headerDeck[header="basic"] + #headerFull {
#headerDeck[header="brief"] + #headerFullTable,
#headerDeck[header="basic"] + #headerFullTable {
display: none;
}
/*
tr {
border: 1px solid black;
}
td {
border: 1px solid red;
}
div, p, a, pre {
border: 1px solid blue;
}
table {
border: 3px solid green;
}*/
.headerButtons {
white-space: nowrap;
@ -110,16 +97,19 @@ table {
width: 1%;
}
.headerLabel:hover {
.headerLabel.wrapped:hover,
.headerLabel.wrappable:hover {
cursor: pointer;
}
.headerLabel.wrap {
border-right: 1px solid black;
body.wrap > table > tr > .headerLabel:hover {
cursor: auto;
}
body.wrap > table > tr > .headerLabel + .headerData > a,
.headerLabel.wrap + .headerData > a {
white-space: normal;
word-wrap: break-word;
}
.headerData {
@ -134,6 +124,8 @@ table {
width: 35%;
}
/* The date is difficult to style, as it must stick to the right edge, which
* doesn't seem to allow for optimal wrapping and flexing and min widths. */
#briefHeaderRow > .headerDataLast {
padding-right: 4px;
width: 5%;
@ -141,24 +133,32 @@ table {
}
#briefHeaderRow > .flexer {
width: 2%;
width: 1%;
}
.fullHeaderRow > .headerData {
overflow-x: auto;
.headerPadTop,
.headerPadBottom{
height: .5em;
}
#headerDeck:not([header="brief"]) > #briefHeaderRow > .headerDataSubject,
#headerDeck:not([header="brief"]) > #briefHeaderRow > .headerLabelSubject {
padding-top: .5em;
#headerDeck:not([header="brief"]) > tr > .headerPadBottom {
display: none;
}
#headerDeck[deleted] > #briefHeaderRow > .headerDataSubject > #subject{
text-decoration: line-through;
}
.fullHeaderRow > .headerData {
overflow-x: auto;
}
#headerFullTable > tr:nth-child(even) {
background: darkgray;
}
.fullHeaderRowSeparator {
border-bottom: 1px solid threedshadow;
border-top: 1px solid threedshadow;
margin: 0 10px;
}

Просмотреть файл

@ -64,10 +64,12 @@
dir="&locale.dir;"
onload="messageContent.createHeader();"
onclick="messageHeaderUtils.onClick(event);"
onresize="messageHeaderUtils.onResize();"
onmousemove="messageHeaderUtils.onMouseMove(event);"
onpageshow="messageHeaderUtils.init();">
<table id="headerDeck">
<tr id="briefHeaderRow">
<td class="headerButtons" valign="top" rowspan="2">
<tr id="briefHeaderButtons">
<td class="headerButtons" valign="top" rowspan="5">
<xul:toolbarbutton id="pinButton"
class="toolbarbutton-1"
type="checkbox"
@ -80,17 +82,33 @@
<xul:toolbarbutton id="headerButton"
class="toolbarbutton-1 tabbable"
header="full"
oncommand="messageHeaderUtils.toggleHeader(null, 'toggle');"
oncommand="messageHeaderUtils.toggleHeader(null, null, 'toggle');"
onmouseover="messageHeaderUtils.tooltip(event, 'show');"
onmousedown="messageHeaderUtils.tooltip(event, 'hide');"
onmouseout="messageHeaderUtils.tooltip(event, 'hide');"
onkeypress="messageHeaderUtils.onButtonKeyPress(event);"
tooltiptext="&toggleHeader.tooltip;"/>
</td>
</tr>
<tr>
<td class="headerPadTop" colspan="2"/>
<td class="headerButtons basicHeaderRow" valign="top" rowspan="5">
<xul:toolbarbutton id="deleteMessageButton"
class="toolbarbutton-1 tabbable"
oncommand="messageHeaderUtils.onDeleteMessageButton()"
onmouseover="messageHeaderUtils.tooltip(event, 'show');"
onmousedown="messageHeaderUtils.tooltip(event, 'hide');"
onmouseout="messageHeaderUtils.tooltip(event, 'hide');"
tooltiptext="&deleteMessagesButton.tooltip;"/>
</td>
</tr>
<tr id="briefHeaderRow">
<!-- Do not leave any space/comments or wrap/indent between headerLabel
and headerData </td><td elements as this will create a #text node and
disrupt nextSibling usage. -->
<td class="headerLabel headerLabelSubject" align="right">&subject.label;</td><td
<td class="headerLabel headerLabelSubject altrowA" align="right">&subject.label;</td><td
class="headerData headerDataSubject"><a id="subject"/></td>
<td class="briefHeader flexer"/>
@ -105,35 +123,29 @@
<td class="headerLabel briefHeader">&timestamp.label;</td><td
class="headerData briefHeader headerDataLast"><a id="briefTimestamp"/></td>
<td class="headerButtons basicHeaderRow" valign="top" rowspan="2">
<xul:toolbarbutton id="deleteMessageButton"
class="toolbarbutton-1 tabbable"
oncommand="messageHeaderUtils.onDeleteMessageButton()"
onmouseover="messageHeaderUtils.tooltip(event, 'show');"
onmousedown="messageHeaderUtils.tooltip(event, 'hide');"
onmouseout="messageHeaderUtils.tooltip(event, 'hide');"
tooltiptext="&deleteMessagesButton.tooltip;"/>
</td>
</tr>
<tr><td class="headerPadBottom" colspan="2"/></tr>
<tr class="basicHeaderRow">
<td class="headerLabel">&author.label;</td><td
<td class="headerLabel altrowB">&author.label;</td><td
class="headerData"><a id="author"/></td>
</tr>
<tr class="basicHeaderRow">
<td class="headerLabel" colspan="2">&timestamp.label;</td><td
class="headerData"><a id="timestamp" colspan="2"/></td>
<td class="headerLabel altrowA" colspan="2">&timestamp.label;</td><td
class="headerData"><a id="timestamp" colspan="3"/></td>
</tr>
<!-- -->
</table>
<div id="headerFull">
<table id="headerFullTable" class="fullHeaderTable">
<tr class="fullHeaderRow">
<td class="fullHeaderRowSeparator" colspan="3"/>
</tr>
</table>
</div>
<!-- Full headers table rows are dynamically created. -->
<table id="headerFullTable" class="fullHeaderTable">
<tr class="fullHeaderRow">
<td class="fullHeaderRowSeparator" colspan="3"/>
</tr>
</table>
</body>
</html>

Просмотреть файл

@ -246,11 +246,17 @@ var messageContent = {
var messageHeaderUtils = {
ROWS_BRIEF: "30,*",
origWidth: null,
origHeight: null,
noResize: false,
init: function() {
var pin = document.getElementById("pinButton");
var headerBcaster = gBrowserWindow.document.
getElementById("viewSnowlHeader");
var wrap = gBrowserWindow.document.
getElementById("viewSnowlHeaderWrap");
var body = document.getElementById("body");
var headerDeck = document.getElementById("headerDeck");
var noHeader = parent.document.
documentElement.getElementsByClassName("noHeader")[0];
@ -263,7 +269,7 @@ var messageHeaderUtils = {
if (checked) {
// Collapse hover area, set header.
noHeader.setAttribute("collapsed", true);
this.toggleHeader(headerDeck, "init");
this.toggleHeader(body, headerDeck, "init");
}
else {
// Uncollapse hover area, hide header frame.
@ -271,23 +277,62 @@ var messageHeaderUtils = {
parent.document.body.rows = "0,*";
noHeader.removeAttribute("collapsed");
}
if (wrap.getAttribute("checked") == "true")
body.classList.add("wrap");
else
body.classList.remove("wrap");
// Fires after onresize done, store new width and height.
window.addEventListener("MozScrolledAreaChanged",
function () {
messageHeaderUtils.origWidth =
parent.document.body.
clientWidth;
messageHeaderUtils.origHeight =
parent.document.getElementById("messageHeader").
clientHeight; },
false);
},
onMouseOver: function(aEvent) {
var node = aEvent.target;
var messageHeader = document.getElementById("messageHeader");
var body = messageHeader.contentDocument.getElementById("body");
var headerDeck = messageHeader.contentDocument.getElementById("headerDeck");
var pin = messageHeader.contentDocument.getElementById("pinButton");
if (node.id != "noHeader" || pin.hasAttribute("checked"))
return;
this.headertimer = window.setTimeout(function() {
messageHeaderUtils.toggleHeader(headerDeck);
messageHeaderUtils.toggleHeader(body, headerDeck, "hover");
document.getElementById("noHeader").
setAttribute("collapsed", true);
}, 500);
},
onMouseMove: function(aEvent) {
var node = aEvent.target;
if (node.classList.contains("headerLabel")) {
// Hovering a header label. Set cursor to indicate toggle for wrap mode
// based on wrap state and wrappability.
if (node.nextSibling.clientWidth < node.nextSibling.firstChild.offsetWidth)
node.classList.add("wrappable");
else
node.classList.remove("wrappable");
var dataCStyle = window.getComputedStyle(node, null)
// Need the height of the <a> content as the containing <td> sizes to match
// any other <td> in the row that has expanded to fit wrapped content.
var dataHt = node.nextSibling.firstChild.scrollHeight;
var dataLnHt = dataCStyle.getPropertyValue("line-height").replace(/px/, "");
if (dataHt != dataLnHt)
node.classList.add("wrapped");
else
node.classList.remove("wrapped");
}
},
onMouseOut: function(aEvent) {
window.clearTimeout(this.headertimer);
delete this.headertimer;
@ -297,6 +342,9 @@ var messageHeaderUtils = {
if (node.id != "messageHeader" || pin.hasAttribute("checked"))
return;
// Set noResize in scope of messageHeader frame document, which listens for
// the onResize event.
messageHeader.contentWindow.wrappedJSObject.messageHeaderUtils.noResize = true;
document.getElementById("messageFrame").setAttribute("border", "0");
document.getElementById("messageFrame").setAttribute("rows", "0,*");
document.getElementById("noHeader").removeAttribute("collapsed");
@ -320,25 +368,54 @@ var messageHeaderUtils = {
headerBcaster.setAttribute("checked", pin.checked);
},
toggleHeader: function(headerDeck, aType) {
setHeaderSize: function(aBody, aHeaderDeck) {
// Calculate how to set header height, given wrap toggling and splitter dnd.
var headerBcaster = gBrowserWindow.document.getElementById("viewSnowlHeader");
var headerIndex = parseInt(headerBcaster.getAttribute("headerIndex"));
var rowsBasic = headerBcaster.getAttribute("rowsBasic");
var rowsFull = headerBcaster.getAttribute("rowsFull");
if ((this.isWrapped(aHeaderDeck) || aBody.classList.contains("wrap")) &&
headerIndex != 2)
// Reset header frame height to flow wrapped content. Full header scrolls,
// so not necessary for 2.
parent.document.body.rows = aBody.scrollHeight + ",*";
else
// Restore user set height if nothing wraps, or on init and mouseover/out.
parent.document.body.rows = headerIndex == 0 ? this.ROWS_BRIEF :
headerIndex == 1 ? rowsBasic : rowsFull;
if (aType != "init" && headerBcaster.getAttribute("checked") == "true") {
// To set a header height: must first be in non Brief header, pin must be
// checked, height can be dnd adjusted as desired, then header must be
// toggled to save the height.
if (headerIndex == 1)
headerBcaster.setAttribute("rowsBasic", parent.document.body.rows);
if (headerIndex == 2)
headerBcaster.setAttribute("rowsFull", parent.document.body.rows);
this.noResize = true;
},
isWrapped: function(aHeaderDeck) {
// Is any row in the brief/basic headers wrapped? Not just toggled to wrap,
// but also flowed to multiple lines.
var oneWrapped = false;
var headerDeck = aHeaderDeck;
var wrappedNodes = headerDeck.getElementsByClassName("wrap");
for (var i = 0; i < wrappedNodes.length && !oneWrapped; i++) {
var node = wrappedNodes[i];
var dataCStyle = window.getComputedStyle(node, null)
var dataHt = dataCStyle.getPropertyValue("height");
var dataLnHt = dataCStyle.getPropertyValue("line-height");
if (dataHt != dataLnHt)
oneWrapped = true;
}
return oneWrapped;
},
toggleHeader: function(aBody, aHeaderDeck, aType) {
var headerBcaster = gBrowserWindow.document.getElementById("viewSnowlHeader");
var headerIndex = parseInt(headerBcaster.getAttribute("headerIndex"));
var body = aBody ? aBody :
document.getElementById("body");
var headerDeck = aHeaderDeck ? aHeaderDeck :
document.getElementById("headerDeck");
if (aType == "toggle") {
// Toggled to next in 3 way
headerDeck = document.getElementById("headerDeck");
headerIndex = ++headerIndex > 2 ? 0 : headerIndex++;
headerBcaster.setAttribute("headerIndex", headerIndex);
}
@ -348,13 +425,15 @@ var messageHeaderUtils = {
headerDeck.setAttribute("header", headerType);
parent.document.body.setAttribute("header", headerType);
parent.document.body.setAttribute("border", "6");
parent.document.body.rows = headerIndex == 0 ? this.ROWS_BRIEF :
headerIndex == 1 ? rowsBasic : rowsFull;
// The message is found in the scope of the parent frameset document.
var messageContent = parent.wrappedJSObject.messageContent;
if (headerIndex == 2 && !messageContent._headers)
messageContent.createFullHeader(headerDeck);
// Set the size to persisted values or make sure toggling results in nice
// headers wrapped content flow.
this.setHeaderSize(body, headerDeck);
},
onDeleteMessageButton: function() {
@ -364,8 +443,61 @@ var messageHeaderUtils = {
},
onClick: function(aEvent) {
if (aEvent.target.classList.contains("headerLabel") && aEvent.button == 0)
aEvent.target.classList.toggle("wrap");
if (aEvent.button != 0)
return;
var node = aEvent.target;
if (node.classList.contains("headerLabel")) {
// Clicked on a header label.
var body = document.getElementById("body");
var headerDeck = document.getElementById("headerDeck");
if (!body.classList.contains("wrap")) {
// Set wrap and resize only if global wrap not set.
node.classList.toggle("wrap");
this.setHeaderSize(body, headerDeck);
}
}
},
onResize: function() {
var messageHeader = parent.document.getElementById("messageHeader");
var body = document.getElementById("body");
var headerDeck = document.getElementById("headerDeck");
var headerBcaster = gBrowserWindow.document.getElementById("viewSnowlHeader");
var headerIndex = parseInt(headerBcaster.getAttribute("headerIndex"));
var rowsBasic = headerBcaster.getAttribute("rowsBasic");
var rowsFull = headerBcaster.getAttribute("rowsFull");
var newWidth = parent.document.body.clientWidth;
var newHeight = messageHeader.clientHeight;
if (this.origWidth != newWidth && headerIndex != 2) {
// If the sidebar width has changed, make sure things reflow nicely; since
// the full header has a scrollbar, only necessary for brief/basic headers.
if (this.isWrapped(headerDeck) || body.classList.contains("wrap"))
// Reset header frame height to flow wrapped content.
parent.document.body.rows = body.scrollHeight + ",*";
else
// Restore user set height if nothing wraps.
parent.document.body.rows = headerIndex == 0 ? this.ROWS_BRIEF :
headerIndex == 1 ? rowsBasic : rowsFull;
}
if (this.noResize) {
// Do not resize if size changed due to header label click, or
// mouseover/out header show/hide. All height resizes must come from
// dragging the frame border, for persisting height.
this.noResize = false;
return;
}
if (this.origHeight != newHeight && this.origWidth == newWidth) {
// Just height changed, must be only due to dnd resize, persist new height.
// There is no frames event that would make persisting height more direct.
if (headerIndex == 1)
headerBcaster.setAttribute("rowsBasic", parent.document.body.rows);
if (headerIndex == 2)
headerBcaster.setAttribute("rowsFull", parent.document.body.rows);
}
},
tooltip: function(aEvent, aShow) {

Просмотреть файл

@ -61,6 +61,8 @@
<!ENTITY showHeader.label "Show Header">
<!ENTITY showHeader.accesskey "H">
<!ENTITY wrapHeader.label "Wrap All Headers">
<!ENTITY wrapHeader.accesskey "W">
<!ENTITY toolbar.label "Sidebar Toolbar">
<!ENTITY toolbar.accesskey "d">

Просмотреть файл

@ -8,6 +8,6 @@
<!ENTITY pinButton.label "Pin Header">
<!ENTITY pinButton.tooltip "Toggle pin to set and unset permanent header">
<!ENTITY toggleHeader.label "Toggle Header">
<!ENTITY toggleHeader.tooltip "Toggle message header to brief, basic, or full; in basic or full header, toggle to save height after adjustment">
<!ENTITY toggleHeader.tooltip "Toggle message header to brief, basic, or full">
<!ENTITY deleteMessagesButton.label "Delete Message">
<!ENTITY deleteMessagesButton.tooltip "Delete this Message">