I tried this code to get the first visible row in a scrolling Table inside a BorderLayout.CENTER, but it didn't work, seems the points returned do not reflect the visible cells, unless I am missing a sort of calculation,
thank you for your insights,
#Override
protected void onScrollY(int scrollY) {
super.onScrollY(scrollY); //To change body of generated methods, choose Tools | Templates.
Component c=getComponentAt(50, scrollY);
if (c instanceof Table){
System.err.println("table "+getWidth()+" "+getHeight()+" s "+scrollY);
return;
}
Button b=(Button) c;
System.err.println("c: "+b.getText());
}
getComponentAt(x,y) takes absolute (screen) coordinates. The scrollY value is a relative coordinate in that container.
So what you want is something like:
Component c = getComponentAt(getAbsoluteX()+50, getAbsoluteY() + scrollY)
Also worth nothing that getComponentAt(x,y) will only return components that are focusable or have been set to grab pointer events. If you just want to find the first paintable immediate child of this container, and you're using a BoxLayout.Y_AXIS layout, then you might be better to just iterate through the children until you find one where y is at least the scrollY.
e.g.
Component c = null;
for (Component child : this) {
if (child.getY() + child.getHeight() > scrollY) {
c = child;
break;
}
}
....
Related
I am trying to set up a system that displays the ground loot near the player (i.e., actors that are overlapping the player's UBoxComponent). This needs to change as and when the player moves and the ground loot actors are no longer being overlapped.
I have sort of got it working - when the actors are on their own the function seems to work. However, when two ground loot actors are close to one another, the inventory displays multiple widgets for the same overlapping actor on the floor and sometimes only recognises one of the actors on the floor. It seems to be having trouble when the Player's UBoxComponent overlaps more than one actor at a time. I'll link a YouTube video to help show the issue I'm having because it's quite hard to describe: https://youtu.be/a_zMl1zOUDc
This is the function for updating the ground loot widgets. It is called by a dynamic multicast delegate that is broadcasted from the player class:
// Iterates through the ground loot around the player and displays a widget for each item
void UInventory::UpdateGroundLoot()
{
MyPlayer = Cast<AMainCharacter>(GetOwningPlayer()->GetCharacter());
if(!MyPlayer) { return; } // Null check
// Get a ref to the Player's inventory component
UInventoryComponent* PlayerInventoryComp = MyPlayer->PlayerInventory;
// Clear any current widgets in the ground loot list
GroundLootScrollBox->ClearChildren();
// GroundLootActorsArray is an array of AActors that gets added to/emptied based on AActors that overlap with the Player's UBoxComponent
if(!PlayerInventoryComp && !PlayerInventoryComp->GroundLootActorsArray.IsValidIndex(0)) { return; } // Null check
for(int32 i = 0; i < PlayerInventoryComp->GroundLootActorsArray.Num(); i++)
{
// Cast each element of the GroundLootItems array to AItem* GroundItem
AItem* GroundItem = Cast<AItem>(PlayerInventoryComp->GroundLootActorsArray[i]);
if(!GroundItem) { return; } // Null check
// Add GroundItem to a new array (GroundLootItemsArray). This is an array specifically of AItems (rather than AActors).
GroundLootItemsArray.Add(GroundItem);
GroundLootWidget = CreateWidget<UInventoryItemWidget>(GetOwningPlayer(), InventoryItemWidgetClass);
// Add each created widget to an array of widgets called GroundLootWidgetsArray
GroundLootWidgetsArray.Add(GroundLootWidget);
if(!GroundLootWidgetsArray.IsValidIndex(0) && !GroundLootItemsArray.IsValidIndex(0)) { return; } // Null check
// Display item specific names/thumbnails for each widget created
GroundLootWidgetsArray[i]->ItemName->SetText(GroundLootItemsArray[i]->ItemDisplayName);
GroundLootWidgetsArray[i]->Thumbnail->SetBrushFromTexture(GroundLootItemsArray[i]->Thumbnail);
// Add each array element of the GroundLootWidgetsArray to the scroll box in the widget blueprint
GroundLootScrollBox->AddChild(GroundLootWidgetsArray[i]);
}
}
DevilsD in the comments pointed me in the right direction here. There were two main issues I had.
The delegate that was designed to update the ground loot widgets was being broadcasted in a for loop for every element of the OverlappingActor array. I think this resulted in multiple widgets being displayed for the same actor. The code in the Player's class now looks like this (TriggerEnter and TriggerExit are called by the OnComponentBeginOverlap and OnComponentEndOverlap delegates in the constructor respectively) :
void AMainCharacter::TriggerEnter(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
bItemIsWithinRange = true;
// I think the TSubclassOf ensures that we only pickup AItem classes and it's children - rather than all actors on the ground
OverlappedComponent->GetOverlappingActors(OverlappingActors, TSubclassOf<AItem>(AItem::StaticClass()));
if(PlayerInventory && OverlappingActors.IsValidIndex(0))
{
for(auto& GroundItems : OverlappingActors)
{
PlayerInventory->AddItemToGroundLoot(GroundItems);
}
OnUpdateGroundLoot.Broadcast();
}
}
void AMainCharacter::TriggerExit(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
bItemIsWithinRange = false;
if(PlayerInventory && OverlappingActors.IsValidIndex(0))
{
for(auto& GroundItems : OverlappingActors)
{
PlayerInventory->GroundLootActorsArray.Remove(GroundItems);
}
OnUpdateGroundLoot.Broadcast();
OverlappingActors.Empty();
}
}
I wasn't emptying the GroundLootItemsArray every time I was updating the ground loot and so previous ground loot was being stored in this array on each new update. This also resulted in showing the same widget for two different actors. I have now set it to empty along with clearing the children of the scroll box before any widget creation takes place in the UInventory.cpp (which is a child of UUserWidget):
// Iterates through the ground loot around the player and displays a widget for each item
void UInventory::UpdateGroundLoot()
{
MyPlayer = Cast<AMainCharacter>(GetOwningPlayer()->GetCharacter());
if(!MyPlayer) { return; } // Null check
// Get a ref to the Player's inventory component
UInventoryComponent* PlayerInventoryComp = MyPlayer->PlayerInventory;
// Clear any current widgets in the ground loot list
GroundLootScrollBox->ClearChildren();
// Empties this array to prevent replication of Item specific information that gets sent to the widget (i.e., the thumbnail or the name of the item)
GroundLootItemsArray.Empty();
// GroundLootActorsArray is an array of AActors that gets added to/emptied based on AActors that overlap with the Player's UBoxComponent
if(!PlayerInventoryComp && !PlayerInventoryComp->GroundLootActorsArray.IsValidIndex(0)) { return; } // Null check
for(int32 i = 0; i < PlayerInventoryComp->GroundLootActorsArray.Num(); i++)
{
// Cast each element of the GroundLootItems array to AItem* GroundItem
AItem* GroundItem = Cast<AItem>(PlayerInventoryComp->GroundLootActorsArray[i]);
if(!GroundItem) { return; } // Null check
// Add GroundItem to a new array (GroundLootItemsArray). This is an array specifically of AItems (rather than AActors).
GroundLootItemsArray.AddUnique(GroundItem);
GroundLootWidget = CreateWidget<UInventoryItemWidget>(GetOwningPlayer(), InventoryItemWidgetClass);
// Add each created widget to an array of widgets called GroundLootWidgetsArray
GroundLootWidgetsArray.AddUnique(GroundLootWidget);
if(!GroundLootWidgetsArray.IsValidIndex(0) && !GroundLootItemsArray.IsValidIndex(0)) { return; } // Null check
// Display item specific names/thumbnails for each widget created
GroundLootWidgetsArray[i]->ItemName->SetText(GroundLootItemsArray[i]->ItemDisplayName);
GroundLootWidgetsArray[i]->Thumbnail->SetBrushFromTexture(GroundLootItemsArray[i]->Thumbnail);
// Add each array element of the GroundLootWidgetsArray to the scroll box in the widget blueprint
GroundLootScrollBox->AddChild(GroundLootWidgetsArray[i]);
}
}
I also changed the adding of actors/items to the arrays from Array.Add(Actor) to Array.AddUnique(Actor). I'm not sure if this was necessary but it made sense to me because I didn't want more than one of any given item accidently being added to the array.
VS2010 with an MDI document layout using tabs along the top to switch between documents. Each document is a "live" view into a database, where the persistent data per document is a group of configuration settings.
We would like to allow the user to rearrange the tabs (this functionality is built in), but need to persist this new order. Right now it appears the document z-order is not affected by moving the tabs around. when closing the app, the documents close in the order they were opened so this is not helpful in determining the final tab order on close.
We are using the EnableMDITabbedGroups(TRUE, mdiTabParams) with m_bEnableTabSwap = TRUE which is the default.
Thanks! Ended up with the following solution in the MainFrame::OnClose() method.
Note that this code example uses two custom classes of 1) CSpectraAnalysisUtilityView which inherits from CView and 2) CReviewDataFolder which is our object that we needed to update the recent Tab Order.
This code solution also implements the GetMDITabGroups in case there are multiple group windows open.
void CMainFrame::OnClose()
{
iReviewDataFolderOrder = 1;
const CObList& tabGroups =m_wndClientArea.GetMDITabGroups();
if (0 < tabGroups.GetCount())
{
POSITION pos = tabGroups.GetHeadPosition();
CMFCTabCtrl* pCrtTabCtrl;
while(pos != NULL)
{
pCrtTabCtrl=DYNAMIC_DOWNCAST(CMFCTabCtrl, tabGroups.GetNext(pos));
int count = pCrtTabCtrl->GetTabsNum();
for(int i = 0; i < count; i++)
{
CWnd* pWnd = pCrtTabCtrl->GetTabWndNoWrapper(i);
CMDIChildWnd *pChild = ((CMDIChildWnd*)(pWnd));
if (pChild)
{
CView *pView = pChild->GetActiveView();
if (pView)
{
if (pView->IsKindOf(RUNTIME_CLASS(CSpectraAnalysisUtilityView)))
{
CSpectraAnalysisUtilityView* specUtilView;
specUtilView = (CSpectraAnalysisUtilityView*)pView;
CReviewDataFolder* pDataFolder = specUtilView->GetSpecReviewDataFolder();
if(pDataFolder)
{
pDataFolder->SetRecentOrder(iReviewDataFolderOrder);
iReviewDataFolderOrder++;
}
}
}
}
}
}
}
CMDIFrameWnd::OnClose();
}
Upon destruction of the outer main frame (OnDestroy) you can access the the CMFCTabCtrl members and can loop over each tab and determine the current sequence stored in the tab. GetTabWnd will allow you to access each tab by its index.
To access the tab control use CMDIClientAreaWnd::GetMDITab.
How can I remove an layout_newInfo from layout_main in runtime (pressing button)?
code I tried:
QLayout *layout = this->layout();
QLayoutItem *item;
while ((item = layout->takeAt(0)) != 0)
layout->removeItem (item);
delete layout_newInfo;
layout_main->update();
What exactly do you want to achieve?
If you want to show/hide the widgets that are now in layout_newInfo, then
don't use a layout. Use a widget that you put in a layout_main (vertical layout), which itself has the newInfo items and layout, then just use setVisible(true/false) on the widget as you need.
How can I remove layout_newInfo from layout_main in runtime given layout_newInfo is nested to layout_main?
The semantically clearer method:
layout_main->removeItem(layout_newInfo); // make sure layout_newInfo object deleted
// after either by parent or somehow else
BTW, usually this should also do the same removing of nested layout:
delete layout_newInfo; // also removes it from upper layout
layout_main->update(); // triggers update on the screen
So, just 2 bottom lines of your code example should be sufficient where layout_main->update() call is needed only sometimes if no other update triggered.
The example from here shows that deleting QLayoutItem which is parent for QLayout does remove it from upper layout structure as well (its destructor does it).
Finally find an answer best way is making void method like void showNewInfo(QString action);
In class cpp file
void MainWind::showNewInfo(QString action)
{
if(action == "true")
{
bt_search->setEnabled(false);
bt_production->setEnabled(false);
bt_drying->setEnabled(false);
bt_storage->setEnabled(false);
ln_spent->show();
cb_thickness1->show();
cb_thickness2->show();
cb_thickness3->show();
cb_EFL1->show();
cb_EFL2->show();
bt_newItem->show();
}
else if(action == "false")
{
bt_search->setEnabled(true);
bt_production->setEnabled(true);
bt_drying->setEnabled(true);
bt_storage->setEnabled(true);
ln_spent->hide();
cb_thickness1->hide();
cb_thickness2->hide();
cb_thickness3->hide();
cb_EFL1->hide();
cb_EFL2->hide();
bt_newItem->hide();
}
}
Also there is possible to use setText(""), so next time showing fragment, it will be clear;
I have several layers in the scene, like TLayer,HLayer,TouchLayer. How can I get HLayer in HLayer? The solution I take is that I pass Layer to other Layer. However I met some problems recently. I push the Scene and pop Scene while TouchLayer still exists. So my problem is that is it right to pass HLayer to TouchLayer. Or is there a better way to do it in Cocos2d-x?
In the init() function in Scene:
this->setbackgroundLayer(BackgroundLayer::create());
CC_BREAK_IF(!backgroundLayer);
this->addChild(backgroundLayer);
this->setTLayer(TcharacterLayer::create(backgroundLayer->tianzige));
CC_BREAK_IF(!TLayer);
this->addChild(TLayer);
this->setHLayer(HcharacterLayer::create(testCharacter,backgroundLayer->tianzige_draw));
CC_BREAK_IF(!HLayer);
this->addChild(HLayer);
this->settouchLayer(TouchLayer::create(TLayer,HLayer));
CC_BREAK_IF(!touchLayer);
this->addChild(touchLayer);
I made my own create function:
TouchLayer* TouchLayer::create(TcharacterLayer* t,HcharacterLayer* h){
TouchLayer* pRet = new TouchLayer();
if (pRet && pRet->init(t,h))
{
pRet->autorelease();
return pRet;
}else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
Here's how we've done this in the past. Define a set of tags for our layers.
typedef enum {
kBgLayerTag,
kHLayerTag,
kTLayerTag,
kTouchLayerTag
} MyLayerTags
Then while creating the layers set layer specific tags:
this->setTLayer(TcharacterLayer::create(backgroundLayer->tianzige));
CC_BREAK_IF(!TLayer);
TLayer->setTag(kTLayerTag);
this->addChild(TLayer);
And in TouchLayer access the TLayer and others like:
TcharacterLayer* myTLayer = this->getParent()->getChildByTag(kTLayerTag);
Sure, there is nothing wrong in adding a layer as a child. I would do that this way :
BackgroundLayer has all the necessary layers as children (remember to add TouchLayer as the last one and then pass Touch to others) and then you only add BackgroundLayer to your scene. There is even more simple way (probably better): to make BackgroundLayer inherit all the previous ones - but that depends on how much scenes you are going to make.
Edit:
1)
BackgroundLayer * bglayer = BackgroundLayer::create();
TcharacterLayer * tcharlayer = TcharacterLayer::create();
HcharacterLayer * hcharlayer = HcharacterLayer::create();
TouchLayer * tlayer = TouchLayer::create();
bglayer->addChild(tcharlayer);
bglayer->addChild(hcharlayer);
bglayer->addChild(tlayer);
this->addChild(bglayer);
(assuming this as a CCScene)
2)
class BackgroundLayer : public TcharacterLayer, HcharacterLayer, TouchLayer
{
...
}
In the first case you can give each layer a specific tag and then get the layer by getChildByTag(int tag) or just create fields in the BackgroundLayer class for each layer.
Sorry if this sounds quite trivial. I am just not getting it. How can one determine if a particular sprite is already in a layer? Basically, I need to check this before determining whether to add it to the layer or not.
if ( [ myNode.children indexOfObject:sprite ] == NSNotFound ) {
// you can add the code here
}
There's so many ways:
1) try to get child
if (![layer getChild:sprite]) {
// Your code
}
2) try to get child by tag
if (![layer getChildByTag:spriteTag]) {
// Your code
}
3) Check if sprite is on children array (like #oopology answer)
if ([layer.children indexOfObject:sprite] == NSNotFound) {
// Your code
}