summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgroby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 01:00:48 +0000
committergroby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 01:00:48 +0000
commit21c52bd950c03e810d3e25b65e15c06d92eb547e (patch)
tree20581d659e93857462ddce1acc794cf0e1f5d507
parentf1933791ead4e0c44066287396f0475e95a2e1e3 (diff)
downloadchromium_src-21c52bd950c03e810d3e25b65e15c06d92eb547e.zip
chromium_src-21c52bd950c03e810d3e25b65e15c06d92eb547e.tar.gz
chromium_src-21c52bd950c03e810d3e25b65e15c06d92eb547e.tar.bz2
Support drag&drop for startup pages
BUG=69686 TEST=Drag & drop individual or multiple startup pages, confirm D&D works Review URL: http://codereview.chromium.org/7044136 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@88936 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/custom_home_pages_table_model.cc58
-rw-r--r--chrome/browser/custom_home_pages_table_model.h5
-rw-r--r--chrome/browser/resources/options/browser_options.html1
-rw-r--r--chrome/browser/resources/options/browser_options_page.css16
-rw-r--r--chrome/browser/resources/options/browser_options_startup_page_list.js152
-rw-r--r--chrome/browser/ui/webui/options/browser_options_handler.cc27
-rw-r--r--chrome/browser/ui/webui/options/browser_options_handler.h3
7 files changed, 262 insertions, 0 deletions
diff --git a/chrome/browser/custom_home_pages_table_model.cc b/chrome/browser/custom_home_pages_table_model.cc
index 5f2cf61..8380230 100644
--- a/chrome/browser/custom_home_pages_table_model.cc
+++ b/chrome/browser/custom_home_pages_table_model.cc
@@ -66,6 +66,64 @@ void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
observer_->OnModelChanged();
}
+/**
+ * Move a number of existing entries to a new position, reordering the table.
+ *
+ * We determine the range of elements affected by the move, save the moved
+ * elements, compact the remaining ones, and re-insert moved elements.
+ * Expects |index_list| to be ordered ascending.
+ */
+void CustomHomePagesTableModel::MoveURLs(int insert_before,
+ const std::vector<int>& index_list)
+{
+ DCHECK(insert_before >= 0 && insert_before <= RowCount());
+
+ // The range of elements that needs to be reshuffled is [ |first|, |last| ).
+ int first = std::min(insert_before, index_list.front());
+ int last = std::max(insert_before, index_list.back() + 1);
+
+ // Save the dragged elements. Also, adjust insertion point if it is before a
+ // dragged element.
+ std::vector<Entry> moved_entries;
+ for (size_t i = 0; i < index_list.size(); ++i) {
+ moved_entries.push_back(entries_[index_list[i]]);
+ if (index_list[i] == insert_before)
+ insert_before++;
+ }
+
+ // Compact the range between beginning and insertion point, moving downwards.
+ size_t skip_count = 0;
+ for (int i = first; i < insert_before; ++i) {
+ if (skip_count < index_list.size() && index_list[skip_count] == i)
+ skip_count++;
+ else
+ entries_[i - skip_count]=entries_[i];
+ }
+
+ // Moving items down created a gap. We start compacting up after it.
+ first = insert_before;
+ insert_before -= skip_count;
+
+ // Now compact up for elements after the insertion point.
+ skip_count = 0;
+ for (int i = last - 1; i >= first; --i) {
+ if (skip_count < index_list.size() &&
+ index_list[index_list.size() - skip_count - 1] == i) {
+ skip_count++;
+ } else {
+ entries_[i + skip_count] = entries_[i];
+ }
+ }
+
+ // Insert moved elements.
+ std::copy(moved_entries.begin(), moved_entries.end(),
+ entries_.begin() + insert_before);
+
+ // Possibly large change, so tell the view to just rebuild itself.
+ if (observer_)
+ observer_->OnModelChanged();
+}
+
void CustomHomePagesTableModel::Add(int index, const GURL& url) {
DCHECK(index >= 0 && index <= RowCount());
entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
diff --git a/chrome/browser/custom_home_pages_table_model.h b/chrome/browser/custom_home_pages_table_model.h
index 5145564..0403d8d 100644
--- a/chrome/browser/custom_home_pages_table_model.h
+++ b/chrome/browser/custom_home_pages_table_model.h
@@ -33,6 +33,11 @@ class CustomHomePagesTableModel : public ui::TableModel {
// Sets the set of urls that this model contains.
void SetURLs(const std::vector<GURL>& urls);
+ // Collect all entries indexed by |index_list|, and moves them to be right
+ // before the element addressed by |insert_before|. Used by Drag&Drop.
+ // Expects |index_list| to be ordered ascending.
+ void MoveURLs(int insert_before, const std::vector<int>& index_list);
+
// Adds an entry at the specified index.
void Add(int index, const GURL& url);
diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html
index 740e98d..9f97abb 100644
--- a/chrome/browser/resources/options/browser_options.html
+++ b/chrome/browser/resources/options/browser_options.html
@@ -35,6 +35,7 @@
i18n-content="startupUseCurrent"></button>
</div>
</div>
+ <div id="startupPagesListDropmarker"></div>
</div>
</div>
</section>
diff --git a/chrome/browser/resources/options/browser_options_page.css b/chrome/browser/resources/options/browser_options_page.css
index 1d0568d..4e3a5fa 100644
--- a/chrome/browser/resources/options/browser_options_page.css
+++ b/chrome/browser/resources/options/browser_options_page.css
@@ -16,6 +16,22 @@
color: #666;
}
+#startupPagesListDropmarker {
+ background-clip: padding-box;
+ background-color: hsl(214, 91%, 65%);
+ border-bottom-color: transparent;
+ border-radius: 0;
+ border-top-color: transparent;
+ border: 2px solid hsl(214, 91%, 65%);
+ box-sizing: border-box;
+ display: none;
+ height: 6px;
+ overflow: hidden;
+ pointer-events: none;
+ position: fixed;
+ z-index: 10;
+}
+
#customHomePageGroup {
display: -webkit-box;
-webkit-box-orient: horizontal;
diff --git a/chrome/browser/resources/options/browser_options_startup_page_list.js b/chrome/browser/resources/options/browser_options_startup_page_list.js
index 9ad47668..84f5516 100644
--- a/chrome/browser/resources/options/browser_options_startup_page_list.js
+++ b/chrome/browser/resources/options/browser_options_startup_page_list.js
@@ -80,6 +80,7 @@ cr.define('options.browser_options', function() {
urlField.addEventListener('blur', function(event) {
self.parentNode.autocompleteList.detach();
});
+ this.draggable = true;
},
/** @inheritDoc */
@@ -117,6 +118,23 @@ cr.define('options.browser_options', function() {
*/
autocompleteList: null,
+ /**
+ * The drop position information: "below" or "above".
+ */
+ dropPos: null,
+
+ /** @inheritDoc */
+ decorate: function() {
+ InlineEditableItemList.prototype.decorate.call(this);
+
+ // Listen to drag and drop events.
+ this.addEventListener('dragstart', this.handleDragStart_.bind(this));
+ this.addEventListener('dragenter', this.handleDragEnter_.bind(this));
+ this.addEventListener('dragover', this.handleDragOver_.bind(this));
+ this.addEventListener('drop', this.handleDrop_.bind(this));
+ this.addEventListener('dragleave', this.handleDragLeave_.bind(this));
+ },
+
/** @inheritDoc */
createItem: function(pageInfo) {
var item = new StartupPageListItem(pageInfo);
@@ -128,6 +146,140 @@ cr.define('options.browser_options', function() {
deleteItemAtIndex: function(index) {
chrome.send('removeStartupPages', [String(index)]);
},
+
+ /*
+ * Computes the target item of drop event.
+ * @param {Event} e The drop or dragover event.
+ * @private
+ */
+ getTargetFromDropEvent_ : function(e) {
+ var target = e.target;
+ // e.target may be an inner element of the list item
+ while (target != null && !(target instanceof StartupPageListItem)) {
+ target = target.parentNode;
+ }
+ return target;
+ },
+
+ /*
+ * Handles the dragstart event.
+ * @param {Event} e The dragstart event.
+ * @private
+ */
+ handleDragStart_: function(e) {
+ var target = e.target;
+ // StartupPageListItem should be the only draggable element type in the
+ // page but let's make sure.
+ if (target instanceof StartupPageListItem) {
+ this.draggedItem = target;
+ this.draggedItem.editable = false;
+ e.dataTransfer.effectAllowed = 'move';
+ // We need to put some kind of data in the drag or it will be
+ // ignored. Use the URL in case the user drags to a text field or the
+ // desktop.
+ e.dataTransfer.setData('text/plain', target.urlField_.value);
+ }
+ },
+
+ /*
+ * Handles the dragenter event.
+ * @param {Event} e The dragenter event.
+ * @private
+ */
+ handleDragEnter_: function(e) {
+ e.preventDefault();
+ },
+
+ /*
+ * Handles the dragover event.
+ * @param {Event} e The dragover event.
+ * @private
+ */
+ handleDragOver_: function(e) {
+ var dropTarget = this.getTargetFromDropEvent_(e);
+ // Determines whether the drop target is to accept the drop.
+ // The drop is only successful on another StartupPageListItem.
+ if (!(dropTarget instanceof StartupPageListItem) ||
+ dropTarget == this.draggedItem || dropTarget.isPlaceholder) {
+ this.hideDropMarker_();
+ return;
+ }
+ // Compute the drop postion. Should we move the dragged item to
+ // below or above the drop target?
+ var rect = dropTarget.getBoundingClientRect();
+ var dy = e.clientY - rect.top;
+ var yRatio = dy / rect.height;
+ var dropPos = yRatio <= .5 ? 'above' : 'below';
+ this.dropPos = dropPos;
+ this.showDropMarker_(dropTarget, dropPos);
+ e.preventDefault();
+ },
+
+ /*
+ * Handles the drop event.
+ * @param {Event} e The drop event.
+ * @private
+ */
+ handleDrop_: function(e) {
+ var dropTarget = this.getTargetFromDropEvent_(e);
+ this.hideDropMarker_();
+
+ // Insert the selection at the new position.
+ var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_);
+ if (this.dropPos == 'below')
+ newIndex += 1;
+
+ var selected = this.selectionModel.selectedIndexes;
+ var stringized_selected = [];
+ for (var j = 0; j < selected.length; j++)
+ stringized_selected.push(String(selected[j]));
+
+ chrome.send('dragDropStartupPage',
+ [String(newIndex), stringized_selected] );
+ },
+
+ /*
+ * Handles the dragleave event.
+ * @param {Event} e The dragleave event
+ * @private
+ */
+ handleDragLeave_ : function(e) {
+ this.hideDropMarker_();
+ },
+
+ /*
+ * Shows and positions the marker to indicate the drop target.
+ * @param {HTMLElement} target The current target list item of drop
+ * @param {string} pos 'below' or 'above'
+ * @private
+ */
+ showDropMarker_ : function(target, pos) {
+ window.clearTimeout(this.hideDropMarkerTimer_);
+ var marker = $('startupPagesListDropmarker');
+ var rect = target.getBoundingClientRect();
+ var markerHeight = 6;
+ if (pos == 'above') {
+ marker.style.top = (rect.top - markerHeight/2) + 'px';
+ } else {
+ marker.style.top = (rect.bottom - markerHeight/2) + 'px';
+ }
+ marker.style.width = rect.width + 'px';
+ marker.style.left = rect.left + 'px';
+ marker.style.display = 'block';
+ },
+
+ /*
+ * Hides the drop marker.
+ * @private
+ */
+ hideDropMarker_ : function() {
+ // Hide the marker in a timeout to reduce flickering as we move between
+ // valid drop targets.
+ window.clearTimeout(this.hideDropMarkerTimer_);
+ this.hideDropMarkerTimer_ = window.setTimeout(function() {
+ $('startupPagesListDropmarker').style.display = '';
+ }, 100);
+ },
};
return {
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 6a31815..3eadb9a 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -114,6 +114,9 @@ void BrowserOptionsHandler::RegisterMessages() {
"setStartupPagesToCurrentPages",
NewCallback(this, &BrowserOptionsHandler::SetStartupPagesToCurrentPages));
web_ui_->RegisterMessageCallback(
+ "dragDropStartupPage",
+ NewCallback(this, &BrowserOptionsHandler::DragDropStartupPage));
+ web_ui_->RegisterMessageCallback(
"requestAutocompleteSuggestions",
NewCallback(this,
&BrowserOptionsHandler::RequestAutocompleteSuggestions));
@@ -405,6 +408,30 @@ void BrowserOptionsHandler::EditStartupPage(const ListValue* args) {
startup_custom_pages_table_model_->SetURLs(urls);
}
+void BrowserOptionsHandler::DragDropStartupPage(const ListValue* args) {
+ CHECK_EQ(args->GetSize(), 2U);
+
+ std::string value;
+ int to_index;
+
+ CHECK(args->GetString(0, &value));
+ base::StringToInt(value, &to_index);
+
+ ListValue* selected;
+ CHECK(args->GetList(1, &selected));
+
+ std::vector<int> index_list;
+ for (size_t i = 0; i < selected->GetSize(); ++i) {
+ int index;
+ CHECK(selected->GetString(i, &value));
+ base::StringToInt(value, &index);
+ index_list.push_back(index);
+ }
+
+ startup_custom_pages_table_model_->MoveURLs(to_index, index_list);
+ SaveStartupPagesPref();
+}
+
void BrowserOptionsHandler::SaveStartupPagesPref() {
PrefService* prefs = web_ui_->GetProfile()->GetPrefs();
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.h b/chrome/browser/ui/webui/options/browser_options_handler.h
index 7264581..021a747 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.h
+++ b/chrome/browser/ui/webui/options/browser_options_handler.h
@@ -79,6 +79,9 @@ class BrowserOptionsHandler : public OptionsPageUIHandler,
// Sets the startup page set to the current pages. Called from WebUI.
void SetStartupPagesToCurrentPages(const ListValue* args);
+ // Writes the current set of startup pages to prefs. Called from WebUI.
+ void DragDropStartupPage(const ListValue* args);
+
// Gets autocomplete suggestions asychronously for the given string.
// Called from WebUI.
void RequestAutocompleteSuggestions(const ListValue* args);