kicad-source/include/tool/construction_manager.h
John Beard e23b83505e Snapping: delayed activation of snap points.
This makes it easier to control what snap points you are aiming for
without accidentally activating objects just by mousing near them
on the way to somewhere else.
2024-10-01 18:06:27 +01:00

268 lines
9.3 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma once
#include <vector>
#include <deque>
#include <preview_items/construction_geom.h>
template <typename T>
class ACTIVATION_HELPER;
class EDA_ITEM;
/**
* Interface wrapper for the construction geometry preview,
* with a callback to signal the view owner that the view needs to be updated.
*/
class CONSTRUCTION_VIEW_HANDLER
{
public:
CONSTRUCTION_VIEW_HANDLER( KIGFX::CONSTRUCTION_GEOM& aHelper ) :
m_constructionGeomPreview( aHelper )
{
}
virtual void updateView() = 0;
KIGFX::CONSTRUCTION_GEOM& GetViewItem() { return m_constructionGeomPreview; }
private:
// An (external) construction helper view item, that this manager adds/removes
// construction objects to/from.
KIGFX::CONSTRUCTION_GEOM& m_constructionGeomPreview;
};
/**
* A class that manages the geometry of a "snap line".
*
* This is a line that has a start point (the "snap origin") and an end point (the "snap end").
* The end can only be set if the origin is set. If the origin is set, the end will be unset.
*/
class SNAP_LINE_MANAGER
{
public:
SNAP_LINE_MANAGER( CONSTRUCTION_VIEW_HANDLER& aViewHandler );
/**
* The snap point is a special point that is located at the last point the cursor
* snapped to. If it is set, the construction manager may add extra construction
* geometry to the helper extending from the snap point origin to the cursor,
* which is the 'snap line'.
*/
void SetSnapLineOrigin( const VECTOR2I& aOrigin );
/**
* Set the end point of the snap line.
*
* Passing std::nullopt will unset the end point, but keep the origin.
*/
void SetSnapLineEnd( const OPT_VECTOR2I& aSnapPoint );
/**
* Clear the snap line origin and end points.
*/
void ClearSnapLine();
const OPT_VECTOR2I& GetSnapLineOrigin() const { return m_snapLineOrigin; }
bool HasCompleteSnapLine() const { return m_snapLineOrigin && m_snapLineEnd; }
/**
* Inform this manager that an anchor snap has been made.
*
* This will also update the start or end of the snap line as appropriate.
*/
void SetSnappedAnchor( const VECTOR2I& aAnchorPos );
/**
* If the snap line is active, return the best snap point that is closest to the cursor
*
* If there's no active snap line, return std::nullopt.
*
* If there's a snap very near, use that otherwise, use the grid point.
* With this point, snap to it on an H/V axis.
*
* Then, if there's a grid point near, snap to it on an H/V axis
*
* @param aCursor The cursor position
* @param aNearestGrid The nearest grid point to the cursor
* @param aDistToNearest The distance to the nearest non-grid snap point, if any
* @param snapRange The snap range
*/
OPT_VECTOR2I GetNearestSnapLinePoint( const VECTOR2I& aCursor, const VECTOR2I& aNearestGrid,
std::optional<int> aDistToNearest, int snapRange ) const;
private:
// If a snap point is "active", extra construction geometry is added to the helper
// extending from the snap point to the cursor.
OPT_VECTOR2I m_snapLineOrigin;
OPT_VECTOR2I m_snapLineEnd;
// The view handler to update when the snap line changes
CONSTRUCTION_VIEW_HANDLER& m_viewHandler;
};
/**
* A class that mananges "construction" objects and geometry.
* These are things like line extensions, arc centers, etc.
*/
class CONSTRUCTION_MANAGER
{
public:
CONSTRUCTION_MANAGER( CONSTRUCTION_VIEW_HANDLER& aViewHandler );
~CONSTRUCTION_MANAGER();
enum class SOURCE
{
FROM_ITEMS,
FROM_SNAP_LINE,
};
/**
* Items to be used for the construction of "virtual" anchors, for example, when snapping to
* a point involving an _extension_ of an existing line or arc.
*
* One item can have multiple construction items (e.g. an arc can have a circle and centre point).
*/
struct CONSTRUCTION_ITEM
{
SOURCE Source;
EDA_ITEM* Item;
std::vector<KIGFX::CONSTRUCTION_GEOM::DRAWABLE> Constructions;
};
// A single batch of construction items. Once batch contains all the items (and associated
// construction geometry) that should be shown for one point of interest.
// More than one batch may be shown on the screen at the same time.
using CONSTRUCTION_ITEM_BATCH = std::vector<CONSTRUCTION_ITEM>;
/**
* Add a batch of construction items to the helper.
*
* @param aBatch The batch of construction items to add.
* @param aIsPersistent If true, the batch is considered "persistent" and will always be shown
* (and it will replace any previous persistent batch).
* If false, the batch is temporary and may be pushed out by other batches.
*/
void ProposeConstructionItems( CONSTRUCTION_ITEM_BATCH aBatch, bool aIsPersistent );
/**
* Cancel outstanding proposals for new geometry.
*/
void CancelProposal();
/**
* Check if all 'real' (non-null = constructed) the items in the batch are in the list of items
* currently 'involved' in an active construction.
*/
bool InvolvesAllGivenRealItems( const std::vector<EDA_ITEM*>& aItems ) const;
// Get the list of additional geometry items that should be considered
void GetConstructionItems( std::vector<CONSTRUCTION_ITEM_BATCH>& aToExtend ) const;
bool HasActiveConstruction() const;
private:
struct PENDING_BATCH;
void acceptConstructionItems( PENDING_BATCH&& aAcceptedBatchHash );
CONSTRUCTION_VIEW_HANDLER& m_viewHandler;
// Within one "operation", there is one set of construction items that are
// "persistent", and are always shown. Usually the original item and any
// extensions.
std::optional<CONSTRUCTION_ITEM_BATCH> m_persistentConstructionBatch;
// Temporary construction items are added and removed as needed
std::deque<CONSTRUCTION_ITEM_BATCH> m_temporaryConstructionBatches;
// Set of all items for which construction geometry has been added
std::set<EDA_ITEM*> m_involvedItems;
std::unique_ptr<ACTIVATION_HELPER<PENDING_BATCH>> m_activationHelper;
};
/**
* A SNAP_MANAGER glues together the snap line manager and construction manager.,
* along with some other state. It provides information for generating snap
* anchors based on this state, as well as keeping the state of visible
* construction geometry involved in that process.
*
* Probably only used by GRID_HELPERs, but it's neater to keep it separate,
* as there's quite a bit of state to manage.
*
* This is also where you may wish to add other 'virtual' snapping state,
* such as 'equal-space' snapping, etc.
*/
class SNAP_MANAGER : public CONSTRUCTION_VIEW_HANDLER
{
public:
using GFX_UPDATE_CALLBACK = std::function<void( bool aShowAnything )>;
SNAP_MANAGER( KIGFX::CONSTRUCTION_GEOM& aHelper );
/**
* Set the callback to call when the construction geometry changes and a view may need updating.
*/
void SetUpdateCallback( GFX_UPDATE_CALLBACK aCallback ) { m_updateCallback = aCallback; }
SNAP_LINE_MANAGER& GetSnapLineManager() { return m_snapLineManager; }
CONSTRUCTION_MANAGER& GetConstructionManager() { return m_constructionManager; }
/**
* Set the reference-only points - these are points that are not snapped to, but can still
* be used for connection to the snap line.
*/
void SetReferenceOnlyPoints( std::vector<VECTOR2I> aPoints )
{
m_referenceOnlyPoints = std::move( aPoints );
}
const std::vector<VECTOR2I>& GetReferenceOnlyPoints() const { return m_referenceOnlyPoints; }
/**
* Get a list of all the active construction geometry, computed from
* the combined state of the snap line and construction manager.
*
* This can be combined with other external geometry to compute snap anchors.
*/
std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH> GetConstructionItems() const;
public:
void updateView() override;
GFX_UPDATE_CALLBACK m_updateCallback;
SNAP_LINE_MANAGER m_snapLineManager;
CONSTRUCTION_MANAGER m_constructionManager;
std::vector<VECTOR2I> m_referenceOnlyPoints;
};