I am creating a fragment containing 2 viewPagers. My goal is to disable user input for the top view pager so that it's scroll will be dependent on the bottom view pager.
so far I've figured out how to detect the drag of bottom viewPager, however I am unable to calculate the parameter of offsetPxFloat required by fakeDragBy() to determine how much scroll is required for the top viewPager.
private val moviePosterVpOnPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int,
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
binding.movieBackdropVp.fakeDragBy(-positionOffsetPixels.toFloat())
}
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)
if (state > 0){
binding.movieBackdropVp.beginFakeDrag()
}else{
binding.movieBackdropVp.endFakeDrag()
}
}
}
Related
I have a spinner with an array adapter. The spinner is populated inside a fragment onCreateView().
spinner.setSelection(0)
spinner.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) {
}
override fun onNothingSelected(var1: AdapterView<*>?) {
}
}
Whenever I get back to the fragment and the spinner is created, the last selected item is selected when onItemSelected() is called automatically and ignoring the spinner.setSelection(0) call.
I have put many logs to see what is going, but I cannot understand why the lately selected item is the one being selected by default and not the one at position 0.
I solved the issue by setting a click listener on the drop down view and basically do the same stuff I was doing with the OnItemSelectedListener.
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val binding = SpinnerItemChartDropdownBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val item = getItem(position)
val root = binding.root
bindDropdown(root, item)
binding.setClickListener {
listener.onChartRangeSelected(item)
}
return root
}
One important stuff. You need to do something like this, to dismiss the drop down view after an item has been selected:
fun hideSpinnerDropDown(spinner: Spinner) {
try {
val method: Method = Spinner::class.java.getDeclaredMethod("onDetachedFromWindow")
method.isAccessible = true
method.invoke(spinner)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
I have an NSTableView where I can drag and drop table rows to reorder them. This works by setting a drag type in my view controller:
#IBOutlet weak var tableView: NSTableView!
let dragType = NSPasteboard.PasteboardType(rawValue: "myapp.task")
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerForDraggedTypes([dragType])
}
...and then implementing the reordering with these table delegate methods:
//Start drag
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: dragType)
return item
}
//Verify proposed drop
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}else{
return []
}
}
//Accept drop of one or multiple rows
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { dragItem, _, _ in
if let str = (dragItem.item as! NSPasteboardItem).string(forType: self.dragType), let index = Int(str) {
oldIndexes.append(index)
}
}
//Do a bunch of logic to reorder the table rows...
}
Now, in addition to reordering my table rows, I want to be able to drag a row and drop it somewhere else in my app--sort of like moving the row to a different place.
I have a custom NSView set up as the drag destination for this, and I can drag a table row and the custom view reacts appropriately with a table row dragged over it:
class MyCustomView: NSView{
required init?(coder: NSCoder) {
super.init(coder: coder)
let taskDragType = NSPasteboard.PasteboardType(rawValue: "myapp.task")
registerForDraggedTypes([taskDragType])
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
//...
}
override func draggingExited(_ sender: NSDraggingInfo?) {
//...
}
}
But the part I'm unclear on is how to get the table row, and its associated object set as a property on the NSTableCellView, when the drop occurs:
//This is another method in MyCustomView
override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool {
guard let items = draggingInfo.draggingPasteboard.pasteboardItems else{ return false }
for item in items{
print("---")
print(item) //<-- NSPasteboardItem
let index = item.propertyList(forType: NSPasteboard.PasteboardType(rawValue: "myapp.task"))
print(index) //<-- Index of the table row
//How can I also get the task object associated with the row?
}
}
I can get the index of the row, but what I need is the entire object from the row's data source so I can take action on the object it represents. My suspicion is that I need to change how I'm using pasteboardWriterForRow to put my object on the pasteboard, but I'm unsure how to do that.
How can I pass both the row index and the object to the pasteboard?
Soon after posting this, I decided to try something crazy, and it turns out I found a way to make this a lot simpler. It seems that NSPasteboard is really only necessary if you need to get stuff into and out of your app. Since I am just moving something from one part of my app to another, I can use the drag and drop delegate methods as events and handle the data myself.
First, I set up a global array for adding dragged task objects:
var draggedTasks = [Task]()
Whenever a task is dragged from my NSTableView, I add them to the array in the aforementioned delegate method where dragging starts:
//Start drag
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
//Queue tasks for moving to phases or projects
draggedTasks.append(tasks[row])
//Queue row for reordering
let item = NSPasteboardItem()
item.setString(String(row), forType: dragType)
return item
}
Then where I accept the drop in MyCustomView, I take action on the draggedTasks array:
//Save dropped tasks
override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool {
//Do stuff to draggedTasks based on the context of where they are dropped
return true
}
This is much simpler than going down the NSPasteboard route. 🙂
I have a text field that I want to limit to integers only. See the code below.
When the view containing the field starts, and if the model is constructed with an initial default value for someInteger, the view displays the number correctly, without extra formatting. It also filters new typed input as expected.
A problem arises when refactoring the model not to have a default value. Being an integer property, it defaults to 0. When I later assign a new value to the property, the controlNewText passed contains punctuation, such as 1,234. That causes the check to fail and the newly assigned value to be filtered out of the view.
Why is the controlNewText getting formatted in the first place? Is there a way to prevent that?
textfield(model.someInteger) {
required()
textFormatter = TextFormatter(IntegerStringConverter(), model.item.someInteger)
stripNonInteger()
filterInput { it.controlNewText.isInt() }
}
class SomeData {
val someIntegerProperty = SimpleIntegerProperty(this, "someInteger")
var someInteger by someIntegerProperty
}
class SomeDataModel : ItemViewModel<SomeData>(SomeData()) {
val someInteger = bind(SomeData::someIntegerProperty)
}
The formatting is performed by the TextFormatter you specified. Make sure to specify one that doesn't add thousand separators. Here is a complete runnable application that configures a NumberStringConverter inside the formatter. Notice that I've removed the filterInput statement, as that's already covered by stripNonInteger.
class MainView : View("Main view") {
val model = SomeDataModel()
override val root = borderpane {
center {
form {
fieldset {
field("Some integer") {
textfield(model.someInteger) {
required()
textFormatter = TextFormatter(NumberStringConverter("########"), model.someInteger.value)
stripNonInteger()
}
}
button("Set some value").action {
model.someInteger.value = 1234
}
}
}
}
}
}
class SomeData {
val someIntegerProperty = SimpleIntegerProperty(this, "someInteger")
var someInteger by someIntegerProperty
}
class SomeDataModel : ItemViewModel<SomeData>(SomeData()) {
val someInteger = bind(SomeData::someIntegerProperty)
}
I have a content part that provides a begin timestamp and end timestamp option. These 2 fields are used to define a period of time in which the content item should be displayed.
I now have difficulties to implement a skip approach whereas content items should not be displayed / skipped when the period of time does not span the current time.
Digging in the source code and trying to find an entry point for my approach resulted in the following content handler
public class SkipContentHandler : Orchard.ContentManagement.Handlers.ContentHandler
{
protected override void BuildDisplayShape(Orchard.ContentManagement.Handlers.BuildDisplayContext aContext)
{
if (...) // my condition to process only content shapes which need to be skipped
{
aContext.Shape = null; // return null shape to skip it
}
}
}
This works but there are several side effects
I had to alter the source code of BuildDisplayContext as the Shape is normally read only
List shape may displayed a wrong pager when it contains content items with my content part because the Count() call in ContainerPartDriver.Display() is executed before BuildDisplay()
calling the URL of a content item that is skipped results in an exception because View(null) is abigious
So, what would be the correct approach here or is there any module in existence that does the job? I couldn't find one.
This is a quite complex task. There are several steps needed to achieve a proper skipping of display items:
Create the part correctly
There are a few pitfalls here as when coming to the task of adding a part view one might utilize Orchards date time editor in connection with the DateTime properties. But this brings a heck of a lot of additional issues to the table but these don't really relate to the question.
If someone is interested in how to use Orchards date time editor then i can post this code too, but for now it would only blow up the code unnecessarly.
So here we go, the part class...
public class ValidityPart : Orchard.ContentManagement.ContentPart<ValidityPartRecord>
{
// public
public System.DateTime? ValidFromUtc
{
get { return Retrieve(r => r.ValidFromUtc); }
set { Store(r => r.ValidFromUtc, value); }
}
...
public System.DateTime? ValidTillUtc
{
get { return Retrieve(r => r.ValidTillUtc); }
set { Store(r => r.ValidTillUtc, value); }
}
...
public bool IsContentItemValid()
{
var lUtcNow = System.DateTime.UtcNow;
return (ValidFromUtc == null || ValidFromUtc.Value <= lUtcNow) && (ValidTillUtc == null || ValidTillUtc.Value >= lUtcNow);
}
...
}
...and the record class...
public class ValidityPartRecord : Orchard.ContentManagement.Records.ContentPartRecord
{
// valid from value as UTC to use Orchard convention (see CommonPart table) and to be compatible with projections
// (date/time tokens work with UTC values, see https://github.com/OrchardCMS/Orchard/issues/6963 for a related issue)
public virtual System.DateTime? ValidFromUtc { get; set; }
// valid from value as UTC to use Orchard convention (see CommonPart table) and to be compatible with projections
// (date/time tokens work with UTC values, see https://github.com/OrchardCMS/Orchard/issues/6963 for a related issue)
public virtual System.DateTime? ValidTillUtc { get; set; }
}
Create a customized content query class
public class MyContentQuery : Orchard.ContentManagement.DefaultContentQuery
{
// public
public ContentQuery(Orchard.ContentManagement.IContentManager aContentManager,
Orchard.Data.ITransactionManager aTransactionManager,
Orchard.Caching.ICacheManager aCacheManager,
Orchard.Caching.ISignals aSignals,
Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentTypeRecord> aContentTypeRepository,
Orchard.IWorkContextAccessor aWorkContextAccessor)
: base(aContentManager, aTransactionManager, aCacheManager, aSignals, aContentTypeRepository)
{
mWorkContextAccessor = aWorkContextAccessor;
}
protected override void BeforeExecuteQuery(NHibernate.ICriteria aContentItemVersionCriteria)
{
base.BeforeExecuteQuery(aContentItemVersionCriteria);
// note:
// this method will be called each time a query for multiple items is going to be executed (e.g. content items of a container, layers, menus),
// this gives us the chance to add a validity criteria
var lWorkContext = mWorkContextAccessor.GetContext();
// exclude admin as content items should still be displayed / accessible when invalid as validity needs to be editable
if (lWorkContext == null || !Orchard.UI.Admin.AdminFilter.IsApplied(lWorkContext.HttpContext.Request.RequestContext))
{
var lUtcNow = System.DateTime.UtcNow;
// left outer join of ValidityPartRecord table as part is optional (not present on all content types)
var ValidityPartRecordCriteria = aContentItemVersionCriteria.CreateCriteria(
"ContentItemRecord.ValidityPartRecord", // string adopted from foreach loops in Orchard.ContentManagement.DefaultContentQuery.WithQueryHints()
NHibernate.SqlCommand.JoinType.LeftOuterJoin
);
// add validity criterion
ValidityPartRecordCriteria.Add(
NHibernate.Criterion.Restrictions.And(
NHibernate.Criterion.Restrictions.Or(
NHibernate.Criterion.Restrictions.IsNull("ValidFromUtc"),
NHibernate.Criterion.Restrictions.Le("ValidFromUtc", lUtcNow)
),
NHibernate.Criterion.Restrictions.Or(
NHibernate.Criterion.Restrictions.IsNull("ValidTillUtc"),
NHibernate.Criterion.Restrictions.Ge("ValidTillUtc", lUtcNow)
)
)
);
}
}
// private
Orchard.IWorkContextAccessor mWorkContextAccessor;
}
This essentially adds a left join of the validity part fields to the SQL query (content query) and extends the WHERE statement with the validity condition.
Please note that this step is only possible with the solution described the following issue: https://github.com/OrchardCMS/Orchard/issues/6978
Register the content query class
public class ContentModule : Autofac.Module
{
protected override void Load(Autofac.ContainerBuilder aBuilder)
{
aBuilder.RegisterType<MyContentQuery>().As<Orchard.ContentManagement.IContentQuery>().InstancePerDependency();
}
}
Create a customized content manager
public class ContentManager : Orchard.ContentManagement.DefaultContentManager
{
// public
public ContentManager(
Autofac.IComponentContext aContext,
Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentTypeRecord> aContentTypeRepository,
Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentItemRecord> aContentItemRepository,
Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentItemVersionRecord> aContentItemVersionRepository,
Orchard.ContentManagement.MetaData.IContentDefinitionManager aContentDefinitionManager,
Orchard.Caching.ICacheManager aCacheManager,
System.Func<Orchard.ContentManagement.IContentManagerSession> aContentManagerSession,
System.Lazy<Orchard.ContentManagement.IContentDisplay> aContentDisplay,
System.Lazy<Orchard.Data.ITransactionManager> aTransactionManager,
System.Lazy<System.Collections.Generic.IEnumerable<Orchard.ContentManagement.Handlers.IContentHandler>> aHandlers,
System.Lazy<System.Collections.Generic.IEnumerable<Orchard.ContentManagement.IIdentityResolverSelector>> aIdentityResolverSelectors,
System.Lazy<System.Collections.Generic.IEnumerable<Orchard.Data.Providers.ISqlStatementProvider>> aSqlStatementProviders,
Orchard.Environment.Configuration.ShellSettings aShellSettings,
Orchard.Caching.ISignals aSignals,
Orchard.IWorkContextAccessor aWorkContextAccessor)
: base(aContext, aContentTypeRepository, aContentItemRepository, aContentItemVersionRepository, aContentDefinitionManager, aCacheManager, aContentManagerSession,
aContentDisplay, aTransactionManager, aHandlers, aIdentityResolverSelectors, aSqlStatementProviders, aShellSettings, aSignals)
{
mWorkContextAccessor = aWorkContextAccessor;
}
public override ContentItem Get(int aId, Orchard.ContentManagement.VersionOptions aOptions, Orchard.ContentManagement.QueryHints aHints)
{
var lResult = base.Get(aId, aOptions, aHints);
if (lResult != null)
{
// note:
// the validity check is done here (after the query has been executed!) as changing base.GetManyImplementation() to
// apply the validity critera directly to the query (like in ContentQuery) will not work due to a second attempt to retrieve the
// content item from IRepository<> (see base.GetManyImplementation(), comment "check in memory") when the query
// returns no data (and the query should not return data when the validity critera is false)
//
// http://stackoverflow.com/q/37841249/3936440
var lWorkContext = mWorkContextAccessor.GetContext();
// exclude admin as content items should still be displayed / accessible when invalid as validity needs to be editable
if (lWorkContext == null || !Orchard.UI.Admin.AdminFilter.IsApplied(lWorkContext.HttpContext.Request.RequestContext))
{
var lValidityPart = lResult.As<ValidityPart>();
if (lValidityPart != null)
{
if (lValidityPart.IsContentItemValid())
{
// content item is valid
}
else
{
// content item is not valid, return null (adopted from base.Get())
lResult = null;
}
}
}
}
return lResult;
}
// private
Orchard.IWorkContextAccessor mWorkContextAccessor;
}
Steps 2-4 are needed when having content items whereas the content type has a Container and Containable part or even content items which are processed / displayed separately. Here you normally cannot customize the content query that is executed behind the scenes.
Steps 2-4 are not needed if you use the Projection module. But again, this brings a few other issues to the table as reported in this issue: https://github.com/OrchardCMS/Orchard/issues/6979
Here is a script for a Drag n Drop game which I am working on. I've made each object draggable, and now I want the objects to return to its original positions once they're not dropped in its current location.
ie;I want to drag an object to a particular place.In case I release that dragged object before I reach that particular area ,the object should come to its initial place where I object was dragged from
public function mouseDownEvent(mouseEvent:MouseEvent):void
{
draging = true;
}
public function mouseUpEvent(mouseEvent:MouseEvent):void
{
draging = false;
selectedObject.stopDrag();
}
public function mouseMoveEvent(mouseEvent:MouseEvent):void
{
if(draging==true)
{
selectedObject.startDrag(true);
}
else
{
selectedObject.stopDrag();
}
}
You can use
selectedObject.setPosition(info.point.x,info.point.y,info.point.z);