Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/client/framework/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  toolbox-tabs-order-manager.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


"use strict";

const { AddonManager } = ChromeUtils.importESModule(
  "resource://gre/modules/AddonManager.sys.mjs",
  // AddonManager is a singleton, never create two instances of it.
  { global: "shared" }
);
const {
  gDevTools,
} = require("resource://devtools/client/framework/devtools.js");
const PREFERENCE_NAME = "devtools.toolbox.tabsOrder";

/**
 * Manage the order of devtools tabs.
 */

class ToolboxTabsOrderManager {
  constructor(toolbox, onOrderUpdated, panelDefinitions) {
    this.toolbox = toolbox;
    this.onOrderUpdated = onOrderUpdated;
    this.currentPanelDefinitions = panelDefinitions || [];

    this.onMouseDown = this.onMouseDown.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.onMouseUp = this.onMouseUp.bind(this);

    Services.prefs.addObserver(PREFERENCE_NAME, this.onOrderUpdated);
  }

  async destroy() {
    Services.prefs.removeObserver(PREFERENCE_NAME, this.onOrderUpdated);

    // Call mouseUp() to clear the state to prepare for in case a dragging was in progress
    // when the destroy() was called.
    await this.onMouseUp();
  }

  insertBefore(target) {
    const xBefore = this.dragTarget.offsetLeft;
    this.toolboxTabsElement.insertBefore(this.dragTarget, target);
    const xAfter = this.dragTarget.offsetLeft;
    this.dragStartX += xAfter - xBefore;
    this.isOrderUpdated = true;
  }

  isFirstTab(tabElement) {
    return !tabElement.previousSibling;
  }

  isLastTab(tabElement) {
    return (
      !tabElement.nextSibling ||
      tabElement.nextSibling.id === "tools-chevron-menu-button"
    );
  }

  isRTL() {
    return this.toolbox.direction === "rtl";
  }

  async saveOrderPreference() {
    const tabs = [...this.toolboxTabsElement.querySelectorAll(".devtools-tab")];
    const tabIds = tabs.map(tab => tab.dataset.extensionId || tab.dataset.id);
    // Concat the overflowed tabs id since they are not contained in visible tabs.
    // The overflowed tabs cannot be reordered so we just append the id from current
    // panel definitions on their order.
    const overflowedTabIds = this.currentPanelDefinitions
      .filter(definition => !tabs.some(tab => tab.dataset.id === definition.id))
      .map(definition => definition.extensionId || definition.id);
    const currentTabIds = tabIds.concat(overflowedTabIds);
    const dragTargetId =
      this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
    const prefIds = getTabsOrderFromPreference();
    const absoluteIds = toAbsoluteOrder(prefIds, currentTabIds, dragTargetId);

    // Remove panel id which is not in panel definitions and addons list.
    const extensions = await AddonManager.getAllAddons();
    const definitions = gDevTools.getToolDefinitionArray();
    const result = absoluteIds.filter(
      id =>
        definitions.find(d => id === (d.extensionId || d.id)) ||
        extensions.find(e => id === e.id)
    );

    Services.prefs.setCharPref(PREFERENCE_NAME, result.join(","));
  }

  setCurrentPanelDefinitions(currentPanelDefinitions) {
    this.currentPanelDefinitions = currentPanelDefinitions;
  }

  onMouseDown(e) {
    if (!e.target.classList.contains("devtools-tab")) {
      return;
    }

    this.dragStartX = e.pageX;
    this.dragTarget = e.target;
    this.previousPageX = e.pageX;
    this.toolboxContainerElement =
      this.dragTarget.closest("#toolbox-container");
    this.toolboxTabsElement = this.dragTarget.closest(".toolbox-tabs");
    this.isOrderUpdated = false;
    this.eventTarget = this.dragTarget.ownerGlobal.top;

    this.eventTarget.addEventListener("mousemove"this.onMouseMove);
    this.eventTarget.addEventListener("mouseup"this.onMouseUp);

    this.toolboxContainerElement.classList.add("tabs-reordering");
  }

  onMouseMove(e) {
    const diffPageX = e.pageX - this.previousPageX;
    let dragTargetCenterX =
      this.dragTarget.offsetLeft + diffPageX + this.dragTarget.clientWidth / 2;
    let isDragTargetPreviousSibling = false;

    const tabElements =
      this.toolboxTabsElement.querySelectorAll(".devtools-tab");

    // Calculate the minimum and maximum X-offset that can be valid for the drag target.
    const firstElement = tabElements[0];
    const firstElementCenterX =
      firstElement.offsetLeft + firstElement.clientWidth / 2;
    const lastElement = tabElements[tabElements.length - 1];
    const lastElementCenterX =
      lastElement.offsetLeft + lastElement.clientWidth / 2;
    const max = Math.max(firstElementCenterX, lastElementCenterX);
    const min = Math.min(firstElementCenterX, lastElementCenterX);

    // Normalize the target center X so to remain between the first and last tab.
    dragTargetCenterX = Math.min(max, dragTargetCenterX);
    dragTargetCenterX = Math.max(min, dragTargetCenterX);

    for (const tabElement of tabElements) {
      if (tabElement === this.dragTarget) {
        isDragTargetPreviousSibling = true;
        continue;
      }

      // Is the dragTarget near the center of the other tab?
      const anotherCenterX = tabElement.offsetLeft + tabElement.clientWidth / 2;
      const distanceWithDragTarget = Math.abs(
        dragTargetCenterX - anotherCenterX
      );
      const isReplaceable = distanceWithDragTarget < tabElement.clientWidth / 3;

      if (isReplaceable) {
        const replaceableElement = isDragTargetPreviousSibling
          ? tabElement.nextSibling
          : tabElement;
        this.insertBefore(replaceableElement);
        break;
      }
    }

    let distance = e.pageX - this.dragStartX;

    // To accomodate for RTL locales, we cannot rely on the first/last element of the
    // NodeList. We cannot have negative distances for the leftmost tab, and we cannot
    // have positive distances for the rightmost tab.
    const isFirstTab = this.isFirstTab(this.dragTarget);
    const isLastTab = this.isLastTab(this.dragTarget);
    const isLeftmostTab = this.isRTL() ? isLastTab : isFirstTab;
    const isRightmostTab = this.isRTL() ? isFirstTab : isLastTab;

    if ((isLeftmostTab && distance < 0) || (isRightmostTab && distance > 0)) {
      // If the drag target is already edge of the tabs and the mouse will make the
      // element to move to same direction more, keep the position.
      distance = 0;
    }

    this.dragTarget.style.left = `${distance}px`;
    this.previousPageX = e.pageX;
  }

  async onMouseUp() {
    if (!this.dragTarget) {
      // The case in here has two type:
      // 1. Although destroy method was called, it was not during reordering.
      // 2. Although mouse event occur, destroy method was called during reordering.
      return;
    }

    if (this.isOrderUpdated) {
      await this.saveOrderPreference();

      // Log which tabs reordered. The question we want to answer is:
      // "How frequently are the tabs re-ordered, also which tabs get re-ordered?"
      const toolId =
        this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
      Glean.devtoolsToolbox.tabsReordered[toolId].add(1);
    }

    this.eventTarget.removeEventListener("mousemove"this.onMouseMove);
    this.eventTarget.removeEventListener("mouseup"this.onMouseUp);

    this.toolboxContainerElement.classList.remove("tabs-reordering");
    this.dragTarget.style.left = null;
    this.dragTarget = null;
    this.toolboxContainerElement = null;
    this.toolboxTabsElement = null;
    this.eventTarget = null;
  }
}

function getTabsOrderFromPreference() {
  const pref = Services.prefs.getCharPref(PREFERENCE_NAME, "");
  return pref ? pref.split(",") : [];
}

function sortPanelDefinitions(definitions) {
  const toolIds = getTabsOrderFromPreference();

  return definitions.sort((a, b) => {
    let orderA = toolIds.indexOf(a.extensionId || a.id);
    let orderB = toolIds.indexOf(b.extensionId || b.id);
    orderA = orderA < 0 ? Number.MAX_VALUE : orderA;
    orderB = orderB < 0 ? Number.MAX_VALUE : orderB;
    return orderA - orderB;
  });
}

/*
 * This function returns absolute tab ids that were merged the both ids that are in
 * preference and tabs.
 * Some tabs added with add-ons etc show/hide depending on conditions.
 * However, all of tabs that include hidden tab always keep the relationship with
 * left side tab, except in case the left tab was target of dragging. If the left
 * tab has been moved, it keeps its relationship with the tab next to it.
 *
 * Case 1: Drag a tab to left
 *   currentTabIds: [T1, T2, T3, T4, T5]
 *   prefIds      : [T1, T2, T3, E1(hidden), T4, T5]
 *   drag T4      : [T1, T2, T4, T3, T5]
 *   result       : [T1, T2, T4, T3, E1, T5]
 *
 * Case 2: Drag a tab to right
 *   currentTabIds: [T1, T2, T3, T4, T5]
 *   prefIds      : [T1, T2, T3, E1(hidden), T4, T5]
 *   drag T2      : [T1, T3, T4, T2, T5]
 *   result       : [T1, T3, E1, T4, T2, T5]
 *
 * Case 3: Hidden tab was left end and drag a tab to left end
 *   currentTabIds: [T1, T2, T3, T4, T5]
 *   prefIds      : [E1(hidden), T1, T2, T3, T4, T5]
 *   drag T4      : [T4, T1, T2, T3, T5]
 *   result       : [E1, T4, T1, T2, T3, T5]
 *
 * Case 4: Hidden tab was right end and drag a tab to right end
 *   currentTabIds: [T1, T2, T3, T4, T5]
 *   prefIds      : [T1, T2, T3, T4, T5, E1(hidden)]
 *   drag T1      : [T2, T3, T4, T5, T1]
 *   result       : [T2, T3, T4, T5, E1, T1]
 *
 * @param Array - prefIds: id array of preference
 * @param Array - currentTabIds: id array of appearanced tabs
 * @param String - dragTargetId: id of dragged target
 * @return Array
 */

function toAbsoluteOrder(prefIds, currentTabIds, dragTargetId) {
  currentTabIds = [...currentTabIds];
  let indexAtCurrentTabs = 0;

  for (const prefId of prefIds) {
    if (prefId === dragTargetId) {
      // do nothing
    } else if (currentTabIds.includes(prefId)) {
      indexAtCurrentTabs = currentTabIds.indexOf(prefId) + 1;
    } else {
      currentTabIds.splice(indexAtCurrentTabs, 0, prefId);
      indexAtCurrentTabs += 1;
    }
  }

  return currentTabIds;
}

module.exports.ToolboxTabsOrderManager = ToolboxTabsOrderManager;
module.exports.sortPanelDefinitions = sortPanelDefinitions;
module.exports.toAbsoluteOrder = toAbsoluteOrder;

98%


¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.